musicxml-io 0.2.5 → 0.2.8

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.
@@ -118,6 +118,12 @@ __export(operations_exports, {
118
118
  });
119
119
  module.exports = __toCommonJS(operations_exports);
120
120
 
121
+ // src/id.ts
122
+ var import_nanoid = require("nanoid");
123
+ function generateId() {
124
+ return "i" + (0, import_nanoid.nanoid)(10);
125
+ }
126
+
121
127
  // src/utils/index.ts
122
128
  var STEPS = ["C", "D", "E", "F", "G", "A", "B"];
123
129
  var STEP_SEMITONES = {
@@ -1194,6 +1200,34 @@ function operationError(code, message, location = {}, details) {
1194
1200
  function cloneScore(score) {
1195
1201
  return JSON.parse(JSON.stringify(score));
1196
1202
  }
1203
+ function cloneNoteWithNewId(note) {
1204
+ const cloned = JSON.parse(JSON.stringify(note));
1205
+ cloned._id = generateId();
1206
+ return cloned;
1207
+ }
1208
+ function cloneEntryWithNewId(entry) {
1209
+ const cloned = JSON.parse(JSON.stringify(entry));
1210
+ cloned._id = generateId();
1211
+ return cloned;
1212
+ }
1213
+ function cloneMeasureWithNewIds(measure) {
1214
+ const cloned = JSON.parse(JSON.stringify(measure));
1215
+ cloned._id = generateId();
1216
+ cloned.entries = cloned.entries.map((entry) => cloneEntryWithNewId(entry));
1217
+ if (cloned.barlines) {
1218
+ cloned.barlines = cloned.barlines.map((barline) => ({
1219
+ ...barline,
1220
+ _id: generateId()
1221
+ }));
1222
+ }
1223
+ return cloned;
1224
+ }
1225
+ function clonePartWithNewIds(part) {
1226
+ const cloned = JSON.parse(JSON.stringify(part));
1227
+ cloned._id = generateId();
1228
+ cloned.measures = cloned.measures.map((measure) => cloneMeasureWithNewIds(measure));
1229
+ return cloned;
1230
+ }
1197
1231
  function getMeasureDuration(divisions, time) {
1198
1232
  const beats = parseInt(time.beats, 10);
1199
1233
  if (isNaN(beats)) return divisions * 4;
@@ -1256,6 +1290,7 @@ function hasNotesInRange(voiceEntries, startPos, endPos) {
1256
1290
  }
1257
1291
  function createRest(duration, voice, staff) {
1258
1292
  return {
1293
+ _id: generateId(),
1259
1294
  type: "note",
1260
1295
  rest: { displayStep: void 0, displayOctave: void 0 },
1261
1296
  duration,
@@ -1317,10 +1352,11 @@ function rebuildMeasureWithVoice(measure, voice, newEntries, measureDuration, st
1317
1352
  for (const { position: targetPos, entry } of allEntries) {
1318
1353
  const diff = targetPos - currentPosition;
1319
1354
  if (diff < 0) {
1320
- result.push({ type: "backup", duration: -diff });
1355
+ result.push({ _id: generateId(), type: "backup", duration: -diff });
1321
1356
  currentPosition = targetPos;
1322
1357
  } else if (diff > 0) {
1323
1358
  result.push({
1359
+ _id: generateId(),
1324
1360
  type: "forward",
1325
1361
  duration: diff,
1326
1362
  voice: entry.type === "note" ? entry.voice : 1,
@@ -1375,6 +1411,7 @@ function insertNote(score, options) {
1375
1411
  )]);
1376
1412
  }
1377
1413
  const newNote = {
1414
+ _id: generateId(),
1378
1415
  type: "note",
1379
1416
  pitch: options.pitch,
1380
1417
  duration: options.duration,
@@ -1481,6 +1518,7 @@ function addChord(score, options) {
1481
1518
  return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
1482
1519
  }
1483
1520
  const chordNote = {
1521
+ _id: generateId(),
1484
1522
  type: "note",
1485
1523
  pitch: options.pitch,
1486
1524
  duration: targetEntry.duration,
@@ -1809,7 +1847,7 @@ function addVoice(score, options) {
1809
1847
  const rest = createRest(measureDuration, options.voice, options.staff);
1810
1848
  const currentEnd = getMeasureEndPosition(measure);
1811
1849
  if (currentEnd > 0) {
1812
- measure.entries.push({ type: "backup", duration: currentEnd });
1850
+ measure.entries.push({ _id: generateId(), type: "backup", duration: currentEnd });
1813
1851
  }
1814
1852
  measure.entries.push(rest);
1815
1853
  return success(result);
@@ -1860,6 +1898,7 @@ function addPart(score, options) {
1860
1898
  const result = cloneScore(score);
1861
1899
  const insertIndex = options.insertIndex ?? result.parts.length;
1862
1900
  const partInfo = {
1901
+ _id: generateId(),
1863
1902
  type: "score-part",
1864
1903
  id: options.id,
1865
1904
  name: options.name,
@@ -1878,10 +1917,10 @@ function addPart(score, options) {
1878
1917
  }
1879
1918
  result.partList.splice(partListInsertIndex, 0, partInfo);
1880
1919
  const measureCount = result.parts.length > 0 ? result.parts[0].measures.length : 1;
1881
- const newPart = { id: options.id, measures: [] };
1920
+ const newPart = { _id: generateId(), id: options.id, measures: [] };
1882
1921
  for (let i = 0; i < measureCount; i++) {
1883
1922
  const measureNumber = result.parts.length > 0 ? result.parts[0].measures[i]?.number ?? String(i + 1) : String(i + 1);
1884
- const measure = { number: measureNumber, entries: [] };
1923
+ const measure = { _id: generateId(), number: measureNumber, entries: [] };
1885
1924
  if (i === 0) {
1886
1925
  measure.attributes = {
1887
1926
  divisions: options.divisions ?? 4,
@@ -1925,10 +1964,11 @@ function duplicatePart(score, options) {
1925
1964
  }
1926
1965
  const result = cloneScore(score);
1927
1966
  const sourcePart = result.parts[sourceIndex];
1928
- const newPart = JSON.parse(JSON.stringify(sourcePart));
1967
+ const newPart = clonePartWithNewIds(sourcePart);
1929
1968
  newPart.id = options.newPartId;
1930
1969
  const sourcePartInfo = result.partList.find((e) => e.type === "score-part" && e.id === options.sourcePartId);
1931
1970
  const newPartInfo = {
1971
+ _id: generateId(),
1932
1972
  type: "score-part",
1933
1973
  id: options.newPartId,
1934
1974
  name: options.newPartName ?? sourcePartInfo?.name,
@@ -2042,7 +2082,7 @@ function insertMeasure(score, options) {
2042
2082
  if (insertIndex === -1) continue;
2043
2083
  const numericPart = parseInt(targetMeasure, 10);
2044
2084
  const newMeasureNumber = String(isNaN(numericPart) ? insertIndex + 2 : numericPart + 1);
2045
- const newMeasure = { number: newMeasureNumber, entries: [] };
2085
+ const newMeasure = { _id: generateId(), number: newMeasureNumber, entries: [] };
2046
2086
  if (options.copyAttributes && part.measures[insertIndex].attributes) {
2047
2087
  newMeasure.attributes = { ...part.measures[insertIndex].attributes };
2048
2088
  }
@@ -2318,6 +2358,7 @@ function addDynamics(score, options) {
2318
2358
  const result = cloneScore(score);
2319
2359
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
2320
2360
  const directionEntry = {
2361
+ _id: generateId(),
2321
2362
  type: "direction",
2322
2363
  directionTypes: [{
2323
2364
  kind: "dynamics",
@@ -2394,6 +2435,7 @@ function insertClefChange(score, options) {
2394
2435
  }
2395
2436
  } else {
2396
2437
  const attributesEntry = {
2438
+ _id: generateId(),
2397
2439
  type: "attributes",
2398
2440
  attributes: {
2399
2441
  clef: [options.clef]
@@ -2862,7 +2904,7 @@ function copyNotes(score, options) {
2862
2904
  if (!entry.chord) {
2863
2905
  const noteEnd = position + entry.duration;
2864
2906
  if (position < options.endPosition && noteEnd > options.startPosition) {
2865
- const clonedNote = JSON.parse(JSON.stringify(entry));
2907
+ const clonedNote = cloneNoteWithNewId(entry);
2866
2908
  if (clonedNote.tie) {
2867
2909
  }
2868
2910
  copiedNotes.push({
@@ -2875,7 +2917,7 @@ function copyNotes(score, options) {
2875
2917
  if (copiedNotes.length > 0) {
2876
2918
  const lastCopied = copiedNotes[copiedNotes.length - 1];
2877
2919
  if (lastCopied.note.voice === entry.voice && (options.staff === void 0 || (lastCopied.note.staff ?? 1) === (entry.staff ?? 1))) {
2878
- const clonedNote = JSON.parse(JSON.stringify(entry));
2920
+ const clonedNote = cloneNoteWithNewId(entry);
2879
2921
  copiedNotes.push({
2880
2922
  relativePosition: lastCopied.relativePosition,
2881
2923
  note: clonedNote
@@ -2951,7 +2993,7 @@ function pasteNotes(score, options) {
2951
2993
  }
2952
2994
  for (const { relativePosition, note } of options.selection.notes) {
2953
2995
  const pastePosition = options.position + Math.max(0, relativePosition);
2954
- const newNote = JSON.parse(JSON.stringify(note));
2996
+ const newNote = cloneNoteWithNewId(note);
2955
2997
  newNote.voice = targetVoice;
2956
2998
  if (targetStaff !== void 0) {
2957
2999
  newNote.staff = targetStaff;
@@ -2986,7 +3028,7 @@ function pasteNotes(score, options) {
2986
3028
  const existingNotes = voiceEntries.filter((e) => e.entry.type === "note").map((e) => ({ position: e.position, entry: e.entry }));
2987
3029
  for (const { relativePosition, note } of options.selection.notes) {
2988
3030
  const pastePosition = options.position + Math.max(0, relativePosition);
2989
- const newNote = JSON.parse(JSON.stringify(note));
3031
+ const newNote = cloneNoteWithNewId(note);
2990
3032
  newNote.voice = targetVoice;
2991
3033
  if (targetStaff !== void 0) {
2992
3034
  newNote.staff = targetStaff;
@@ -3094,14 +3136,14 @@ function copyNotesMultiMeasure(score, options) {
3094
3136
  if (entry.type === "note") {
3095
3137
  if (entry.voice === options.voice && (options.staff === void 0 || (entry.staff ?? 1) === options.staff)) {
3096
3138
  if (!entry.chord && !entry.rest) {
3097
- const clonedNote = JSON.parse(JSON.stringify(entry));
3139
+ const clonedNote = cloneNoteWithNewId(entry);
3098
3140
  copiedNotes.push({
3099
3141
  relativePosition: position,
3100
3142
  note: clonedNote
3101
3143
  });
3102
3144
  position += entry.duration;
3103
3145
  } else if (entry.chord && copiedNotes.length > 0) {
3104
- const clonedNote = JSON.parse(JSON.stringify(entry));
3146
+ const clonedNote = cloneNoteWithNewId(entry);
3105
3147
  copiedNotes.push({
3106
3148
  relativePosition: copiedNotes[copiedNotes.length - 1].relativePosition,
3107
3149
  note: clonedNote
@@ -3155,7 +3197,7 @@ function pasteNotesMultiMeasure(score, options) {
3155
3197
  entriesToKeep = voiceEntries.filter((e) => e.entry.type === "note").map((e) => ({ position: e.position, entry: e.entry }));
3156
3198
  }
3157
3199
  for (const { relativePosition, note } of measureData.notes) {
3158
- const newNote = JSON.parse(JSON.stringify(note));
3200
+ const newNote = cloneNoteWithNewId(note);
3159
3201
  newNote.voice = targetVoice;
3160
3202
  if (targetStaff !== void 0) {
3161
3203
  newNote.staff = targetStaff;
@@ -3217,6 +3259,7 @@ function addTempo(score, options) {
3217
3259
  });
3218
3260
  }
3219
3261
  const direction = {
3262
+ _id: generateId(),
3220
3263
  type: "direction",
3221
3264
  directionTypes,
3222
3265
  placement: options.placement ?? "above",
@@ -3269,6 +3312,7 @@ function addWedge(score, options) {
3269
3312
  const result = cloneScore(score);
3270
3313
  const startMeasure = result.parts[options.partIndex].measures[options.startMeasureIndex];
3271
3314
  const startDirection = {
3315
+ _id: generateId(),
3272
3316
  type: "direction",
3273
3317
  directionTypes: [{
3274
3318
  kind: "wedge",
@@ -3280,6 +3324,7 @@ function addWedge(score, options) {
3280
3324
  insertDirectionAtPosition(startMeasure, startDirection, options.startPosition);
3281
3325
  const endMeasure = result.parts[options.partIndex].measures[options.endMeasureIndex];
3282
3326
  const endDirection = {
3327
+ _id: generateId(),
3283
3328
  type: "direction",
3284
3329
  directionTypes: [{
3285
3330
  kind: "wedge",
@@ -3460,6 +3505,7 @@ function addPedal(score, options) {
3460
3505
  const result = cloneScore(score);
3461
3506
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3462
3507
  const direction = {
3508
+ _id: generateId(),
3463
3509
  type: "direction",
3464
3510
  directionTypes: [{
3465
3511
  kind: "pedal",
@@ -3512,6 +3558,7 @@ function addTextDirection(score, options) {
3512
3558
  const result = cloneScore(score);
3513
3559
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3514
3560
  const direction = {
3561
+ _id: generateId(),
3515
3562
  type: "direction",
3516
3563
  directionTypes: [{
3517
3564
  kind: "words",
@@ -3535,6 +3582,7 @@ function addRehearsalMark(score, options) {
3535
3582
  const result = cloneScore(score);
3536
3583
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3537
3584
  const direction = {
3585
+ _id: generateId(),
3538
3586
  type: "direction",
3539
3587
  directionTypes: [{
3540
3588
  kind: "rehearsal",
@@ -3593,6 +3641,7 @@ function addRepeatBarline(score, options) {
3593
3641
  measure.barlines.splice(nonRepeatIndex, 1);
3594
3642
  }
3595
3643
  measure.barlines.push({
3644
+ _id: generateId(),
3596
3645
  location,
3597
3646
  barStyle,
3598
3647
  repeat: {
@@ -3655,7 +3704,7 @@ function addEnding(score, options) {
3655
3704
  }
3656
3705
  let barline = measure.barlines.find((b) => b.location === location);
3657
3706
  if (!barline) {
3658
- barline = { location };
3707
+ barline = { _id: generateId(), location };
3659
3708
  measure.barlines.push(barline);
3660
3709
  }
3661
3710
  if (barline.ending) {
@@ -3717,7 +3766,7 @@ function changeBarline(score, options) {
3717
3766
  }
3718
3767
  let barline = measure.barlines.find((b) => b.location === location);
3719
3768
  if (!barline) {
3720
- barline = { location };
3769
+ barline = { _id: generateId(), location };
3721
3770
  measure.barlines.push(barline);
3722
3771
  }
3723
3772
  barline.barStyle = barStyle;
@@ -3736,6 +3785,7 @@ function addSegno(score, options) {
3736
3785
  const result = cloneScore(score);
3737
3786
  const measure = result.parts[partIndex].measures[measureIndex];
3738
3787
  const direction = {
3788
+ _id: generateId(),
3739
3789
  type: "direction",
3740
3790
  directionTypes: [{ kind: "segno" }],
3741
3791
  placement: "above"
@@ -3755,6 +3805,7 @@ function addCoda(score, options) {
3755
3805
  const result = cloneScore(score);
3756
3806
  const measure = result.parts[partIndex].measures[measureIndex];
3757
3807
  const direction = {
3808
+ _id: generateId(),
3758
3809
  type: "direction",
3759
3810
  directionTypes: [{ kind: "coda" }],
3760
3811
  placement: "above"
@@ -3777,12 +3828,14 @@ function addDaCapo(score, options) {
3777
3828
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3778
3829
  const insertPos = position ?? measureDuration;
3779
3830
  const direction = {
3831
+ _id: generateId(),
3780
3832
  type: "direction",
3781
3833
  directionTypes: [{ kind: "words", text: "D.C." }],
3782
3834
  placement: "above"
3783
3835
  };
3784
3836
  insertDirectionAtPosition(measure, direction, insertPos);
3785
3837
  const sound = {
3838
+ _id: generateId(),
3786
3839
  type: "sound",
3787
3840
  dacapo: true
3788
3841
  };
@@ -3804,12 +3857,14 @@ function addDalSegno(score, options) {
3804
3857
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3805
3858
  const insertPos = position ?? measureDuration;
3806
3859
  const direction = {
3860
+ _id: generateId(),
3807
3861
  type: "direction",
3808
3862
  directionTypes: [{ kind: "words", text: "D.S." }],
3809
3863
  placement: "above"
3810
3864
  };
3811
3865
  insertDirectionAtPosition(measure, direction, insertPos);
3812
3866
  const sound = {
3867
+ _id: generateId(),
3813
3868
  type: "sound",
3814
3869
  dalsegno: "segno"
3815
3870
  };
@@ -3831,12 +3886,14 @@ function addFine(score, options) {
3831
3886
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3832
3887
  const insertPos = position ?? measureDuration;
3833
3888
  const direction = {
3889
+ _id: generateId(),
3834
3890
  type: "direction",
3835
3891
  directionTypes: [{ kind: "words", text: "Fine" }],
3836
3892
  placement: "above"
3837
3893
  };
3838
3894
  insertDirectionAtPosition(measure, direction, insertPos);
3839
3895
  const sound = {
3896
+ _id: generateId(),
3840
3897
  type: "sound",
3841
3898
  fine: true
3842
3899
  };
@@ -3858,12 +3915,14 @@ function addToCoda(score, options) {
3858
3915
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3859
3916
  const insertPos = position ?? measureDuration;
3860
3917
  const direction = {
3918
+ _id: generateId(),
3861
3919
  type: "direction",
3862
3920
  directionTypes: [{ kind: "words", text: "To Coda" }],
3863
3921
  placement: "above"
3864
3922
  };
3865
3923
  insertDirectionAtPosition(measure, direction, insertPos);
3866
3924
  const sound = {
3925
+ _id: generateId(),
3867
3926
  type: "sound",
3868
3927
  tocoda: "coda"
3869
3928
  };
@@ -3900,6 +3959,7 @@ function addGraceNote(score, options) {
3900
3959
  const result = cloneScore(score);
3901
3960
  const resultMeasure = result.parts[partIndex].measures[measureIndex];
3902
3961
  const graceNote = {
3962
+ _id: generateId(),
3903
3963
  type: "note",
3904
3964
  pitch,
3905
3965
  duration: 0,
@@ -4140,6 +4200,7 @@ function addHarmony(score, options) {
4140
4200
  const result = cloneScore(score);
4141
4201
  const measure = result.parts[partIndex].measures[measureIndex];
4142
4202
  const harmony = {
4203
+ _id: generateId(),
4143
4204
  type: "harmony",
4144
4205
  root: {
4145
4206
  rootStep: root.step.toUpperCase(),
@@ -4538,6 +4599,7 @@ function addOctaveShift(score, options) {
4538
4599
  const result = cloneScore(score);
4539
4600
  const measure = result.parts[partIndex].measures[measureIndex];
4540
4601
  const direction = {
4602
+ _id: generateId(),
4541
4603
  type: "direction",
4542
4604
  directionTypes: [{
4543
4605
  kind: "octave-shift",
@@ -4561,6 +4623,7 @@ function stopOctaveShift(score, options) {
4561
4623
  const result = cloneScore(score);
4562
4624
  const measure = result.parts[partIndex].measures[measureIndex];
4563
4625
  const direction = {
4626
+ _id: generateId(),
4564
4627
  type: "direction",
4565
4628
  directionTypes: [{
4566
4629
  kind: "octave-shift",