musicxml-io 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +177 -142
  2. package/dist/index.mjs +177 -142
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -214,9 +214,14 @@ var _chunkVE2KCZMAjs = require('./chunk-VE2KCZMA.js');
214
214
 
215
215
  // src/importers/musicxml.ts
216
216
  var _txml = require('txml');
217
+ var _entityMap = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
218
+ var _entityRegex = /&(?:(amp|lt|gt|quot|apos)|#(\d+)|#x([0-9a-fA-F]+));/g;
217
219
  function decodeXmlEntities(s) {
218
220
  if (s.indexOf("&") === -1) return s;
219
- return s.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num, 10))).replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
221
+ return s.replace(
222
+ _entityRegex,
223
+ (_, named, dec, hex) => named ? _entityMap[named] : dec ? String.fromCharCode(parseInt(dec, 10)) : String.fromCharCode(parseInt(hex, 16))
224
+ );
220
225
  }
221
226
  function decodeTree(nodes) {
222
227
  for (let i = 0; i < nodes.length; i++) {
@@ -1040,12 +1045,8 @@ function parseNote(elements, attrs) {
1040
1045
  const note = {
1041
1046
  _id: _chunkKABVHR2Pjs.generateId.call(void 0, ),
1042
1047
  type: "note",
1043
- duration: getElementTextAsInt(elements, "duration", 0)
1048
+ duration: 0
1044
1049
  };
1045
- const voiceValue = getElementText(elements, "voice");
1046
- if (voiceValue !== void 0 && voiceValue !== "") {
1047
- note.voice = voiceValue;
1048
- }
1049
1050
  if (attrs["default-x"]) note.defaultX = parseFloat(attrs["default-x"]);
1050
1051
  if (attrs["default-y"]) note.defaultY = parseFloat(attrs["default-y"]);
1051
1052
  if (attrs["relative-x"]) note.relativeX = parseFloat(attrs["relative-x"]);
@@ -1056,149 +1057,183 @@ function parseNote(elements, attrs) {
1056
1057
  if (attrs["print-dot"] === "yes") note.printDot = true;
1057
1058
  if (attrs["print-spacing"] === "yes") note.printSpacing = true;
1058
1059
  if (attrs["print-spacing"] === "no") note.printSpacing = false;
1059
- if (hasElement(elements, "cue")) {
1060
- note.cue = true;
1061
- }
1062
- const instData = parseFirstElement(elements, "instrument", (_, attrs2) => attrs2["id"]);
1063
- if (instData) note.instrument = instData;
1064
- const pitch = getElementContent(elements, "pitch");
1065
- if (pitch) {
1066
- note.pitch = parsePitch(pitch);
1067
- }
1068
- for (const el of elements) {
1060
+ let dotCount = 0;
1061
+ let notationsIndex = 0;
1062
+ let tieElements;
1063
+ let beams;
1064
+ let allNotations;
1065
+ let lyrics;
1066
+ let hasGrace = false;
1067
+ for (let i = 0; i < elements.length; i++) {
1068
+ const el = elements[i];
1069
1069
  if (typeof el === "string") continue;
1070
- if (el.tagName === "rest") {
1071
- const restContent = el.children;
1072
- const restInfo = {};
1073
- const restAttrs = el.attributes;
1074
- if (restAttrs["measure"] === "yes") restInfo.measure = true;
1075
- const displayStep = getElementText(restContent, "display-step");
1076
- if (displayStep) restInfo.displayStep = displayStep;
1077
- const displayOctave = getElementText(restContent, "display-octave");
1078
- if (displayOctave) restInfo.displayOctave = parseInt(displayOctave, 10);
1079
- note.rest = restInfo;
1080
- break;
1070
+ const tag = el.tagName;
1071
+ const elAttrs = el.attributes;
1072
+ const c = el.children;
1073
+ switch (tag) {
1074
+ case "duration": {
1075
+ const text = extractText(c);
1076
+ if (text) note.duration = parseInt(text, 10) || 0;
1077
+ break;
1078
+ }
1079
+ case "voice": {
1080
+ const text = extractText(c);
1081
+ if (text) note.voice = text;
1082
+ break;
1083
+ }
1084
+ case "cue":
1085
+ note.cue = true;
1086
+ break;
1087
+ case "chord":
1088
+ note.chord = true;
1089
+ break;
1090
+ case "dot":
1091
+ dotCount++;
1092
+ break;
1093
+ case "instrument":
1094
+ if (elAttrs["id"]) note.instrument = elAttrs["id"];
1095
+ break;
1096
+ case "pitch":
1097
+ note.pitch = parsePitch(c);
1098
+ break;
1099
+ case "rest": {
1100
+ const restInfo = {};
1101
+ if (elAttrs["measure"] === "yes") restInfo.measure = true;
1102
+ const displayStep = getElementText(c, "display-step");
1103
+ if (displayStep) restInfo.displayStep = displayStep;
1104
+ const displayOctave = getElementText(c, "display-octave");
1105
+ if (displayOctave) restInfo.displayOctave = parseInt(displayOctave, 10);
1106
+ note.rest = restInfo;
1107
+ break;
1108
+ }
1109
+ case "unpitched": {
1110
+ note.unpitched = {};
1111
+ const displayStep = getElementText(c, "display-step");
1112
+ if (displayStep) note.unpitched.displayStep = displayStep;
1113
+ const displayOctave = getElementText(c, "display-octave");
1114
+ if (displayOctave) note.unpitched.displayOctave = parseInt(displayOctave, 10);
1115
+ break;
1116
+ }
1117
+ case "staff": {
1118
+ const text = extractText(c);
1119
+ if (text) {
1120
+ const v = parseInt(text, 10);
1121
+ if (!isNaN(v)) note.staff = v;
1122
+ }
1123
+ break;
1124
+ }
1125
+ case "type": {
1126
+ const noteType = extractText(c);
1127
+ if (isValidNoteType(noteType)) note.noteType = noteType;
1128
+ if (elAttrs["size"]) note.noteTypeSize = elAttrs["size"];
1129
+ break;
1130
+ }
1131
+ case "accidental": {
1132
+ const accValue = extractText(c);
1133
+ if (isValidAccidental(accValue)) {
1134
+ const accInfo = { value: accValue };
1135
+ if (elAttrs["cautionary"] === "yes") accInfo.cautionary = true;
1136
+ if (elAttrs["editorial"] === "yes") accInfo.editorial = true;
1137
+ if (elAttrs["parentheses"] === "yes") accInfo.parentheses = true;
1138
+ if (elAttrs["bracket"] === "yes") accInfo.bracket = true;
1139
+ if (elAttrs["relative-x"]) accInfo.relativeX = parseFloat(elAttrs["relative-x"]);
1140
+ if (elAttrs["relative-y"]) accInfo.relativeY = parseFloat(elAttrs["relative-y"]);
1141
+ if (elAttrs["color"]) accInfo.color = elAttrs["color"];
1142
+ if (elAttrs["size"]) accInfo.size = elAttrs["size"];
1143
+ if (elAttrs["font-size"]) accInfo.fontSize = elAttrs["font-size"];
1144
+ note.accidental = accInfo;
1145
+ }
1146
+ break;
1147
+ }
1148
+ case "stem": {
1149
+ const stemValue = extractText(c);
1150
+ if (stemValue === "up" || stemValue === "down" || stemValue === "none" || stemValue === "double") {
1151
+ note.stem = { value: stemValue };
1152
+ if (elAttrs["default-x"]) note.stem.defaultX = parseFloat(elAttrs["default-x"]);
1153
+ if (elAttrs["default-y"]) note.stem.defaultY = parseFloat(elAttrs["default-y"]);
1154
+ }
1155
+ break;
1156
+ }
1157
+ case "notehead": {
1158
+ const nhValue = extractText(c);
1159
+ if (isValidNotehead(nhValue)) {
1160
+ const nhInfo = { value: nhValue };
1161
+ if (elAttrs["filled"] === "yes") nhInfo.filled = true;
1162
+ else if (elAttrs["filled"] === "no") nhInfo.filled = false;
1163
+ if (elAttrs["parentheses"] === "yes") nhInfo.parentheses = true;
1164
+ note.notehead = nhInfo;
1165
+ }
1166
+ break;
1167
+ }
1168
+ case "tie": {
1169
+ const t = elAttrs["type"];
1170
+ if (t === "start" || t === "stop" || t === "continue") {
1171
+ if (!tieElements) tieElements = [];
1172
+ tieElements.push({ type: t });
1173
+ }
1174
+ break;
1175
+ }
1176
+ case "beam": {
1177
+ if (!beams) beams = [];
1178
+ beams.push(parseBeam(c, elAttrs));
1179
+ break;
1180
+ }
1181
+ case "notations": {
1182
+ const parsedNotations = parseNotations(c, notationsIndex);
1183
+ if (parsedNotations.length > 0) {
1184
+ if (!allNotations) allNotations = [];
1185
+ for (let j = 0; j < parsedNotations.length; j++) allNotations.push(parsedNotations[j]);
1186
+ }
1187
+ notationsIndex++;
1188
+ break;
1189
+ }
1190
+ case "lyric": {
1191
+ if (!lyrics) lyrics = [];
1192
+ lyrics.push(parseLyric(c, elAttrs));
1193
+ break;
1194
+ }
1195
+ case "grace": {
1196
+ hasGrace = true;
1197
+ note.grace = {};
1198
+ if (elAttrs["slash"] === "yes") note.grace.slash = true;
1199
+ else if (elAttrs["slash"] === "no") note.grace.slash = false;
1200
+ if (elAttrs["steal-time-previous"]) {
1201
+ note.grace.stealTimePrevious = parseFloat(elAttrs["steal-time-previous"]);
1202
+ }
1203
+ if (elAttrs["steal-time-following"]) {
1204
+ note.grace.stealTimeFollowing = parseFloat(elAttrs["steal-time-following"]);
1205
+ }
1206
+ break;
1207
+ }
1208
+ case "time-modification": {
1209
+ const actualNotes = getElementText(c, "actual-notes");
1210
+ const normalNotes = getElementText(c, "normal-notes");
1211
+ const normalType = getElementText(c, "normal-type");
1212
+ note.timeModification = {
1213
+ actualNotes: parseInt(actualNotes || "3", 10),
1214
+ normalNotes: parseInt(normalNotes || "2", 10)
1215
+ };
1216
+ if (normalType && isValidNoteType(normalType)) {
1217
+ note.timeModification.normalType = normalType;
1218
+ }
1219
+ let ndCount = 0;
1220
+ for (const tm of c) {
1221
+ if (typeof tm !== "string" && tm.tagName === "normal-dot") ndCount++;
1222
+ }
1223
+ if (ndCount > 0) note.timeModification.normalDots = ndCount;
1224
+ break;
1225
+ }
1081
1226
  }
1082
1227
  }
1083
- parseFirstElement(elements, "unpitched", (c) => {
1084
- note.unpitched = {};
1085
- const displayStep = getElementText(c, "display-step");
1086
- if (displayStep) note.unpitched.displayStep = displayStep;
1087
- const displayOctave = getElementText(c, "display-octave");
1088
- if (displayOctave) note.unpitched.displayOctave = parseInt(displayOctave, 10);
1089
- });
1090
- const staff = getElementTextAsInt(elements, "staff");
1091
- if (staff !== void 0) note.staff = staff;
1092
- if (hasElement(elements, "chord")) {
1093
- note.chord = true;
1094
- }
1095
- parseFirstElement(elements, "type", (c, a) => {
1096
- const noteType = extractText(c);
1097
- if (isValidNoteType(noteType)) note.noteType = noteType;
1098
- if (a["size"]) note.noteTypeSize = a["size"];
1099
- });
1100
- const dotCount = elements.filter((el) => typeof el !== "string" && el.tagName === "dot").length;
1101
1228
  if (dotCount > 0) note.dots = dotCount;
1102
- parseFirstElement(elements, "accidental", (c, a) => {
1103
- const accValue = extractText(c);
1104
- if (isValidAccidental(accValue)) {
1105
- const accInfo = { value: accValue };
1106
- if (a["cautionary"] === "yes") accInfo.cautionary = true;
1107
- if (a["editorial"] === "yes") accInfo.editorial = true;
1108
- if (a["parentheses"] === "yes") accInfo.parentheses = true;
1109
- if (a["bracket"] === "yes") accInfo.bracket = true;
1110
- if (a["relative-x"]) accInfo.relativeX = parseFloat(a["relative-x"]);
1111
- if (a["relative-y"]) accInfo.relativeY = parseFloat(a["relative-y"]);
1112
- if (a["color"]) accInfo.color = a["color"];
1113
- if (a["size"]) accInfo.size = a["size"];
1114
- if (a["font-size"]) accInfo.fontSize = a["font-size"];
1115
- note.accidental = accInfo;
1116
- }
1117
- });
1118
- parseFirstElement(elements, "stem", (c, a) => {
1119
- const stemValue = extractText(c);
1120
- if (stemValue === "up" || stemValue === "down" || stemValue === "none" || stemValue === "double") {
1121
- note.stem = { value: stemValue };
1122
- if (a["default-x"]) note.stem.defaultX = parseFloat(a["default-x"]);
1123
- if (a["default-y"]) note.stem.defaultY = parseFloat(a["default-y"]);
1124
- }
1125
- });
1126
- parseFirstElement(elements, "notehead", (c, a) => {
1127
- const nhValue = extractText(c);
1128
- if (isValidNotehead(nhValue)) {
1129
- const nhInfo = { value: nhValue };
1130
- if (a["filled"] === "yes") nhInfo.filled = true;
1131
- else if (a["filled"] === "no") nhInfo.filled = false;
1132
- if (a["parentheses"] === "yes") nhInfo.parentheses = true;
1133
- note.notehead = nhInfo;
1134
- }
1135
- });
1136
- const tieElements = collectElements(elements, "tie", (_, a) => {
1137
- const t = a["type"];
1138
- return t === "start" || t === "stop" || t === "continue" ? { type: t } : null;
1139
- }).filter((t) => t !== null);
1140
- if (tieElements.length > 0) {
1229
+ if (tieElements) {
1141
1230
  note.tie = tieElements[0];
1142
1231
  if (tieElements.length > 1) note.ties = tieElements;
1143
1232
  }
1144
- const beams = collectElements(elements, "beam", (c, a) => parseBeam(c, a));
1145
- if (beams.length > 0) note.beam = beams;
1146
- const allNotations = [];
1147
- let notationsIndex = 0;
1148
- for (const el of elements) {
1149
- if (typeof el === "string") continue;
1150
- if (el.tagName === "notations") {
1151
- const parsedNotations = parseNotations(el.children, notationsIndex);
1152
- allNotations.push(...parsedNotations);
1153
- notationsIndex++;
1154
- }
1155
- }
1156
- if (allNotations.length > 0) {
1157
- note.notations = allNotations;
1158
- }
1159
- const lyrics = [];
1160
- for (const el of elements) {
1161
- if (typeof el === "string") continue;
1162
- if (el.tagName === "lyric") {
1163
- lyrics.push(parseLyric(el.children, el.attributes));
1164
- }
1165
- }
1166
- if (lyrics.length > 0) note.lyrics = lyrics;
1167
- for (const el of elements) {
1168
- if (typeof el === "string") continue;
1169
- if (el.tagName === "grace") {
1170
- const graceAttrs = el.attributes;
1171
- note.grace = {};
1172
- if (graceAttrs["slash"] === "yes") note.grace.slash = true;
1173
- else if (graceAttrs["slash"] === "no") note.grace.slash = false;
1174
- if (graceAttrs["steal-time-previous"]) {
1175
- note.grace.stealTimePrevious = parseFloat(graceAttrs["steal-time-previous"]);
1176
- }
1177
- if (graceAttrs["steal-time-following"]) {
1178
- note.grace.stealTimeFollowing = parseFloat(graceAttrs["steal-time-following"]);
1179
- }
1180
- note.duration = 0;
1181
- break;
1182
- }
1183
- }
1184
- const timeMod = getElementContent(elements, "time-modification");
1185
- if (timeMod) {
1186
- const actualNotes = getElementText(timeMod, "actual-notes");
1187
- const normalNotes = getElementText(timeMod, "normal-notes");
1188
- const normalType = getElementText(timeMod, "normal-type");
1189
- note.timeModification = {
1190
- actualNotes: parseInt(actualNotes || "3", 10),
1191
- normalNotes: parseInt(normalNotes || "2", 10)
1192
- };
1193
- if (normalType && isValidNoteType(normalType)) {
1194
- note.timeModification.normalType = normalType;
1195
- }
1196
- let dotCount2 = 0;
1197
- for (const tm of timeMod) {
1198
- if (typeof tm !== "string" && tm.tagName === "normal-dot") dotCount2++;
1199
- }
1200
- if (dotCount2 > 0) note.timeModification.normalDots = dotCount2;
1201
- }
1233
+ if (beams) note.beam = beams;
1234
+ if (allNotations) note.notations = allNotations;
1235
+ if (lyrics) note.lyrics = lyrics;
1236
+ if (hasGrace) note.duration = 0;
1202
1237
  return note;
1203
1238
  }
1204
1239
  function parsePitch(elements) {
package/dist/index.mjs CHANGED
@@ -214,9 +214,14 @@ import {
214
214
 
215
215
  // src/importers/musicxml.ts
216
216
  import { parse as txmlParse } from "txml";
217
+ var _entityMap = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
218
+ var _entityRegex = /&(?:(amp|lt|gt|quot|apos)|#(\d+)|#x([0-9a-fA-F]+));/g;
217
219
  function decodeXmlEntities(s) {
218
220
  if (s.indexOf("&") === -1) return s;
219
- return s.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num, 10))).replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
221
+ return s.replace(
222
+ _entityRegex,
223
+ (_, named, dec, hex) => named ? _entityMap[named] : dec ? String.fromCharCode(parseInt(dec, 10)) : String.fromCharCode(parseInt(hex, 16))
224
+ );
220
225
  }
221
226
  function decodeTree(nodes) {
222
227
  for (let i = 0; i < nodes.length; i++) {
@@ -1040,12 +1045,8 @@ function parseNote(elements, attrs) {
1040
1045
  const note = {
1041
1046
  _id: generateId(),
1042
1047
  type: "note",
1043
- duration: getElementTextAsInt(elements, "duration", 0)
1048
+ duration: 0
1044
1049
  };
1045
- const voiceValue = getElementText(elements, "voice");
1046
- if (voiceValue !== void 0 && voiceValue !== "") {
1047
- note.voice = voiceValue;
1048
- }
1049
1050
  if (attrs["default-x"]) note.defaultX = parseFloat(attrs["default-x"]);
1050
1051
  if (attrs["default-y"]) note.defaultY = parseFloat(attrs["default-y"]);
1051
1052
  if (attrs["relative-x"]) note.relativeX = parseFloat(attrs["relative-x"]);
@@ -1056,149 +1057,183 @@ function parseNote(elements, attrs) {
1056
1057
  if (attrs["print-dot"] === "yes") note.printDot = true;
1057
1058
  if (attrs["print-spacing"] === "yes") note.printSpacing = true;
1058
1059
  if (attrs["print-spacing"] === "no") note.printSpacing = false;
1059
- if (hasElement(elements, "cue")) {
1060
- note.cue = true;
1061
- }
1062
- const instData = parseFirstElement(elements, "instrument", (_, attrs2) => attrs2["id"]);
1063
- if (instData) note.instrument = instData;
1064
- const pitch = getElementContent(elements, "pitch");
1065
- if (pitch) {
1066
- note.pitch = parsePitch(pitch);
1067
- }
1068
- for (const el of elements) {
1060
+ let dotCount = 0;
1061
+ let notationsIndex = 0;
1062
+ let tieElements;
1063
+ let beams;
1064
+ let allNotations;
1065
+ let lyrics;
1066
+ let hasGrace = false;
1067
+ for (let i = 0; i < elements.length; i++) {
1068
+ const el = elements[i];
1069
1069
  if (typeof el === "string") continue;
1070
- if (el.tagName === "rest") {
1071
- const restContent = el.children;
1072
- const restInfo = {};
1073
- const restAttrs = el.attributes;
1074
- if (restAttrs["measure"] === "yes") restInfo.measure = true;
1075
- const displayStep = getElementText(restContent, "display-step");
1076
- if (displayStep) restInfo.displayStep = displayStep;
1077
- const displayOctave = getElementText(restContent, "display-octave");
1078
- if (displayOctave) restInfo.displayOctave = parseInt(displayOctave, 10);
1079
- note.rest = restInfo;
1080
- break;
1070
+ const tag = el.tagName;
1071
+ const elAttrs = el.attributes;
1072
+ const c = el.children;
1073
+ switch (tag) {
1074
+ case "duration": {
1075
+ const text = extractText(c);
1076
+ if (text) note.duration = parseInt(text, 10) || 0;
1077
+ break;
1078
+ }
1079
+ case "voice": {
1080
+ const text = extractText(c);
1081
+ if (text) note.voice = text;
1082
+ break;
1083
+ }
1084
+ case "cue":
1085
+ note.cue = true;
1086
+ break;
1087
+ case "chord":
1088
+ note.chord = true;
1089
+ break;
1090
+ case "dot":
1091
+ dotCount++;
1092
+ break;
1093
+ case "instrument":
1094
+ if (elAttrs["id"]) note.instrument = elAttrs["id"];
1095
+ break;
1096
+ case "pitch":
1097
+ note.pitch = parsePitch(c);
1098
+ break;
1099
+ case "rest": {
1100
+ const restInfo = {};
1101
+ if (elAttrs["measure"] === "yes") restInfo.measure = true;
1102
+ const displayStep = getElementText(c, "display-step");
1103
+ if (displayStep) restInfo.displayStep = displayStep;
1104
+ const displayOctave = getElementText(c, "display-octave");
1105
+ if (displayOctave) restInfo.displayOctave = parseInt(displayOctave, 10);
1106
+ note.rest = restInfo;
1107
+ break;
1108
+ }
1109
+ case "unpitched": {
1110
+ note.unpitched = {};
1111
+ const displayStep = getElementText(c, "display-step");
1112
+ if (displayStep) note.unpitched.displayStep = displayStep;
1113
+ const displayOctave = getElementText(c, "display-octave");
1114
+ if (displayOctave) note.unpitched.displayOctave = parseInt(displayOctave, 10);
1115
+ break;
1116
+ }
1117
+ case "staff": {
1118
+ const text = extractText(c);
1119
+ if (text) {
1120
+ const v = parseInt(text, 10);
1121
+ if (!isNaN(v)) note.staff = v;
1122
+ }
1123
+ break;
1124
+ }
1125
+ case "type": {
1126
+ const noteType = extractText(c);
1127
+ if (isValidNoteType(noteType)) note.noteType = noteType;
1128
+ if (elAttrs["size"]) note.noteTypeSize = elAttrs["size"];
1129
+ break;
1130
+ }
1131
+ case "accidental": {
1132
+ const accValue = extractText(c);
1133
+ if (isValidAccidental(accValue)) {
1134
+ const accInfo = { value: accValue };
1135
+ if (elAttrs["cautionary"] === "yes") accInfo.cautionary = true;
1136
+ if (elAttrs["editorial"] === "yes") accInfo.editorial = true;
1137
+ if (elAttrs["parentheses"] === "yes") accInfo.parentheses = true;
1138
+ if (elAttrs["bracket"] === "yes") accInfo.bracket = true;
1139
+ if (elAttrs["relative-x"]) accInfo.relativeX = parseFloat(elAttrs["relative-x"]);
1140
+ if (elAttrs["relative-y"]) accInfo.relativeY = parseFloat(elAttrs["relative-y"]);
1141
+ if (elAttrs["color"]) accInfo.color = elAttrs["color"];
1142
+ if (elAttrs["size"]) accInfo.size = elAttrs["size"];
1143
+ if (elAttrs["font-size"]) accInfo.fontSize = elAttrs["font-size"];
1144
+ note.accidental = accInfo;
1145
+ }
1146
+ break;
1147
+ }
1148
+ case "stem": {
1149
+ const stemValue = extractText(c);
1150
+ if (stemValue === "up" || stemValue === "down" || stemValue === "none" || stemValue === "double") {
1151
+ note.stem = { value: stemValue };
1152
+ if (elAttrs["default-x"]) note.stem.defaultX = parseFloat(elAttrs["default-x"]);
1153
+ if (elAttrs["default-y"]) note.stem.defaultY = parseFloat(elAttrs["default-y"]);
1154
+ }
1155
+ break;
1156
+ }
1157
+ case "notehead": {
1158
+ const nhValue = extractText(c);
1159
+ if (isValidNotehead(nhValue)) {
1160
+ const nhInfo = { value: nhValue };
1161
+ if (elAttrs["filled"] === "yes") nhInfo.filled = true;
1162
+ else if (elAttrs["filled"] === "no") nhInfo.filled = false;
1163
+ if (elAttrs["parentheses"] === "yes") nhInfo.parentheses = true;
1164
+ note.notehead = nhInfo;
1165
+ }
1166
+ break;
1167
+ }
1168
+ case "tie": {
1169
+ const t = elAttrs["type"];
1170
+ if (t === "start" || t === "stop" || t === "continue") {
1171
+ if (!tieElements) tieElements = [];
1172
+ tieElements.push({ type: t });
1173
+ }
1174
+ break;
1175
+ }
1176
+ case "beam": {
1177
+ if (!beams) beams = [];
1178
+ beams.push(parseBeam(c, elAttrs));
1179
+ break;
1180
+ }
1181
+ case "notations": {
1182
+ const parsedNotations = parseNotations(c, notationsIndex);
1183
+ if (parsedNotations.length > 0) {
1184
+ if (!allNotations) allNotations = [];
1185
+ for (let j = 0; j < parsedNotations.length; j++) allNotations.push(parsedNotations[j]);
1186
+ }
1187
+ notationsIndex++;
1188
+ break;
1189
+ }
1190
+ case "lyric": {
1191
+ if (!lyrics) lyrics = [];
1192
+ lyrics.push(parseLyric(c, elAttrs));
1193
+ break;
1194
+ }
1195
+ case "grace": {
1196
+ hasGrace = true;
1197
+ note.grace = {};
1198
+ if (elAttrs["slash"] === "yes") note.grace.slash = true;
1199
+ else if (elAttrs["slash"] === "no") note.grace.slash = false;
1200
+ if (elAttrs["steal-time-previous"]) {
1201
+ note.grace.stealTimePrevious = parseFloat(elAttrs["steal-time-previous"]);
1202
+ }
1203
+ if (elAttrs["steal-time-following"]) {
1204
+ note.grace.stealTimeFollowing = parseFloat(elAttrs["steal-time-following"]);
1205
+ }
1206
+ break;
1207
+ }
1208
+ case "time-modification": {
1209
+ const actualNotes = getElementText(c, "actual-notes");
1210
+ const normalNotes = getElementText(c, "normal-notes");
1211
+ const normalType = getElementText(c, "normal-type");
1212
+ note.timeModification = {
1213
+ actualNotes: parseInt(actualNotes || "3", 10),
1214
+ normalNotes: parseInt(normalNotes || "2", 10)
1215
+ };
1216
+ if (normalType && isValidNoteType(normalType)) {
1217
+ note.timeModification.normalType = normalType;
1218
+ }
1219
+ let ndCount = 0;
1220
+ for (const tm of c) {
1221
+ if (typeof tm !== "string" && tm.tagName === "normal-dot") ndCount++;
1222
+ }
1223
+ if (ndCount > 0) note.timeModification.normalDots = ndCount;
1224
+ break;
1225
+ }
1081
1226
  }
1082
1227
  }
1083
- parseFirstElement(elements, "unpitched", (c) => {
1084
- note.unpitched = {};
1085
- const displayStep = getElementText(c, "display-step");
1086
- if (displayStep) note.unpitched.displayStep = displayStep;
1087
- const displayOctave = getElementText(c, "display-octave");
1088
- if (displayOctave) note.unpitched.displayOctave = parseInt(displayOctave, 10);
1089
- });
1090
- const staff = getElementTextAsInt(elements, "staff");
1091
- if (staff !== void 0) note.staff = staff;
1092
- if (hasElement(elements, "chord")) {
1093
- note.chord = true;
1094
- }
1095
- parseFirstElement(elements, "type", (c, a) => {
1096
- const noteType = extractText(c);
1097
- if (isValidNoteType(noteType)) note.noteType = noteType;
1098
- if (a["size"]) note.noteTypeSize = a["size"];
1099
- });
1100
- const dotCount = elements.filter((el) => typeof el !== "string" && el.tagName === "dot").length;
1101
1228
  if (dotCount > 0) note.dots = dotCount;
1102
- parseFirstElement(elements, "accidental", (c, a) => {
1103
- const accValue = extractText(c);
1104
- if (isValidAccidental(accValue)) {
1105
- const accInfo = { value: accValue };
1106
- if (a["cautionary"] === "yes") accInfo.cautionary = true;
1107
- if (a["editorial"] === "yes") accInfo.editorial = true;
1108
- if (a["parentheses"] === "yes") accInfo.parentheses = true;
1109
- if (a["bracket"] === "yes") accInfo.bracket = true;
1110
- if (a["relative-x"]) accInfo.relativeX = parseFloat(a["relative-x"]);
1111
- if (a["relative-y"]) accInfo.relativeY = parseFloat(a["relative-y"]);
1112
- if (a["color"]) accInfo.color = a["color"];
1113
- if (a["size"]) accInfo.size = a["size"];
1114
- if (a["font-size"]) accInfo.fontSize = a["font-size"];
1115
- note.accidental = accInfo;
1116
- }
1117
- });
1118
- parseFirstElement(elements, "stem", (c, a) => {
1119
- const stemValue = extractText(c);
1120
- if (stemValue === "up" || stemValue === "down" || stemValue === "none" || stemValue === "double") {
1121
- note.stem = { value: stemValue };
1122
- if (a["default-x"]) note.stem.defaultX = parseFloat(a["default-x"]);
1123
- if (a["default-y"]) note.stem.defaultY = parseFloat(a["default-y"]);
1124
- }
1125
- });
1126
- parseFirstElement(elements, "notehead", (c, a) => {
1127
- const nhValue = extractText(c);
1128
- if (isValidNotehead(nhValue)) {
1129
- const nhInfo = { value: nhValue };
1130
- if (a["filled"] === "yes") nhInfo.filled = true;
1131
- else if (a["filled"] === "no") nhInfo.filled = false;
1132
- if (a["parentheses"] === "yes") nhInfo.parentheses = true;
1133
- note.notehead = nhInfo;
1134
- }
1135
- });
1136
- const tieElements = collectElements(elements, "tie", (_, a) => {
1137
- const t = a["type"];
1138
- return t === "start" || t === "stop" || t === "continue" ? { type: t } : null;
1139
- }).filter((t) => t !== null);
1140
- if (tieElements.length > 0) {
1229
+ if (tieElements) {
1141
1230
  note.tie = tieElements[0];
1142
1231
  if (tieElements.length > 1) note.ties = tieElements;
1143
1232
  }
1144
- const beams = collectElements(elements, "beam", (c, a) => parseBeam(c, a));
1145
- if (beams.length > 0) note.beam = beams;
1146
- const allNotations = [];
1147
- let notationsIndex = 0;
1148
- for (const el of elements) {
1149
- if (typeof el === "string") continue;
1150
- if (el.tagName === "notations") {
1151
- const parsedNotations = parseNotations(el.children, notationsIndex);
1152
- allNotations.push(...parsedNotations);
1153
- notationsIndex++;
1154
- }
1155
- }
1156
- if (allNotations.length > 0) {
1157
- note.notations = allNotations;
1158
- }
1159
- const lyrics = [];
1160
- for (const el of elements) {
1161
- if (typeof el === "string") continue;
1162
- if (el.tagName === "lyric") {
1163
- lyrics.push(parseLyric(el.children, el.attributes));
1164
- }
1165
- }
1166
- if (lyrics.length > 0) note.lyrics = lyrics;
1167
- for (const el of elements) {
1168
- if (typeof el === "string") continue;
1169
- if (el.tagName === "grace") {
1170
- const graceAttrs = el.attributes;
1171
- note.grace = {};
1172
- if (graceAttrs["slash"] === "yes") note.grace.slash = true;
1173
- else if (graceAttrs["slash"] === "no") note.grace.slash = false;
1174
- if (graceAttrs["steal-time-previous"]) {
1175
- note.grace.stealTimePrevious = parseFloat(graceAttrs["steal-time-previous"]);
1176
- }
1177
- if (graceAttrs["steal-time-following"]) {
1178
- note.grace.stealTimeFollowing = parseFloat(graceAttrs["steal-time-following"]);
1179
- }
1180
- note.duration = 0;
1181
- break;
1182
- }
1183
- }
1184
- const timeMod = getElementContent(elements, "time-modification");
1185
- if (timeMod) {
1186
- const actualNotes = getElementText(timeMod, "actual-notes");
1187
- const normalNotes = getElementText(timeMod, "normal-notes");
1188
- const normalType = getElementText(timeMod, "normal-type");
1189
- note.timeModification = {
1190
- actualNotes: parseInt(actualNotes || "3", 10),
1191
- normalNotes: parseInt(normalNotes || "2", 10)
1192
- };
1193
- if (normalType && isValidNoteType(normalType)) {
1194
- note.timeModification.normalType = normalType;
1195
- }
1196
- let dotCount2 = 0;
1197
- for (const tm of timeMod) {
1198
- if (typeof tm !== "string" && tm.tagName === "normal-dot") dotCount2++;
1199
- }
1200
- if (dotCount2 > 0) note.timeModification.normalDots = dotCount2;
1201
- }
1233
+ if (beams) note.beam = beams;
1234
+ if (allNotations) note.notations = allNotations;
1235
+ if (lyrics) note.lyrics = lyrics;
1236
+ if (hasGrace) note.duration = 0;
1202
1237
  return note;
1203
1238
  }
1204
1239
  function parsePitch(elements) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musicxml-io",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Parse and serialize MusicXML (.xml/.mxl) and ABC notation with high round-trip fidelity",
5
5
  "author": "tan-z-tan",
6
6
  "license": "MIT",