musicxml-io 0.3.1 → 0.3.2

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 +121 -103
  2. package/dist/index.mjs +121 -103
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5572,6 +5572,97 @@ function serializeCompressed(score, options = {}) {
5572
5572
  return (0, import_fflate2.zipSync)(files, { level: 6 });
5573
5573
  }
5574
5574
 
5575
+ // src/entry-accessors.ts
5576
+ function getDirectionOfKind(entry, kind) {
5577
+ return entry.directionTypes.find((d) => d.kind === kind);
5578
+ }
5579
+ function getDirectionsOfKind(entry, kind) {
5580
+ return entry.directionTypes.filter((d) => d.kind === kind);
5581
+ }
5582
+ function hasDirectionOfKind(entry, kind) {
5583
+ return entry.directionTypes.some((d) => d.kind === kind);
5584
+ }
5585
+ function getSoundTempo(entry) {
5586
+ return entry.sound?.tempo;
5587
+ }
5588
+ function getSoundDynamics(entry) {
5589
+ return entry.sound?.dynamics;
5590
+ }
5591
+ function getSoundDamperPedal(entry) {
5592
+ return entry.sound?.damperPedal;
5593
+ }
5594
+ function getSoundSoftPedal(entry) {
5595
+ return entry.sound?.softPedal;
5596
+ }
5597
+ function getSoundSostenutoPedal(entry) {
5598
+ return entry.sound?.sostenutoPedal;
5599
+ }
5600
+ function isRest(entry) {
5601
+ return entry.rest !== void 0 || !entry.pitch && !entry.unpitched;
5602
+ }
5603
+ function isPitchedNote(entry) {
5604
+ return entry.pitch !== void 0;
5605
+ }
5606
+ function isUnpitchedNote(entry) {
5607
+ return entry.unpitched !== void 0;
5608
+ }
5609
+ function isChordNote(entry) {
5610
+ return entry.chord === true;
5611
+ }
5612
+ function isGraceNote(entry) {
5613
+ return entry.grace !== void 0;
5614
+ }
5615
+ function hasTie(entry) {
5616
+ return entry.tie !== void 0 || entry.ties !== void 0 && entry.ties.length > 0;
5617
+ }
5618
+ function hasTieStart(entry) {
5619
+ if (entry.tie?.type === "start") return true;
5620
+ return entry.ties?.some((t) => t.type === "start") ?? false;
5621
+ }
5622
+ function hasTieStop(entry) {
5623
+ if (entry.tie?.type === "stop") return true;
5624
+ return entry.ties?.some((t) => t.type === "stop") ?? false;
5625
+ }
5626
+ function isCueNote(entry) {
5627
+ return entry.cue === true;
5628
+ }
5629
+ function hasBeam(entry) {
5630
+ return entry.beam !== void 0 && entry.beam.length > 0;
5631
+ }
5632
+ function hasLyrics(entry) {
5633
+ return entry.lyrics !== void 0 && entry.lyrics.length > 0;
5634
+ }
5635
+ function hasNotations(entry) {
5636
+ return entry.notations !== void 0 && entry.notations.length > 0;
5637
+ }
5638
+ function hasTuplet(entry) {
5639
+ return entry.timeModification !== void 0;
5640
+ }
5641
+ function isPartInfo(entry) {
5642
+ return entry.type === "score-part";
5643
+ }
5644
+ function getPartInfo(score, partId) {
5645
+ return score.partList.find((entry) => {
5646
+ return entry.type === "score-part" && entry.id === partId;
5647
+ });
5648
+ }
5649
+ function getPartName(score, partId) {
5650
+ return getPartInfo(score, partId)?.name;
5651
+ }
5652
+ function getPartAbbreviation(score, partId) {
5653
+ return getPartInfo(score, partId)?.abbreviation;
5654
+ }
5655
+ function getAllPartInfos(score) {
5656
+ return score.partList.filter(isPartInfo);
5657
+ }
5658
+ function getPartNameMap(score) {
5659
+ const map = {};
5660
+ for (const part of getAllPartInfos(score)) {
5661
+ map[part.id] = part.name;
5662
+ }
5663
+ return map;
5664
+ }
5665
+
5575
5666
  // src/exporters/midi.ts
5576
5667
  function exportMidi(score, options = {}) {
5577
5668
  const ticksPerQuarterNote = options.ticksPerQuarterNote ?? 480;
@@ -5729,18 +5820,24 @@ function createPartTrack(part, _score, channel, program, ticksPerQuarterNote, de
5729
5820
  const notePosition = note.chord ? chordBasePosition : position;
5730
5821
  const startTick = measureStartTick + Math.round(notePosition * ticksPerQuarterNote / divisions);
5731
5822
  const durationTicks = Math.round(note.duration * ticksPerQuarterNote / divisions);
5732
- noteEvents.push({
5733
- tick: startTick,
5734
- type: "on",
5735
- note: midiNote,
5736
- velocity: defaultVelocity
5737
- });
5738
- noteEvents.push({
5739
- tick: startTick + durationTicks,
5740
- type: "off",
5741
- note: midiNote,
5742
- velocity: 0
5743
- });
5823
+ const isTieStop = hasTieStop(note);
5824
+ const isTieStart = hasTieStart(note);
5825
+ if (!isTieStop) {
5826
+ noteEvents.push({
5827
+ tick: startTick,
5828
+ type: "on",
5829
+ note: midiNote,
5830
+ velocity: defaultVelocity
5831
+ });
5832
+ }
5833
+ if (!isTieStart) {
5834
+ noteEvents.push({
5835
+ tick: startTick + durationTicks,
5836
+ type: "off",
5837
+ note: midiNote,
5838
+ velocity: 0
5839
+ });
5840
+ }
5744
5841
  }
5745
5842
  if (!note.chord) {
5746
5843
  chordBasePosition = position;
@@ -5773,6 +5870,18 @@ function createPartTrack(part, _score, channel, program, ticksPerQuarterNote, de
5773
5870
  }
5774
5871
  }
5775
5872
  }
5873
+ const onCounts = /* @__PURE__ */ new Map();
5874
+ const offCounts = /* @__PURE__ */ new Map();
5875
+ for (const e of noteEvents) {
5876
+ const map = e.type === "on" ? onCounts : offCounts;
5877
+ map.set(e.note, (map.get(e.note) ?? 0) + 1);
5878
+ }
5879
+ for (const [note, onCount] of onCounts) {
5880
+ const offCount = offCounts.get(note) ?? 0;
5881
+ for (let i = 0; i < onCount - offCount; i++) {
5882
+ noteEvents.push({ tick: currentTick, type: "off", note, velocity: 0 });
5883
+ }
5884
+ }
5776
5885
  noteEvents.sort((a, b) => {
5777
5886
  if (a.tick !== b.tick) return a.tick - b.tick;
5778
5887
  if (a.type !== b.type) return a.type === "off" ? -1 : 1;
@@ -11446,97 +11555,6 @@ async function serializeToFile(score, filePath, options = {}) {
11446
11555
  await (0, import_promises.writeFile)(filePath, xmlString, "utf-8");
11447
11556
  }
11448
11557
  }
11449
-
11450
- // src/entry-accessors.ts
11451
- function getDirectionOfKind(entry, kind) {
11452
- return entry.directionTypes.find((d) => d.kind === kind);
11453
- }
11454
- function getDirectionsOfKind(entry, kind) {
11455
- return entry.directionTypes.filter((d) => d.kind === kind);
11456
- }
11457
- function hasDirectionOfKind(entry, kind) {
11458
- return entry.directionTypes.some((d) => d.kind === kind);
11459
- }
11460
- function getSoundTempo(entry) {
11461
- return entry.sound?.tempo;
11462
- }
11463
- function getSoundDynamics(entry) {
11464
- return entry.sound?.dynamics;
11465
- }
11466
- function getSoundDamperPedal(entry) {
11467
- return entry.sound?.damperPedal;
11468
- }
11469
- function getSoundSoftPedal(entry) {
11470
- return entry.sound?.softPedal;
11471
- }
11472
- function getSoundSostenutoPedal(entry) {
11473
- return entry.sound?.sostenutoPedal;
11474
- }
11475
- function isRest(entry) {
11476
- return entry.rest !== void 0 || !entry.pitch && !entry.unpitched;
11477
- }
11478
- function isPitchedNote(entry) {
11479
- return entry.pitch !== void 0;
11480
- }
11481
- function isUnpitchedNote(entry) {
11482
- return entry.unpitched !== void 0;
11483
- }
11484
- function isChordNote(entry) {
11485
- return entry.chord === true;
11486
- }
11487
- function isGraceNote(entry) {
11488
- return entry.grace !== void 0;
11489
- }
11490
- function hasTie(entry) {
11491
- return entry.tie !== void 0 || entry.ties !== void 0 && entry.ties.length > 0;
11492
- }
11493
- function hasTieStart(entry) {
11494
- if (entry.tie?.type === "start") return true;
11495
- return entry.ties?.some((t) => t.type === "start") ?? false;
11496
- }
11497
- function hasTieStop(entry) {
11498
- if (entry.tie?.type === "stop") return true;
11499
- return entry.ties?.some((t) => t.type === "stop") ?? false;
11500
- }
11501
- function isCueNote(entry) {
11502
- return entry.cue === true;
11503
- }
11504
- function hasBeam(entry) {
11505
- return entry.beam !== void 0 && entry.beam.length > 0;
11506
- }
11507
- function hasLyrics(entry) {
11508
- return entry.lyrics !== void 0 && entry.lyrics.length > 0;
11509
- }
11510
- function hasNotations(entry) {
11511
- return entry.notations !== void 0 && entry.notations.length > 0;
11512
- }
11513
- function hasTuplet(entry) {
11514
- return entry.timeModification !== void 0;
11515
- }
11516
- function isPartInfo(entry) {
11517
- return entry.type === "score-part";
11518
- }
11519
- function getPartInfo(score, partId) {
11520
- return score.partList.find((entry) => {
11521
- return entry.type === "score-part" && entry.id === partId;
11522
- });
11523
- }
11524
- function getPartName(score, partId) {
11525
- return getPartInfo(score, partId)?.name;
11526
- }
11527
- function getPartAbbreviation(score, partId) {
11528
- return getPartInfo(score, partId)?.abbreviation;
11529
- }
11530
- function getAllPartInfos(score) {
11531
- return score.partList.filter(isPartInfo);
11532
- }
11533
- function getPartNameMap(score) {
11534
- const map = {};
11535
- for (const part of getAllPartInfos(score)) {
11536
- map[part.id] = part.name;
11537
- }
11538
- return map;
11539
- }
11540
11558
  // Annotate the CommonJS export names for ESM import in node:
11541
11559
  0 && (module.exports = {
11542
11560
  STEPS,
package/dist/index.mjs CHANGED
@@ -5301,6 +5301,97 @@ function serializeCompressed(score, options = {}) {
5301
5301
  return zipSync(files, { level: 6 });
5302
5302
  }
5303
5303
 
5304
+ // src/entry-accessors.ts
5305
+ function getDirectionOfKind(entry, kind) {
5306
+ return entry.directionTypes.find((d) => d.kind === kind);
5307
+ }
5308
+ function getDirectionsOfKind(entry, kind) {
5309
+ return entry.directionTypes.filter((d) => d.kind === kind);
5310
+ }
5311
+ function hasDirectionOfKind(entry, kind) {
5312
+ return entry.directionTypes.some((d) => d.kind === kind);
5313
+ }
5314
+ function getSoundTempo(entry) {
5315
+ return entry.sound?.tempo;
5316
+ }
5317
+ function getSoundDynamics(entry) {
5318
+ return entry.sound?.dynamics;
5319
+ }
5320
+ function getSoundDamperPedal(entry) {
5321
+ return entry.sound?.damperPedal;
5322
+ }
5323
+ function getSoundSoftPedal(entry) {
5324
+ return entry.sound?.softPedal;
5325
+ }
5326
+ function getSoundSostenutoPedal(entry) {
5327
+ return entry.sound?.sostenutoPedal;
5328
+ }
5329
+ function isRest(entry) {
5330
+ return entry.rest !== void 0 || !entry.pitch && !entry.unpitched;
5331
+ }
5332
+ function isPitchedNote(entry) {
5333
+ return entry.pitch !== void 0;
5334
+ }
5335
+ function isUnpitchedNote(entry) {
5336
+ return entry.unpitched !== void 0;
5337
+ }
5338
+ function isChordNote(entry) {
5339
+ return entry.chord === true;
5340
+ }
5341
+ function isGraceNote(entry) {
5342
+ return entry.grace !== void 0;
5343
+ }
5344
+ function hasTie(entry) {
5345
+ return entry.tie !== void 0 || entry.ties !== void 0 && entry.ties.length > 0;
5346
+ }
5347
+ function hasTieStart(entry) {
5348
+ if (entry.tie?.type === "start") return true;
5349
+ return entry.ties?.some((t) => t.type === "start") ?? false;
5350
+ }
5351
+ function hasTieStop(entry) {
5352
+ if (entry.tie?.type === "stop") return true;
5353
+ return entry.ties?.some((t) => t.type === "stop") ?? false;
5354
+ }
5355
+ function isCueNote(entry) {
5356
+ return entry.cue === true;
5357
+ }
5358
+ function hasBeam(entry) {
5359
+ return entry.beam !== void 0 && entry.beam.length > 0;
5360
+ }
5361
+ function hasLyrics(entry) {
5362
+ return entry.lyrics !== void 0 && entry.lyrics.length > 0;
5363
+ }
5364
+ function hasNotations(entry) {
5365
+ return entry.notations !== void 0 && entry.notations.length > 0;
5366
+ }
5367
+ function hasTuplet(entry) {
5368
+ return entry.timeModification !== void 0;
5369
+ }
5370
+ function isPartInfo(entry) {
5371
+ return entry.type === "score-part";
5372
+ }
5373
+ function getPartInfo(score, partId) {
5374
+ return score.partList.find((entry) => {
5375
+ return entry.type === "score-part" && entry.id === partId;
5376
+ });
5377
+ }
5378
+ function getPartName(score, partId) {
5379
+ return getPartInfo(score, partId)?.name;
5380
+ }
5381
+ function getPartAbbreviation(score, partId) {
5382
+ return getPartInfo(score, partId)?.abbreviation;
5383
+ }
5384
+ function getAllPartInfos(score) {
5385
+ return score.partList.filter(isPartInfo);
5386
+ }
5387
+ function getPartNameMap(score) {
5388
+ const map = {};
5389
+ for (const part of getAllPartInfos(score)) {
5390
+ map[part.id] = part.name;
5391
+ }
5392
+ return map;
5393
+ }
5394
+
5304
5395
  // src/exporters/midi.ts
5305
5396
  function exportMidi(score, options = {}) {
5306
5397
  const ticksPerQuarterNote = options.ticksPerQuarterNote ?? 480;
@@ -5458,18 +5549,24 @@ function createPartTrack(part, _score, channel, program, ticksPerQuarterNote, de
5458
5549
  const notePosition = note.chord ? chordBasePosition : position;
5459
5550
  const startTick = measureStartTick + Math.round(notePosition * ticksPerQuarterNote / divisions);
5460
5551
  const durationTicks = Math.round(note.duration * ticksPerQuarterNote / divisions);
5461
- noteEvents.push({
5462
- tick: startTick,
5463
- type: "on",
5464
- note: midiNote,
5465
- velocity: defaultVelocity
5466
- });
5467
- noteEvents.push({
5468
- tick: startTick + durationTicks,
5469
- type: "off",
5470
- note: midiNote,
5471
- velocity: 0
5472
- });
5552
+ const isTieStop = hasTieStop(note);
5553
+ const isTieStart = hasTieStart(note);
5554
+ if (!isTieStop) {
5555
+ noteEvents.push({
5556
+ tick: startTick,
5557
+ type: "on",
5558
+ note: midiNote,
5559
+ velocity: defaultVelocity
5560
+ });
5561
+ }
5562
+ if (!isTieStart) {
5563
+ noteEvents.push({
5564
+ tick: startTick + durationTicks,
5565
+ type: "off",
5566
+ note: midiNote,
5567
+ velocity: 0
5568
+ });
5569
+ }
5473
5570
  }
5474
5571
  if (!note.chord) {
5475
5572
  chordBasePosition = position;
@@ -5502,6 +5599,18 @@ function createPartTrack(part, _score, channel, program, ticksPerQuarterNote, de
5502
5599
  }
5503
5600
  }
5504
5601
  }
5602
+ const onCounts = /* @__PURE__ */ new Map();
5603
+ const offCounts = /* @__PURE__ */ new Map();
5604
+ for (const e of noteEvents) {
5605
+ const map = e.type === "on" ? onCounts : offCounts;
5606
+ map.set(e.note, (map.get(e.note) ?? 0) + 1);
5607
+ }
5608
+ for (const [note, onCount] of onCounts) {
5609
+ const offCount = offCounts.get(note) ?? 0;
5610
+ for (let i = 0; i < onCount - offCount; i++) {
5611
+ noteEvents.push({ tick: currentTick, type: "off", note, velocity: 0 });
5612
+ }
5613
+ }
5505
5614
  noteEvents.sort((a, b) => {
5506
5615
  if (a.tick !== b.tick) return a.tick - b.tick;
5507
5616
  if (a.type !== b.type) return a.type === "off" ? -1 : 1;
@@ -11175,97 +11284,6 @@ async function serializeToFile(score, filePath, options = {}) {
11175
11284
  await writeFile(filePath, xmlString, "utf-8");
11176
11285
  }
11177
11286
  }
11178
-
11179
- // src/entry-accessors.ts
11180
- function getDirectionOfKind(entry, kind) {
11181
- return entry.directionTypes.find((d) => d.kind === kind);
11182
- }
11183
- function getDirectionsOfKind(entry, kind) {
11184
- return entry.directionTypes.filter((d) => d.kind === kind);
11185
- }
11186
- function hasDirectionOfKind(entry, kind) {
11187
- return entry.directionTypes.some((d) => d.kind === kind);
11188
- }
11189
- function getSoundTempo(entry) {
11190
- return entry.sound?.tempo;
11191
- }
11192
- function getSoundDynamics(entry) {
11193
- return entry.sound?.dynamics;
11194
- }
11195
- function getSoundDamperPedal(entry) {
11196
- return entry.sound?.damperPedal;
11197
- }
11198
- function getSoundSoftPedal(entry) {
11199
- return entry.sound?.softPedal;
11200
- }
11201
- function getSoundSostenutoPedal(entry) {
11202
- return entry.sound?.sostenutoPedal;
11203
- }
11204
- function isRest(entry) {
11205
- return entry.rest !== void 0 || !entry.pitch && !entry.unpitched;
11206
- }
11207
- function isPitchedNote(entry) {
11208
- return entry.pitch !== void 0;
11209
- }
11210
- function isUnpitchedNote(entry) {
11211
- return entry.unpitched !== void 0;
11212
- }
11213
- function isChordNote(entry) {
11214
- return entry.chord === true;
11215
- }
11216
- function isGraceNote(entry) {
11217
- return entry.grace !== void 0;
11218
- }
11219
- function hasTie(entry) {
11220
- return entry.tie !== void 0 || entry.ties !== void 0 && entry.ties.length > 0;
11221
- }
11222
- function hasTieStart(entry) {
11223
- if (entry.tie?.type === "start") return true;
11224
- return entry.ties?.some((t) => t.type === "start") ?? false;
11225
- }
11226
- function hasTieStop(entry) {
11227
- if (entry.tie?.type === "stop") return true;
11228
- return entry.ties?.some((t) => t.type === "stop") ?? false;
11229
- }
11230
- function isCueNote(entry) {
11231
- return entry.cue === true;
11232
- }
11233
- function hasBeam(entry) {
11234
- return entry.beam !== void 0 && entry.beam.length > 0;
11235
- }
11236
- function hasLyrics(entry) {
11237
- return entry.lyrics !== void 0 && entry.lyrics.length > 0;
11238
- }
11239
- function hasNotations(entry) {
11240
- return entry.notations !== void 0 && entry.notations.length > 0;
11241
- }
11242
- function hasTuplet(entry) {
11243
- return entry.timeModification !== void 0;
11244
- }
11245
- function isPartInfo(entry) {
11246
- return entry.type === "score-part";
11247
- }
11248
- function getPartInfo(score, partId) {
11249
- return score.partList.find((entry) => {
11250
- return entry.type === "score-part" && entry.id === partId;
11251
- });
11252
- }
11253
- function getPartName(score, partId) {
11254
- return getPartInfo(score, partId)?.name;
11255
- }
11256
- function getPartAbbreviation(score, partId) {
11257
- return getPartInfo(score, partId)?.abbreviation;
11258
- }
11259
- function getAllPartInfos(score) {
11260
- return score.partList.filter(isPartInfo);
11261
- }
11262
- function getPartNameMap(score) {
11263
- const map = {};
11264
- for (const part of getAllPartInfos(score)) {
11265
- map[part.id] = part.name;
11266
- }
11267
- return map;
11268
- }
11269
11287
  export {
11270
11288
  STEPS,
11271
11289
  STEP_SEMITONES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musicxml-io",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Parse and serialize MusicXML (.xml/.mxl) with high round-trip fidelity",
5
5
  "author": "tan-z-tan",
6
6
  "license": "MIT",