musicxml-io 0.1.0 → 0.2.0
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.
- package/README.md +3 -3
- package/dist/accessors/index.d.mts +280 -2
- package/dist/accessors/index.d.ts +280 -2
- package/dist/accessors/index.js +1299 -1
- package/dist/accessors/index.mjs +1251 -1
- package/dist/index-DCty0U8p.d.ts +377 -0
- package/dist/index-nCaclfqu.d.mts +377 -0
- package/dist/index.d.mts +6 -184
- package/dist/index.d.ts +6 -184
- package/dist/index.js +2087 -309
- package/dist/index.mjs +2039 -308
- package/dist/operations/index.d.mts +2 -92
- package/dist/operations/index.d.ts +2 -92
- package/dist/operations/index.js +1580 -150
- package/dist/operations/index.mjs +1562 -148
- package/dist/query/index.d.mts +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +0 -1
- package/dist/query/index.mjs +0 -1
- package/dist/{types-DC_TnJu_.d.ts → types-Dp9zcgIg.d.mts} +298 -3
- package/dist/{types-DC_TnJu_.d.mts → types-Dp9zcgIg.d.ts} +298 -3
- package/package.json +6 -2
- package/dist/accessors/index.js.map +0 -1
- package/dist/accessors/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/operations/index.js.map +0 -1
- package/dist/operations/index.mjs.map +0 -1
- package/dist/query/index.js.map +0 -1
- package/dist/query/index.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -378,8 +378,10 @@ function parsePartList(elements) {
|
|
|
378
378
|
const sound = getElementText(instContent, "instrument-sound");
|
|
379
379
|
if (sound) inst.sound = sound;
|
|
380
380
|
if (hasElement(instContent, "solo")) inst.solo = true;
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
if (hasElement(instContent, "ensemble")) {
|
|
382
|
+
const ensText = getElementText(instContent, "ensemble");
|
|
383
|
+
inst.ensemble = ensText ? parseInt(ensText, 10) : 0;
|
|
384
|
+
}
|
|
383
385
|
instruments.push(inst);
|
|
384
386
|
}
|
|
385
387
|
}
|
|
@@ -562,6 +564,8 @@ function parseAttributes(elements) {
|
|
|
562
564
|
if (divisions !== void 0) attrs.divisions = divisions;
|
|
563
565
|
const staves = getElementTextAsInt(elements, "staves");
|
|
564
566
|
if (staves !== void 0) attrs.staves = staves;
|
|
567
|
+
const instruments = getElementTextAsInt(elements, "instruments");
|
|
568
|
+
if (instruments !== void 0) attrs.instruments = instruments;
|
|
565
569
|
const time = getElementContent(elements, "time");
|
|
566
570
|
if (time) {
|
|
567
571
|
attrs.time = parseTimeSignature(time, elements);
|
|
@@ -658,7 +662,7 @@ function parseKeySignature(elements) {
|
|
|
658
662
|
const key = {
|
|
659
663
|
fifths: parseInt(fifths || "0", 10)
|
|
660
664
|
};
|
|
661
|
-
const validModes = ["major", "minor", "dorian", "phrygian", "lydian", "mixolydian", "aeolian", "ionian", "locrian"];
|
|
665
|
+
const validModes = ["major", "minor", "dorian", "phrygian", "lydian", "mixolydian", "aeolian", "ionian", "locrian", "none"];
|
|
662
666
|
if (mode && validModes.includes(mode)) {
|
|
663
667
|
key.mode = mode;
|
|
664
668
|
}
|
|
@@ -689,6 +693,12 @@ function parseClef(elements, attrs) {
|
|
|
689
693
|
if (octaveChange !== void 0) {
|
|
690
694
|
clef.clefOctaveChange = octaveChange;
|
|
691
695
|
}
|
|
696
|
+
if (attrs["print-object"] === "no") {
|
|
697
|
+
clef.printObject = false;
|
|
698
|
+
}
|
|
699
|
+
if (attrs["after-barline"] === "yes") {
|
|
700
|
+
clef.afterBarline = true;
|
|
701
|
+
}
|
|
692
702
|
return clef;
|
|
693
703
|
}
|
|
694
704
|
function parseTranspose(elements) {
|
|
@@ -898,6 +908,7 @@ function parseNotations(elements, notationsIndex = 0) {
|
|
|
898
908
|
slurType: attrs["type"] || "start",
|
|
899
909
|
number: attrs["number"] ? parseInt(attrs["number"], 10) : void 0,
|
|
900
910
|
lineType: attrs["line-type"],
|
|
911
|
+
orientation: attrs["orientation"],
|
|
901
912
|
defaultX: attrs["default-x"] ? parseFloat(attrs["default-x"]) : void 0,
|
|
902
913
|
defaultY: attrs["default-y"] ? parseFloat(attrs["default-y"]) : void 0,
|
|
903
914
|
bezierX: attrs["bezier-x"] ? parseFloat(attrs["bezier-x"]) : void 0,
|
|
@@ -1410,6 +1421,9 @@ function parseDirection(elements, attrs) {
|
|
|
1410
1421
|
direction.sound = {};
|
|
1411
1422
|
if (soundAttrs["tempo"]) direction.sound.tempo = parseFloat(soundAttrs["tempo"]);
|
|
1412
1423
|
if (soundAttrs["dynamics"]) direction.sound.dynamics = parseFloat(soundAttrs["dynamics"]);
|
|
1424
|
+
if (soundAttrs["damper-pedal"]) direction.sound.damperPedal = soundAttrs["damper-pedal"];
|
|
1425
|
+
if (soundAttrs["soft-pedal"]) direction.sound.softPedal = soundAttrs["soft-pedal"];
|
|
1426
|
+
if (soundAttrs["sostenuto-pedal"]) direction.sound.sostenutoPedal = soundAttrs["sostenuto-pedal"];
|
|
1413
1427
|
for (const soundEl of soundContent) {
|
|
1414
1428
|
if (soundEl["midi-instrument"]) {
|
|
1415
1429
|
const midiAttrs = getAttributes(soundEl);
|
|
@@ -1570,6 +1584,8 @@ function parseDirectionType(elements) {
|
|
|
1570
1584
|
if (bracketAttrs["number"]) result.number = parseInt(bracketAttrs["number"], 10);
|
|
1571
1585
|
if (bracketAttrs["line-end"]) result.lineEnd = bracketAttrs["line-end"];
|
|
1572
1586
|
if (bracketAttrs["line-type"]) result.lineType = bracketAttrs["line-type"];
|
|
1587
|
+
if (bracketAttrs["default-y"]) result.defaultY = parseFloat(bracketAttrs["default-y"]);
|
|
1588
|
+
if (bracketAttrs["relative-x"]) result.relativeX = parseFloat(bracketAttrs["relative-x"]);
|
|
1573
1589
|
return result;
|
|
1574
1590
|
}
|
|
1575
1591
|
}
|
|
@@ -1869,6 +1885,8 @@ var VALID_BAR_STYLES = /* @__PURE__ */ new Set([
|
|
|
1869
1885
|
"light-heavy",
|
|
1870
1886
|
"heavy-light",
|
|
1871
1887
|
"heavy-heavy",
|
|
1888
|
+
"tick",
|
|
1889
|
+
"short",
|
|
1872
1890
|
"none"
|
|
1873
1891
|
]);
|
|
1874
1892
|
function isValidNoteType(value) {
|
|
@@ -2138,6 +2156,9 @@ function parseSound(elements, attrs) {
|
|
|
2138
2156
|
if (attrs["tocoda"]) sound.tocoda = attrs["tocoda"];
|
|
2139
2157
|
if (attrs["fine"] === "yes") sound.fine = true;
|
|
2140
2158
|
if (attrs["forward-repeat"] === "yes") sound.forwardRepeat = true;
|
|
2159
|
+
if (attrs["damper-pedal"]) sound.damperPedal = attrs["damper-pedal"];
|
|
2160
|
+
if (attrs["soft-pedal"]) sound.softPedal = attrs["soft-pedal"];
|
|
2161
|
+
if (attrs["sostenuto-pedal"]) sound.sostenutoPedal = attrs["sostenuto-pedal"];
|
|
2141
2162
|
for (const el of elements) {
|
|
2142
2163
|
if (el["swing"]) {
|
|
2143
2164
|
const swingContent = el["swing"];
|
|
@@ -2234,6 +2255,8 @@ function parseAuto(data) {
|
|
|
2234
2255
|
var DEFAULT_OPTIONS = {
|
|
2235
2256
|
checkDivisions: true,
|
|
2236
2257
|
checkMeasureDuration: true,
|
|
2258
|
+
checkMeasureFullness: false,
|
|
2259
|
+
// Piano Roll semantics - opt-in
|
|
2237
2260
|
checkPosition: true,
|
|
2238
2261
|
checkTies: true,
|
|
2239
2262
|
checkBeams: true,
|
|
@@ -2290,6 +2313,14 @@ function validate(score, options = {}) {
|
|
|
2290
2313
|
opts.durationTolerance
|
|
2291
2314
|
));
|
|
2292
2315
|
}
|
|
2316
|
+
if (opts.checkMeasureFullness && currentTime) {
|
|
2317
|
+
allErrors.push(...validateMeasureFullness(
|
|
2318
|
+
measure,
|
|
2319
|
+
currentDivisions,
|
|
2320
|
+
currentTime,
|
|
2321
|
+
location
|
|
2322
|
+
));
|
|
2323
|
+
}
|
|
2293
2324
|
if (opts.checkPosition) {
|
|
2294
2325
|
allErrors.push(...validateBackupForward(measure, location));
|
|
2295
2326
|
}
|
|
@@ -2438,6 +2469,92 @@ function calculateVoiceDurations(measure) {
|
|
|
2438
2469
|
}
|
|
2439
2470
|
return voiceDurations;
|
|
2440
2471
|
}
|
|
2472
|
+
function validateMeasureFullness(measure, divisions, time, location) {
|
|
2473
|
+
const errors = [];
|
|
2474
|
+
if (time.senzaMisura) {
|
|
2475
|
+
return errors;
|
|
2476
|
+
}
|
|
2477
|
+
const beats = parseInt(time.beats, 10);
|
|
2478
|
+
if (isNaN(beats)) {
|
|
2479
|
+
return errors;
|
|
2480
|
+
}
|
|
2481
|
+
const expectedDuration = beats / time.beatType * 4 * divisions;
|
|
2482
|
+
const voiceCoverage = /* @__PURE__ */ new Map();
|
|
2483
|
+
let currentPosition = 0;
|
|
2484
|
+
for (const entry of measure.entries) {
|
|
2485
|
+
if (entry.type === "note") {
|
|
2486
|
+
const staff = entry.staff ?? 1;
|
|
2487
|
+
const voice = entry.voice;
|
|
2488
|
+
const key = `${staff}-${voice}`;
|
|
2489
|
+
if (!entry.chord) {
|
|
2490
|
+
if (!voiceCoverage.has(key)) {
|
|
2491
|
+
voiceCoverage.set(key, { segments: [] });
|
|
2492
|
+
}
|
|
2493
|
+
voiceCoverage.get(key).segments.push({
|
|
2494
|
+
start: currentPosition,
|
|
2495
|
+
end: currentPosition + entry.duration
|
|
2496
|
+
});
|
|
2497
|
+
currentPosition += entry.duration;
|
|
2498
|
+
}
|
|
2499
|
+
} else if (entry.type === "backup") {
|
|
2500
|
+
currentPosition -= entry.duration;
|
|
2501
|
+
} else if (entry.type === "forward") {
|
|
2502
|
+
const staff = entry.staff ?? 1;
|
|
2503
|
+
const voice = entry.voice ?? 1;
|
|
2504
|
+
const key = `${staff}-${voice}`;
|
|
2505
|
+
if (!voiceCoverage.has(key)) {
|
|
2506
|
+
voiceCoverage.set(key, { segments: [] });
|
|
2507
|
+
}
|
|
2508
|
+
voiceCoverage.get(key).segments.push({
|
|
2509
|
+
start: currentPosition,
|
|
2510
|
+
end: currentPosition + entry.duration
|
|
2511
|
+
});
|
|
2512
|
+
currentPosition += entry.duration;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
for (const [voiceKey, { segments }] of voiceCoverage.entries()) {
|
|
2516
|
+
const [staff, voice] = voiceKey.split("-").map(Number);
|
|
2517
|
+
const sorted = [...segments].sort((a, b) => a.start - b.start);
|
|
2518
|
+
let lastEnd = 0;
|
|
2519
|
+
const gaps = [];
|
|
2520
|
+
for (const seg of sorted) {
|
|
2521
|
+
if (seg.start > lastEnd) {
|
|
2522
|
+
gaps.push({ start: lastEnd, end: seg.start });
|
|
2523
|
+
}
|
|
2524
|
+
lastEnd = Math.max(lastEnd, seg.end);
|
|
2525
|
+
}
|
|
2526
|
+
if (lastEnd < expectedDuration) {
|
|
2527
|
+
gaps.push({ start: lastEnd, end: expectedDuration });
|
|
2528
|
+
}
|
|
2529
|
+
for (const gap of gaps) {
|
|
2530
|
+
errors.push({
|
|
2531
|
+
code: "VOICE_GAP",
|
|
2532
|
+
level: "warning",
|
|
2533
|
+
message: `Voice ${voice} (staff ${staff}) has gap from position ${gap.start} to ${gap.end}`,
|
|
2534
|
+
location: { ...location, voice, staff },
|
|
2535
|
+
details: {
|
|
2536
|
+
gapStart: gap.start,
|
|
2537
|
+
gapEnd: gap.end,
|
|
2538
|
+
gapDuration: gap.end - gap.start
|
|
2539
|
+
}
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
if (lastEnd < expectedDuration) {
|
|
2543
|
+
errors.push({
|
|
2544
|
+
code: "VOICE_INCOMPLETE",
|
|
2545
|
+
level: "warning",
|
|
2546
|
+
message: `Voice ${voice} (staff ${staff}) ends at ${lastEnd}, expected ${expectedDuration}`,
|
|
2547
|
+
location: { ...location, voice, staff },
|
|
2548
|
+
details: {
|
|
2549
|
+
actualEnd: lastEnd,
|
|
2550
|
+
expectedDuration,
|
|
2551
|
+
missing: expectedDuration - lastEnd
|
|
2552
|
+
}
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return errors;
|
|
2557
|
+
}
|
|
2441
2558
|
function validateBackupForward(measure, location) {
|
|
2442
2559
|
const errors = [];
|
|
2443
2560
|
let position = 0;
|
|
@@ -2904,6 +3021,8 @@ function validateStaffStructure(part, partIndex) {
|
|
|
2904
3021
|
}
|
|
2905
3022
|
var DEFAULT_LOCAL_OPTIONS = {
|
|
2906
3023
|
checkMeasureDuration: true,
|
|
3024
|
+
checkMeasureFullness: false,
|
|
3025
|
+
// Piano Roll semantics - opt-in
|
|
2907
3026
|
checkPosition: true,
|
|
2908
3027
|
checkBeams: true,
|
|
2909
3028
|
checkTuplets: true,
|
|
@@ -2928,6 +3047,14 @@ function validateMeasureLocal(measure, context, options = {}) {
|
|
|
2928
3047
|
opts.durationTolerance
|
|
2929
3048
|
));
|
|
2930
3049
|
}
|
|
3050
|
+
if (opts.checkMeasureFullness && context.time) {
|
|
3051
|
+
errors.push(...validateMeasureFullness(
|
|
3052
|
+
measure,
|
|
3053
|
+
context.divisions,
|
|
3054
|
+
context.time,
|
|
3055
|
+
location
|
|
3056
|
+
));
|
|
3057
|
+
}
|
|
2931
3058
|
if (opts.checkPosition) {
|
|
2932
3059
|
errors.push(...validateBackupForward(measure, location));
|
|
2933
3060
|
}
|
|
@@ -3494,7 +3621,11 @@ function serializeScorePart(part, indent) {
|
|
|
3494
3621
|
lines.push(`${indent} <solo/>`);
|
|
3495
3622
|
}
|
|
3496
3623
|
if (inst.ensemble !== void 0) {
|
|
3497
|
-
|
|
3624
|
+
if (inst.ensemble === 0) {
|
|
3625
|
+
lines.push(`${indent} <ensemble/>`);
|
|
3626
|
+
} else {
|
|
3627
|
+
lines.push(`${indent} <ensemble>${inst.ensemble}</ensemble>`);
|
|
3628
|
+
}
|
|
3498
3629
|
}
|
|
3499
3630
|
lines.push(`${indent} </score-instrument>`);
|
|
3500
3631
|
}
|
|
@@ -3674,6 +3805,9 @@ function serializeAttributes(attrs, indent) {
|
|
|
3674
3805
|
if (attrs.staves !== void 0) {
|
|
3675
3806
|
lines.push(`${indent} <staves>${attrs.staves}</staves>`);
|
|
3676
3807
|
}
|
|
3808
|
+
if (attrs.instruments !== void 0) {
|
|
3809
|
+
lines.push(`${indent} <instruments>${attrs.instruments}</instruments>`);
|
|
3810
|
+
}
|
|
3677
3811
|
if (attrs.clef) {
|
|
3678
3812
|
for (const clef of attrs.clef) {
|
|
3679
3813
|
lines.push(...serializeClef(clef, indent + " "));
|
|
@@ -3762,8 +3896,10 @@ function serializeTime(time, indent) {
|
|
|
3762
3896
|
}
|
|
3763
3897
|
function serializeClef(clef, indent) {
|
|
3764
3898
|
const lines = [];
|
|
3765
|
-
|
|
3766
|
-
|
|
3899
|
+
let attrs = clef.staff ? ` number="${clef.staff}"` : "";
|
|
3900
|
+
if (clef.printObject === false) attrs += ' print-object="no"';
|
|
3901
|
+
if (clef.afterBarline) attrs += ' after-barline="yes"';
|
|
3902
|
+
lines.push(`${indent}<clef${attrs}>`);
|
|
3767
3903
|
lines.push(`${indent} <sign>${clef.sign}</sign>`);
|
|
3768
3904
|
lines.push(`${indent} <line>${clef.line}</line>`);
|
|
3769
3905
|
if (clef.clefOctaveChange !== void 0) {
|
|
@@ -4012,6 +4148,7 @@ function serializeNotationsGroup(notations, indent) {
|
|
|
4012
4148
|
if (notation.number !== void 0) attrs += ` number="${notation.number}"`;
|
|
4013
4149
|
attrs += ` type="${notation.slurType}"`;
|
|
4014
4150
|
if (notation.lineType) attrs += ` line-type="${notation.lineType}"`;
|
|
4151
|
+
if (notation.orientation) attrs += ` orientation="${notation.orientation}"`;
|
|
4015
4152
|
if (notation.defaultX !== void 0) attrs += ` default-x="${notation.defaultX}"`;
|
|
4016
4153
|
if (notation.defaultY !== void 0) attrs += ` default-y="${notation.defaultY}"`;
|
|
4017
4154
|
if (notation.bezierX !== void 0) attrs += ` bezier-x="${notation.bezierX}"`;
|
|
@@ -4325,6 +4462,15 @@ function serializeDirection(direction, indent) {
|
|
|
4325
4462
|
if (direction.sound.dynamics !== void 0) {
|
|
4326
4463
|
attrs2.push(`dynamics="${direction.sound.dynamics}"`);
|
|
4327
4464
|
}
|
|
4465
|
+
if (direction.sound.damperPedal) {
|
|
4466
|
+
attrs2.push(`damper-pedal="${direction.sound.damperPedal}"`);
|
|
4467
|
+
}
|
|
4468
|
+
if (direction.sound.softPedal) {
|
|
4469
|
+
attrs2.push(`soft-pedal="${direction.sound.softPedal}"`);
|
|
4470
|
+
}
|
|
4471
|
+
if (direction.sound.sostenutoPedal) {
|
|
4472
|
+
attrs2.push(`sostenuto-pedal="${direction.sound.sostenutoPedal}"`);
|
|
4473
|
+
}
|
|
4328
4474
|
const attrStr = attrs2.length > 0 ? ` ${attrs2.join(" ")}` : "";
|
|
4329
4475
|
if (direction.sound.midiInstrument) {
|
|
4330
4476
|
lines.push(`${indent} <sound${attrStr}>`);
|
|
@@ -4429,6 +4575,8 @@ function serializeDirectionType(dirType, indent) {
|
|
|
4429
4575
|
if (dirType.number !== void 0) bracketAttrs += ` number="${dirType.number}"`;
|
|
4430
4576
|
if (dirType.lineEnd) bracketAttrs += ` line-end="${dirType.lineEnd}"`;
|
|
4431
4577
|
if (dirType.lineType) bracketAttrs += ` line-type="${dirType.lineType}"`;
|
|
4578
|
+
if (dirType.defaultY !== void 0) bracketAttrs += ` default-y="${dirType.defaultY}"`;
|
|
4579
|
+
if (dirType.relativeX !== void 0) bracketAttrs += ` relative-x="${dirType.relativeX}"`;
|
|
4432
4580
|
lines.push(`${indent} <bracket${bracketAttrs}/>`);
|
|
4433
4581
|
break;
|
|
4434
4582
|
}
|
|
@@ -4763,6 +4911,9 @@ function serializeSound(sound, indent) {
|
|
|
4763
4911
|
if (sound.tocoda) attrs.push(`tocoda="${escapeXml(sound.tocoda)}"`);
|
|
4764
4912
|
if (sound.fine) attrs.push('fine="yes"');
|
|
4765
4913
|
if (sound.forwardRepeat) attrs.push('forward-repeat="yes"');
|
|
4914
|
+
if (sound.damperPedal) attrs.push(`damper-pedal="${sound.damperPedal === true ? "yes" : sound.damperPedal}"`);
|
|
4915
|
+
if (sound.softPedal) attrs.push(`soft-pedal="${sound.softPedal === true ? "yes" : sound.softPedal}"`);
|
|
4916
|
+
if (sound.sostenutoPedal) attrs.push(`sostenuto-pedal="${sound.sostenutoPedal === true ? "yes" : sound.sostenutoPedal}"`);
|
|
4766
4917
|
const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
4767
4918
|
if (sound.swing) {
|
|
4768
4919
|
lines.push(`${indent}<sound${attrStr}>`);
|
|
@@ -5355,178 +5506,1381 @@ function getNormalizedDuration(note, options) {
|
|
|
5355
5506
|
const currentDivisions = options.currentDivisions ?? 1;
|
|
5356
5507
|
return note.duration * options.baseDivisions / currentDivisions;
|
|
5357
5508
|
}
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
if (!part) return void 0;
|
|
5363
|
-
const targetMeasure = String(options.measure);
|
|
5364
|
-
return part.measures.find((m) => m.number === targetMeasure);
|
|
5365
|
-
}
|
|
5366
|
-
function getMeasureByIndex(score, options) {
|
|
5367
|
-
const part = score.parts[options.part];
|
|
5368
|
-
if (!part) return void 0;
|
|
5369
|
-
return part.measures[options.measureIndex];
|
|
5370
|
-
}
|
|
5371
|
-
function getMeasureCount(score) {
|
|
5372
|
-
if (score.parts.length === 0) return 0;
|
|
5373
|
-
return score.parts[0].measures.length;
|
|
5374
|
-
}
|
|
5375
|
-
function getDivisions(score, options) {
|
|
5376
|
-
const part = score.parts[options.part];
|
|
5377
|
-
if (!part) return 1;
|
|
5378
|
-
const targetMeasure = parseInt(String(options.measure), 10);
|
|
5379
|
-
if (isNaN(targetMeasure)) return 1;
|
|
5380
|
-
for (let i = 0; i < part.measures.length; i++) {
|
|
5381
|
-
const m = part.measures[i];
|
|
5382
|
-
const mNum = parseInt(m.number, 10);
|
|
5383
|
-
if (!isNaN(mNum) && mNum > targetMeasure) break;
|
|
5384
|
-
if (m.attributes?.divisions !== void 0) {
|
|
5509
|
+
function getEntriesForStaff(measure, staff) {
|
|
5510
|
+
return measure.entries.filter((entry) => {
|
|
5511
|
+
if (entry.type === "note") {
|
|
5512
|
+
return (entry.staff ?? 1) === staff;
|
|
5385
5513
|
}
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
for (const m of part.measures) {
|
|
5389
|
-
const mNum = parseInt(m.number, 10);
|
|
5390
|
-
if (!isNaN(mNum) && mNum > targetMeasure) break;
|
|
5391
|
-
if (m.attributes?.divisions !== void 0) {
|
|
5392
|
-
divisions = m.attributes.divisions;
|
|
5514
|
+
if (entry.type === "forward") {
|
|
5515
|
+
return (entry.staff ?? 1) === staff;
|
|
5393
5516
|
}
|
|
5394
|
-
|
|
5395
|
-
|
|
5517
|
+
if (entry.type === "direction") {
|
|
5518
|
+
return (entry.staff ?? 1) === staff;
|
|
5519
|
+
}
|
|
5520
|
+
if (entry.type === "backup") {
|
|
5521
|
+
return false;
|
|
5522
|
+
}
|
|
5523
|
+
return false;
|
|
5524
|
+
});
|
|
5396
5525
|
}
|
|
5397
|
-
function
|
|
5398
|
-
const
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
if (m.attributes.divisions !== void 0) result.divisions = m.attributes.divisions;
|
|
5407
|
-
if (m.attributes.time !== void 0) result.time = m.attributes.time;
|
|
5408
|
-
if (m.attributes.key !== void 0) result.key = m.attributes.key;
|
|
5409
|
-
if (m.attributes.clef !== void 0) result.clef = m.attributes.clef;
|
|
5410
|
-
if (m.attributes.staves !== void 0) result.staves = m.attributes.staves;
|
|
5411
|
-
if (m.attributes.transpose !== void 0) result.transpose = m.attributes.transpose;
|
|
5526
|
+
function buildVoiceToStaffMap(measure) {
|
|
5527
|
+
const map = /* @__PURE__ */ new Map();
|
|
5528
|
+
for (const entry of measure.entries) {
|
|
5529
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
5530
|
+
const voice = entry.voice;
|
|
5531
|
+
const staff = entry.staff;
|
|
5532
|
+
if (!map.has(voice)) {
|
|
5533
|
+
map.set(voice, staff);
|
|
5534
|
+
}
|
|
5412
5535
|
}
|
|
5413
5536
|
}
|
|
5414
|
-
return
|
|
5537
|
+
return {
|
|
5538
|
+
get: (voice) => map.get(voice),
|
|
5539
|
+
has: (voice) => map.has(voice),
|
|
5540
|
+
entries: () => map.entries(),
|
|
5541
|
+
size: map.size
|
|
5542
|
+
};
|
|
5415
5543
|
}
|
|
5416
|
-
function
|
|
5417
|
-
const
|
|
5418
|
-
for (const
|
|
5419
|
-
for (const
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
const minValue = pitchToSemitone(filter.pitchRange.min);
|
|
5426
|
-
if (noteValue < minValue) continue;
|
|
5427
|
-
}
|
|
5428
|
-
if (filter.pitchRange.max) {
|
|
5429
|
-
const maxValue = pitchToSemitone(filter.pitchRange.max);
|
|
5430
|
-
if (noteValue > maxValue) continue;
|
|
5431
|
-
}
|
|
5432
|
-
}
|
|
5433
|
-
if (filter.voice !== void 0 && entry.voice !== filter.voice) continue;
|
|
5434
|
-
if (filter.staff !== void 0 && (entry.staff ?? 1) !== filter.staff) continue;
|
|
5435
|
-
if (filter.noteType !== void 0 && entry.noteType !== filter.noteType) continue;
|
|
5436
|
-
if (filter.hasTie !== void 0) {
|
|
5437
|
-
const hasTie = entry.tie !== void 0;
|
|
5438
|
-
if (filter.hasTie !== hasTie) continue;
|
|
5544
|
+
function buildVoiceToStaffMapForPart(part) {
|
|
5545
|
+
const map = /* @__PURE__ */ new Map();
|
|
5546
|
+
for (const measure of part.measures) {
|
|
5547
|
+
for (const entry of measure.entries) {
|
|
5548
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
5549
|
+
const voice = entry.voice;
|
|
5550
|
+
const staff = entry.staff;
|
|
5551
|
+
if (!map.has(voice)) {
|
|
5552
|
+
map.set(voice, staff);
|
|
5439
5553
|
}
|
|
5440
|
-
results.push(entry);
|
|
5441
5554
|
}
|
|
5442
5555
|
}
|
|
5443
5556
|
}
|
|
5444
|
-
return
|
|
5557
|
+
return {
|
|
5558
|
+
get: (voice) => map.get(voice),
|
|
5559
|
+
has: (voice) => map.has(voice),
|
|
5560
|
+
entries: () => map.entries(),
|
|
5561
|
+
size: map.size
|
|
5562
|
+
};
|
|
5445
5563
|
}
|
|
5446
|
-
function
|
|
5447
|
-
if (
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5564
|
+
function inferStaff(entry, voiceToStaffMap) {
|
|
5565
|
+
if (entry.staff !== void 0) {
|
|
5566
|
+
return entry.staff;
|
|
5567
|
+
}
|
|
5568
|
+
const inferredStaff = voiceToStaffMap.get(entry.voice);
|
|
5569
|
+
if (inferredStaff !== void 0) {
|
|
5570
|
+
return inferredStaff;
|
|
5571
|
+
}
|
|
5572
|
+
return 1;
|
|
5573
|
+
}
|
|
5574
|
+
function getEffectiveStaff(entry, measure) {
|
|
5575
|
+
if (entry.staff !== void 0) {
|
|
5576
|
+
return entry.staff;
|
|
5577
|
+
}
|
|
5578
|
+
const map = buildVoiceToStaffMap(measure);
|
|
5579
|
+
return inferStaff(entry, map);
|
|
5580
|
+
}
|
|
5581
|
+
function getClefForStaff(score, options) {
|
|
5582
|
+
const part = score.parts[options.partIndex];
|
|
5583
|
+
if (!part) return void 0;
|
|
5584
|
+
for (let i = options.measureIndex; i >= 0; i--) {
|
|
5585
|
+
const measure = part.measures[i];
|
|
5456
5586
|
for (const entry of measure.entries) {
|
|
5457
|
-
if (entry.type === "
|
|
5458
|
-
|
|
5587
|
+
if (entry.type === "attributes" && entry.attributes.clef) {
|
|
5588
|
+
for (const clef of entry.attributes.clef) {
|
|
5589
|
+
if ((clef.staff ?? 1) === options.staff) {
|
|
5590
|
+
return clef;
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5459
5593
|
}
|
|
5460
5594
|
}
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5595
|
+
if (measure.attributes?.clef) {
|
|
5596
|
+
for (const clef of measure.attributes.clef) {
|
|
5597
|
+
if ((clef.staff ?? 1) === options.staff) {
|
|
5598
|
+
return clef;
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5466
5601
|
}
|
|
5467
|
-
totalDuration += measureDuration;
|
|
5468
5602
|
}
|
|
5469
|
-
return
|
|
5470
|
-
}
|
|
5471
|
-
function getPartById(score, id) {
|
|
5472
|
-
return score.parts.find((p) => p.id === id);
|
|
5473
|
-
}
|
|
5474
|
-
function getPartIndex(score, id) {
|
|
5475
|
-
return score.parts.findIndex((p) => p.id === id);
|
|
5603
|
+
return void 0;
|
|
5476
5604
|
}
|
|
5477
|
-
function
|
|
5478
|
-
const
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5605
|
+
function getVoicesForStaff(measure, staff) {
|
|
5606
|
+
const voices = /* @__PURE__ */ new Set();
|
|
5607
|
+
for (const entry of measure.entries) {
|
|
5608
|
+
if (entry.type === "note") {
|
|
5609
|
+
const entryStaff = entry.staff ?? 1;
|
|
5610
|
+
if (entryStaff === staff) {
|
|
5611
|
+
voices.add(entry.voice);
|
|
5612
|
+
}
|
|
5483
5613
|
}
|
|
5484
5614
|
}
|
|
5485
|
-
return
|
|
5615
|
+
return Array.from(voices).sort((a, b) => a - b);
|
|
5486
5616
|
}
|
|
5487
|
-
function
|
|
5617
|
+
function getStaffRange(score, partIndex) {
|
|
5488
5618
|
const part = score.parts[partIndex];
|
|
5489
|
-
if (!part) return 1;
|
|
5619
|
+
if (!part) return { min: 1, max: 1 };
|
|
5620
|
+
let min = 1;
|
|
5621
|
+
let max = 1;
|
|
5490
5622
|
for (const measure of part.measures) {
|
|
5491
5623
|
if (measure.attributes?.staves !== void 0) {
|
|
5492
|
-
|
|
5624
|
+
max = Math.max(max, measure.attributes.staves);
|
|
5625
|
+
}
|
|
5626
|
+
for (const entry of measure.entries) {
|
|
5627
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
5628
|
+
max = Math.max(max, entry.staff);
|
|
5629
|
+
}
|
|
5493
5630
|
}
|
|
5494
5631
|
}
|
|
5495
|
-
return
|
|
5496
|
-
}
|
|
5497
|
-
function measureRoundtrip(original, exported) {
|
|
5498
|
-
const notesOriginal = countNotes(original);
|
|
5499
|
-
const notesExported = countNotes(exported);
|
|
5500
|
-
const notesPreserved = Math.min(notesOriginal, notesExported);
|
|
5501
|
-
const measuresOriginal = getMeasureCount(original);
|
|
5502
|
-
const measuresExported = getMeasureCount(exported);
|
|
5503
|
-
const measuresPreserved = Math.min(measuresOriginal, measuresExported);
|
|
5504
|
-
const partsOriginal = original.parts.length;
|
|
5505
|
-
const partsExported = exported.parts.length;
|
|
5506
|
-
const partsPreserved = Math.min(partsOriginal, partsExported);
|
|
5507
|
-
const preservationRate = notesOriginal > 0 ? notesPreserved / notesOriginal : 1;
|
|
5508
|
-
return {
|
|
5509
|
-
notesOriginal,
|
|
5510
|
-
notesPreserved,
|
|
5511
|
-
measuresOriginal,
|
|
5512
|
-
measuresPreserved,
|
|
5513
|
-
partsOriginal,
|
|
5514
|
-
partsPreserved,
|
|
5515
|
-
preservationRate
|
|
5516
|
-
};
|
|
5632
|
+
return { min, max };
|
|
5517
5633
|
}
|
|
5518
|
-
function
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5634
|
+
function getEntriesAtPosition(measure, position, options) {
|
|
5635
|
+
const result = [];
|
|
5636
|
+
const state = createPositionState();
|
|
5637
|
+
for (const entry of measure.entries) {
|
|
5638
|
+
const currentPosition = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
5639
|
+
if (currentPosition === position) {
|
|
5640
|
+
if (entry.type === "note") {
|
|
5641
|
+
if (options?.staff !== void 0 && (entry.staff ?? 1) !== options.staff) {
|
|
5642
|
+
updatePositionForEntry(state, entry);
|
|
5643
|
+
continue;
|
|
5644
|
+
}
|
|
5645
|
+
if (options?.voice !== void 0 && entry.voice !== options.voice) {
|
|
5646
|
+
updatePositionForEntry(state, entry);
|
|
5647
|
+
continue;
|
|
5648
|
+
}
|
|
5649
|
+
if (options?.includeChordNotes === false && entry.chord) {
|
|
5650
|
+
updatePositionForEntry(state, entry);
|
|
5651
|
+
continue;
|
|
5525
5652
|
}
|
|
5526
5653
|
}
|
|
5654
|
+
result.push(entry);
|
|
5527
5655
|
}
|
|
5656
|
+
updatePositionForEntry(state, entry);
|
|
5528
5657
|
}
|
|
5529
|
-
return
|
|
5658
|
+
return result;
|
|
5659
|
+
}
|
|
5660
|
+
function getNotesAtPosition(measure, position, options) {
|
|
5661
|
+
return getEntriesAtPosition(measure, position, options).filter(
|
|
5662
|
+
(entry) => entry.type === "note"
|
|
5663
|
+
);
|
|
5664
|
+
}
|
|
5665
|
+
function getEntriesInRange(measure, range, options) {
|
|
5666
|
+
const result = [];
|
|
5667
|
+
const state = createPositionState();
|
|
5668
|
+
for (const entry of measure.entries) {
|
|
5669
|
+
const currentPosition = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
5670
|
+
if (currentPosition >= range.start && currentPosition < range.end) {
|
|
5671
|
+
if (entry.type === "note") {
|
|
5672
|
+
if (options?.staff !== void 0 && (entry.staff ?? 1) !== options.staff) {
|
|
5673
|
+
updatePositionForEntry(state, entry);
|
|
5674
|
+
continue;
|
|
5675
|
+
}
|
|
5676
|
+
if (options?.voice !== void 0 && entry.voice !== options.voice) {
|
|
5677
|
+
updatePositionForEntry(state, entry);
|
|
5678
|
+
continue;
|
|
5679
|
+
}
|
|
5680
|
+
if (options?.includeChordNotes === false && entry.chord) {
|
|
5681
|
+
updatePositionForEntry(state, entry);
|
|
5682
|
+
continue;
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
result.push(entry);
|
|
5686
|
+
}
|
|
5687
|
+
updatePositionForEntry(state, entry);
|
|
5688
|
+
}
|
|
5689
|
+
return result;
|
|
5690
|
+
}
|
|
5691
|
+
function getNotesInRange(measure, range, options) {
|
|
5692
|
+
return getEntriesInRange(measure, range, options).filter(
|
|
5693
|
+
(entry) => entry.type === "note"
|
|
5694
|
+
);
|
|
5695
|
+
}
|
|
5696
|
+
function getVerticalSlice(score, options) {
|
|
5697
|
+
const parts = /* @__PURE__ */ new Map();
|
|
5698
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
5699
|
+
const part = score.parts[partIndex];
|
|
5700
|
+
const measure = part.measures[options.measureIndex];
|
|
5701
|
+
if (!measure) continue;
|
|
5702
|
+
const notes = getNotesAtPosition(measure, options.position);
|
|
5703
|
+
if (notes.length > 0) {
|
|
5704
|
+
parts.set(partIndex, notes);
|
|
5705
|
+
}
|
|
5706
|
+
}
|
|
5707
|
+
return {
|
|
5708
|
+
measureIndex: options.measureIndex,
|
|
5709
|
+
position: options.position,
|
|
5710
|
+
parts
|
|
5711
|
+
};
|
|
5712
|
+
}
|
|
5713
|
+
function getVoiceLine(score, options) {
|
|
5714
|
+
const part = score.parts[options.partIndex];
|
|
5715
|
+
if (!part) {
|
|
5716
|
+
return { partIndex: options.partIndex, voice: options.voice, staff: options.staff, notes: [] };
|
|
5717
|
+
}
|
|
5718
|
+
const notes = [];
|
|
5719
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
5720
|
+
const measure = part.measures[measureIndex];
|
|
5721
|
+
const state = createPositionState();
|
|
5722
|
+
for (const entry of measure.entries) {
|
|
5723
|
+
if (entry.type === "note") {
|
|
5724
|
+
const entryStaff = entry.staff ?? 1;
|
|
5725
|
+
const matchesVoice = entry.voice === options.voice;
|
|
5726
|
+
const matchesStaff = options.staff === void 0 || entryStaff === options.staff;
|
|
5727
|
+
if (matchesVoice && matchesStaff) {
|
|
5728
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
5729
|
+
notes.push({
|
|
5730
|
+
note: entry,
|
|
5731
|
+
part,
|
|
5732
|
+
partIndex: options.partIndex,
|
|
5733
|
+
measure,
|
|
5734
|
+
measureIndex,
|
|
5735
|
+
position
|
|
5736
|
+
});
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
updatePositionForEntry(state, entry);
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
return {
|
|
5743
|
+
partIndex: options.partIndex,
|
|
5744
|
+
voice: options.voice,
|
|
5745
|
+
staff: options.staff,
|
|
5746
|
+
notes
|
|
5747
|
+
};
|
|
5748
|
+
}
|
|
5749
|
+
function getVoiceLineInRange(score, options) {
|
|
5750
|
+
const part = score.parts[options.partIndex];
|
|
5751
|
+
if (!part) {
|
|
5752
|
+
return { partIndex: options.partIndex, voice: options.voice, staff: options.staff, notes: [] };
|
|
5753
|
+
}
|
|
5754
|
+
const notes = [];
|
|
5755
|
+
for (let measureIndex = options.startMeasure; measureIndex <= options.endMeasure && measureIndex < part.measures.length; measureIndex++) {
|
|
5756
|
+
const measure = part.measures[measureIndex];
|
|
5757
|
+
const state = createPositionState();
|
|
5758
|
+
for (const entry of measure.entries) {
|
|
5759
|
+
if (entry.type === "note") {
|
|
5760
|
+
const entryStaff = entry.staff ?? 1;
|
|
5761
|
+
const matchesVoice = entry.voice === options.voice;
|
|
5762
|
+
const matchesStaff = options.staff === void 0 || entryStaff === options.staff;
|
|
5763
|
+
if (matchesVoice && matchesStaff) {
|
|
5764
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
5765
|
+
notes.push({
|
|
5766
|
+
note: entry,
|
|
5767
|
+
part,
|
|
5768
|
+
partIndex: options.partIndex,
|
|
5769
|
+
measure,
|
|
5770
|
+
measureIndex,
|
|
5771
|
+
position
|
|
5772
|
+
});
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
updatePositionForEntry(state, entry);
|
|
5776
|
+
}
|
|
5777
|
+
}
|
|
5778
|
+
return {
|
|
5779
|
+
partIndex: options.partIndex,
|
|
5780
|
+
voice: options.voice,
|
|
5781
|
+
staff: options.staff,
|
|
5782
|
+
notes
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
function* iterateEntries(score) {
|
|
5786
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
5787
|
+
const part = score.parts[partIndex];
|
|
5788
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
5789
|
+
const measure = part.measures[measureIndex];
|
|
5790
|
+
const state = createPositionState();
|
|
5791
|
+
for (const entry of measure.entries) {
|
|
5792
|
+
const position = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
5793
|
+
yield {
|
|
5794
|
+
entry,
|
|
5795
|
+
part,
|
|
5796
|
+
partIndex,
|
|
5797
|
+
measure,
|
|
5798
|
+
measureIndex,
|
|
5799
|
+
position
|
|
5800
|
+
};
|
|
5801
|
+
updatePositionForEntry(state, entry);
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
function getNextNote(score, context) {
|
|
5807
|
+
const part = score.parts[context.partIndex];
|
|
5808
|
+
if (!part) return null;
|
|
5809
|
+
let foundCurrent = false;
|
|
5810
|
+
for (let measureIndex = context.measureIndex; measureIndex < part.measures.length; measureIndex++) {
|
|
5811
|
+
const measure = part.measures[measureIndex];
|
|
5812
|
+
const state = createPositionState();
|
|
5813
|
+
for (const entry of measure.entries) {
|
|
5814
|
+
if (entry.type === "note") {
|
|
5815
|
+
const entryPosition = entry.chord ? state.lastNonChordPosition : state.position;
|
|
5816
|
+
if (measureIndex === context.measureIndex && entry === context.note) {
|
|
5817
|
+
foundCurrent = true;
|
|
5818
|
+
updatePositionForEntry(state, entry);
|
|
5819
|
+
continue;
|
|
5820
|
+
}
|
|
5821
|
+
if (foundCurrent && entry.voice === context.note.voice && !entry.chord) {
|
|
5822
|
+
if (context.note.staff !== void 0) {
|
|
5823
|
+
if ((entry.staff ?? 1) !== context.note.staff) {
|
|
5824
|
+
updatePositionForEntry(state, entry);
|
|
5825
|
+
continue;
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
return {
|
|
5829
|
+
note: entry,
|
|
5830
|
+
part,
|
|
5831
|
+
partIndex: context.partIndex,
|
|
5832
|
+
measure,
|
|
5833
|
+
measureIndex,
|
|
5834
|
+
position: entryPosition
|
|
5835
|
+
};
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
updatePositionForEntry(state, entry);
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
return null;
|
|
5842
|
+
}
|
|
5843
|
+
function getPrevNote(score, context) {
|
|
5844
|
+
const part = score.parts[context.partIndex];
|
|
5845
|
+
if (!part) return null;
|
|
5846
|
+
let lastCandidate = null;
|
|
5847
|
+
for (let measureIndex = 0; measureIndex <= context.measureIndex; measureIndex++) {
|
|
5848
|
+
const measure = part.measures[measureIndex];
|
|
5849
|
+
const state = createPositionState();
|
|
5850
|
+
for (const entry of measure.entries) {
|
|
5851
|
+
if (entry.type === "note") {
|
|
5852
|
+
const entryPosition = entry.chord ? state.lastNonChordPosition : state.position;
|
|
5853
|
+
if (measureIndex === context.measureIndex && entry === context.note) {
|
|
5854
|
+
return lastCandidate;
|
|
5855
|
+
}
|
|
5856
|
+
if (entry.voice === context.note.voice && !entry.chord) {
|
|
5857
|
+
if (context.note.staff !== void 0) {
|
|
5858
|
+
if ((entry.staff ?? 1) !== context.note.staff) {
|
|
5859
|
+
updatePositionForEntry(state, entry);
|
|
5860
|
+
continue;
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
lastCandidate = {
|
|
5864
|
+
note: entry,
|
|
5865
|
+
part,
|
|
5866
|
+
partIndex: context.partIndex,
|
|
5867
|
+
measure,
|
|
5868
|
+
measureIndex,
|
|
5869
|
+
position: entryPosition
|
|
5870
|
+
};
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5873
|
+
updatePositionForEntry(state, entry);
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
return null;
|
|
5877
|
+
}
|
|
5878
|
+
function getAdjacentNotes(score, context) {
|
|
5879
|
+
return {
|
|
5880
|
+
prev: getPrevNote(score, context),
|
|
5881
|
+
next: getNextNote(score, context)
|
|
5882
|
+
};
|
|
5883
|
+
}
|
|
5884
|
+
function getDirections(score, options) {
|
|
5885
|
+
const results = [];
|
|
5886
|
+
const startPart = options?.partIndex ?? 0;
|
|
5887
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
5888
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
5889
|
+
const part = score.parts[partIndex];
|
|
5890
|
+
if (!part) continue;
|
|
5891
|
+
const startMeasure = options?.measureIndex ?? 0;
|
|
5892
|
+
const endMeasure = options?.measureIndex !== void 0 ? options.measureIndex + 1 : part.measures.length;
|
|
5893
|
+
for (let measureIndex = startMeasure; measureIndex < endMeasure; measureIndex++) {
|
|
5894
|
+
const measure = part.measures[measureIndex];
|
|
5895
|
+
if (!measure) continue;
|
|
5896
|
+
const state = createPositionState();
|
|
5897
|
+
for (const entry of measure.entries) {
|
|
5898
|
+
if (entry.type === "direction") {
|
|
5899
|
+
results.push({
|
|
5900
|
+
direction: entry,
|
|
5901
|
+
part,
|
|
5902
|
+
partIndex,
|
|
5903
|
+
measure,
|
|
5904
|
+
measureIndex,
|
|
5905
|
+
position: state.position
|
|
5906
|
+
});
|
|
5907
|
+
}
|
|
5908
|
+
updatePositionForEntry(state, entry);
|
|
5909
|
+
}
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
return results;
|
|
5913
|
+
}
|
|
5914
|
+
function getDirectionsAtPosition(measure, position) {
|
|
5915
|
+
const results = [];
|
|
5916
|
+
const state = createPositionState();
|
|
5917
|
+
for (const entry of measure.entries) {
|
|
5918
|
+
if (entry.type === "direction" && state.position === position) {
|
|
5919
|
+
results.push(entry);
|
|
5920
|
+
}
|
|
5921
|
+
updatePositionForEntry(state, entry);
|
|
5922
|
+
}
|
|
5923
|
+
return results;
|
|
5924
|
+
}
|
|
5925
|
+
function findDirectionsByType(score, kind) {
|
|
5926
|
+
const allDirections = getDirections(score);
|
|
5927
|
+
return allDirections.filter(
|
|
5928
|
+
(d) => d.direction.directionTypes.some((dt) => dt.kind === kind)
|
|
5929
|
+
);
|
|
5930
|
+
}
|
|
5931
|
+
function getDynamics(score, options) {
|
|
5932
|
+
const results = [];
|
|
5933
|
+
const startPart = options?.partIndex ?? 0;
|
|
5934
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
5935
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
5936
|
+
const part = score.parts[partIndex];
|
|
5937
|
+
if (!part) continue;
|
|
5938
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
5939
|
+
const measure = part.measures[measureIndex];
|
|
5940
|
+
const state = createPositionState();
|
|
5941
|
+
for (const entry of measure.entries) {
|
|
5942
|
+
if (entry.type === "direction") {
|
|
5943
|
+
for (const dirType of entry.directionTypes) {
|
|
5944
|
+
if (dirType.kind === "dynamics") {
|
|
5945
|
+
results.push({
|
|
5946
|
+
dynamic: dirType.value,
|
|
5947
|
+
direction: entry,
|
|
5948
|
+
part,
|
|
5949
|
+
partIndex,
|
|
5950
|
+
measure,
|
|
5951
|
+
measureIndex,
|
|
5952
|
+
position: state.position
|
|
5953
|
+
});
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
updatePositionForEntry(state, entry);
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
return results;
|
|
5962
|
+
}
|
|
5963
|
+
function getTempoMarkings(score) {
|
|
5964
|
+
const results = [];
|
|
5965
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
5966
|
+
const part = score.parts[partIndex];
|
|
5967
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
5968
|
+
const measure = part.measures[measureIndex];
|
|
5969
|
+
const state = createPositionState();
|
|
5970
|
+
for (const entry of measure.entries) {
|
|
5971
|
+
if (entry.type === "direction") {
|
|
5972
|
+
for (const dirType of entry.directionTypes) {
|
|
5973
|
+
if (dirType.kind === "metronome") {
|
|
5974
|
+
results.push({
|
|
5975
|
+
beatUnit: dirType.beatUnit,
|
|
5976
|
+
perMinute: dirType.perMinute,
|
|
5977
|
+
beatUnitDot: dirType.beatUnitDot,
|
|
5978
|
+
direction: entry,
|
|
5979
|
+
partIndex,
|
|
5980
|
+
measureIndex,
|
|
5981
|
+
position: state.position
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
updatePositionForEntry(state, entry);
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
return results;
|
|
5991
|
+
}
|
|
5992
|
+
function getPedalMarkings(score, options) {
|
|
5993
|
+
const results = [];
|
|
5994
|
+
const startPart = options?.partIndex ?? 0;
|
|
5995
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
5996
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
5997
|
+
const part = score.parts[partIndex];
|
|
5998
|
+
if (!part) continue;
|
|
5999
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6000
|
+
const measure = part.measures[measureIndex];
|
|
6001
|
+
const state = createPositionState();
|
|
6002
|
+
for (const entry of measure.entries) {
|
|
6003
|
+
if (entry.type === "direction") {
|
|
6004
|
+
for (const dirType of entry.directionTypes) {
|
|
6005
|
+
if (dirType.kind === "pedal") {
|
|
6006
|
+
results.push({
|
|
6007
|
+
pedalType: dirType.type,
|
|
6008
|
+
direction: entry,
|
|
6009
|
+
partIndex,
|
|
6010
|
+
measureIndex,
|
|
6011
|
+
position: state.position
|
|
6012
|
+
});
|
|
6013
|
+
}
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
updatePositionForEntry(state, entry);
|
|
6017
|
+
}
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
return results;
|
|
6021
|
+
}
|
|
6022
|
+
function getWedges(score, options) {
|
|
6023
|
+
const results = [];
|
|
6024
|
+
const startPart = options?.partIndex ?? 0;
|
|
6025
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6026
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6027
|
+
const part = score.parts[partIndex];
|
|
6028
|
+
if (!part) continue;
|
|
6029
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6030
|
+
const measure = part.measures[measureIndex];
|
|
6031
|
+
const state = createPositionState();
|
|
6032
|
+
for (const entry of measure.entries) {
|
|
6033
|
+
if (entry.type === "direction") {
|
|
6034
|
+
for (const dirType of entry.directionTypes) {
|
|
6035
|
+
if (dirType.kind === "wedge") {
|
|
6036
|
+
results.push({
|
|
6037
|
+
wedgeType: dirType.type,
|
|
6038
|
+
direction: entry,
|
|
6039
|
+
partIndex,
|
|
6040
|
+
measureIndex,
|
|
6041
|
+
position: state.position
|
|
6042
|
+
});
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
updatePositionForEntry(state, entry);
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
return results;
|
|
6051
|
+
}
|
|
6052
|
+
function getOctaveShifts(score, options) {
|
|
6053
|
+
const results = [];
|
|
6054
|
+
const startPart = options?.partIndex ?? 0;
|
|
6055
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6056
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6057
|
+
const part = score.parts[partIndex];
|
|
6058
|
+
if (!part) continue;
|
|
6059
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6060
|
+
const measure = part.measures[measureIndex];
|
|
6061
|
+
const state = createPositionState();
|
|
6062
|
+
for (const entry of measure.entries) {
|
|
6063
|
+
if (entry.type === "direction") {
|
|
6064
|
+
for (const dirType of entry.directionTypes) {
|
|
6065
|
+
if (dirType.kind === "octave-shift") {
|
|
6066
|
+
results.push({
|
|
6067
|
+
shiftType: dirType.type,
|
|
6068
|
+
size: dirType.size,
|
|
6069
|
+
direction: entry,
|
|
6070
|
+
partIndex,
|
|
6071
|
+
measureIndex,
|
|
6072
|
+
position: state.position
|
|
6073
|
+
});
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
updatePositionForEntry(state, entry);
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
return results;
|
|
6082
|
+
}
|
|
6083
|
+
function getTiedNoteGroups(score, options) {
|
|
6084
|
+
const results = [];
|
|
6085
|
+
const startPart = options?.partIndex ?? 0;
|
|
6086
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6087
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6088
|
+
const part = score.parts[partIndex];
|
|
6089
|
+
if (!part) continue;
|
|
6090
|
+
const pendingTies = /* @__PURE__ */ new Map();
|
|
6091
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6092
|
+
const measure = part.measures[measureIndex];
|
|
6093
|
+
const state = createPositionState();
|
|
6094
|
+
for (const entry of measure.entries) {
|
|
6095
|
+
if (entry.type === "note" && entry.pitch) {
|
|
6096
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6097
|
+
const pitchKey = `${entry.pitch.step}${entry.pitch.octave}-${entry.voice}`;
|
|
6098
|
+
const context = {
|
|
6099
|
+
note: entry,
|
|
6100
|
+
part,
|
|
6101
|
+
partIndex,
|
|
6102
|
+
measure,
|
|
6103
|
+
measureIndex,
|
|
6104
|
+
position
|
|
6105
|
+
};
|
|
6106
|
+
const hasTieStart = entry.tie?.type === "start" || entry.ties?.some((t) => t.type === "start") || entry.notations?.some((n) => n.type === "tied" && n.tiedType === "start");
|
|
6107
|
+
const hasTieStop = entry.tie?.type === "stop" || entry.ties?.some((t) => t.type === "stop") || entry.notations?.some((n) => n.type === "tied" && n.tiedType === "stop");
|
|
6108
|
+
if (hasTieStop && pendingTies.has(pitchKey)) {
|
|
6109
|
+
const group = pendingTies.get(pitchKey);
|
|
6110
|
+
group.push(context);
|
|
6111
|
+
if (!hasTieStart) {
|
|
6112
|
+
const totalDuration = group.reduce((sum, nc) => sum + nc.note.duration, 0);
|
|
6113
|
+
results.push({ notes: group, totalDuration });
|
|
6114
|
+
pendingTies.delete(pitchKey);
|
|
6115
|
+
}
|
|
6116
|
+
} else if (hasTieStart) {
|
|
6117
|
+
pendingTies.set(pitchKey, [context]);
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
updatePositionForEntry(state, entry);
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
return results;
|
|
6125
|
+
}
|
|
6126
|
+
function getSlurSpans(score, options) {
|
|
6127
|
+
const results = [];
|
|
6128
|
+
const startPart = options?.partIndex ?? 0;
|
|
6129
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6130
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6131
|
+
const part = score.parts[partIndex];
|
|
6132
|
+
if (!part) continue;
|
|
6133
|
+
const pendingSlurs = /* @__PURE__ */ new Map();
|
|
6134
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6135
|
+
const measure = part.measures[measureIndex];
|
|
6136
|
+
const state = createPositionState();
|
|
6137
|
+
for (const entry of measure.entries) {
|
|
6138
|
+
if (entry.type === "note") {
|
|
6139
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6140
|
+
const context = {
|
|
6141
|
+
note: entry,
|
|
6142
|
+
part,
|
|
6143
|
+
partIndex,
|
|
6144
|
+
measure,
|
|
6145
|
+
measureIndex,
|
|
6146
|
+
position
|
|
6147
|
+
};
|
|
6148
|
+
if (entry.notations) {
|
|
6149
|
+
for (const notation of entry.notations) {
|
|
6150
|
+
if (notation.type === "slur") {
|
|
6151
|
+
const slurNumber = notation.number ?? 1;
|
|
6152
|
+
if (notation.slurType === "start") {
|
|
6153
|
+
pendingSlurs.set(slurNumber, { startNote: context, notes: [context] });
|
|
6154
|
+
} else if (notation.slurType === "stop" && pendingSlurs.has(slurNumber)) {
|
|
6155
|
+
const pending = pendingSlurs.get(slurNumber);
|
|
6156
|
+
pending.notes.push(context);
|
|
6157
|
+
results.push({
|
|
6158
|
+
number: slurNumber,
|
|
6159
|
+
startNote: pending.startNote,
|
|
6160
|
+
endNote: context,
|
|
6161
|
+
notes: pending.notes
|
|
6162
|
+
});
|
|
6163
|
+
pendingSlurs.delete(slurNumber);
|
|
6164
|
+
} else if (notation.slurType === "continue" && pendingSlurs.has(slurNumber)) {
|
|
6165
|
+
pendingSlurs.get(slurNumber).notes.push(context);
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
for (const [, pending] of pendingSlurs) {
|
|
6171
|
+
const lastNote = pending.notes[pending.notes.length - 1];
|
|
6172
|
+
if (lastNote !== context) {
|
|
6173
|
+
pending.notes.push(context);
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
}
|
|
6177
|
+
updatePositionForEntry(state, entry);
|
|
6178
|
+
}
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
return results;
|
|
6182
|
+
}
|
|
6183
|
+
function getTupletGroups(score, options) {
|
|
6184
|
+
const results = [];
|
|
6185
|
+
const startPart = options?.partIndex ?? 0;
|
|
6186
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6187
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6188
|
+
const part = score.parts[partIndex];
|
|
6189
|
+
if (!part) continue;
|
|
6190
|
+
const pendingTuplets = /* @__PURE__ */ new Map();
|
|
6191
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6192
|
+
const measure = part.measures[measureIndex];
|
|
6193
|
+
const state = createPositionState();
|
|
6194
|
+
for (const entry of measure.entries) {
|
|
6195
|
+
if (entry.type === "note") {
|
|
6196
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6197
|
+
const context = {
|
|
6198
|
+
note: entry,
|
|
6199
|
+
part,
|
|
6200
|
+
partIndex,
|
|
6201
|
+
measure,
|
|
6202
|
+
measureIndex,
|
|
6203
|
+
position
|
|
6204
|
+
};
|
|
6205
|
+
if (entry.notations) {
|
|
6206
|
+
for (const notation of entry.notations) {
|
|
6207
|
+
if (notation.type === "tuplet") {
|
|
6208
|
+
const tupletNumber = notation.number ?? 1;
|
|
6209
|
+
if (notation.tupletType === "start") {
|
|
6210
|
+
const actualNotes = entry.timeModification?.actualNotes ?? 3;
|
|
6211
|
+
const normalNotes = entry.timeModification?.normalNotes ?? 2;
|
|
6212
|
+
pendingTuplets.set(tupletNumber, {
|
|
6213
|
+
notes: [context],
|
|
6214
|
+
actualNotes,
|
|
6215
|
+
normalNotes
|
|
6216
|
+
});
|
|
6217
|
+
} else if (notation.tupletType === "stop" && pendingTuplets.has(tupletNumber)) {
|
|
6218
|
+
const pending = pendingTuplets.get(tupletNumber);
|
|
6219
|
+
pending.notes.push(context);
|
|
6220
|
+
results.push({
|
|
6221
|
+
number: tupletNumber,
|
|
6222
|
+
notes: pending.notes,
|
|
6223
|
+
actualNotes: pending.actualNotes,
|
|
6224
|
+
normalNotes: pending.normalNotes
|
|
6225
|
+
});
|
|
6226
|
+
pendingTuplets.delete(tupletNumber);
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6231
|
+
if (entry.timeModification && !entry.notations?.some((n) => n.type === "tuplet")) {
|
|
6232
|
+
for (const [, pending] of pendingTuplets) {
|
|
6233
|
+
if (entry.timeModification.actualNotes === pending.actualNotes && entry.timeModification.normalNotes === pending.normalNotes) {
|
|
6234
|
+
pending.notes.push(context);
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
updatePositionForEntry(state, entry);
|
|
6240
|
+
}
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
return results;
|
|
6244
|
+
}
|
|
6245
|
+
function getBeamGroups(measure) {
|
|
6246
|
+
const results = [];
|
|
6247
|
+
const state = createPositionState();
|
|
6248
|
+
const pendingBeams = /* @__PURE__ */ new Map();
|
|
6249
|
+
for (const entry of measure.entries) {
|
|
6250
|
+
if (entry.type === "note" && entry.beam) {
|
|
6251
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6252
|
+
const context = {
|
|
6253
|
+
note: entry,
|
|
6254
|
+
part: {},
|
|
6255
|
+
// Will be set by caller if needed
|
|
6256
|
+
partIndex: 0,
|
|
6257
|
+
measure,
|
|
6258
|
+
measureIndex: 0,
|
|
6259
|
+
position
|
|
6260
|
+
};
|
|
6261
|
+
for (const beam of entry.beam) {
|
|
6262
|
+
const beamNumber = beam.number;
|
|
6263
|
+
if (beam.type === "begin") {
|
|
6264
|
+
pendingBeams.set(beamNumber, [context]);
|
|
6265
|
+
} else if (beam.type === "continue" && pendingBeams.has(beamNumber)) {
|
|
6266
|
+
pendingBeams.get(beamNumber).push(context);
|
|
6267
|
+
} else if (beam.type === "end" && pendingBeams.has(beamNumber)) {
|
|
6268
|
+
const group = pendingBeams.get(beamNumber);
|
|
6269
|
+
group.push(context);
|
|
6270
|
+
results.push({
|
|
6271
|
+
notes: group,
|
|
6272
|
+
beamLevel: beamNumber
|
|
6273
|
+
});
|
|
6274
|
+
pendingBeams.delete(beamNumber);
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
updatePositionForEntry(state, entry);
|
|
6279
|
+
}
|
|
6280
|
+
return results;
|
|
6281
|
+
}
|
|
6282
|
+
function findNotesWithNotation(score, notationType, options) {
|
|
6283
|
+
const results = [];
|
|
6284
|
+
const startPart = options?.partIndex ?? 0;
|
|
6285
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6286
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6287
|
+
const part = score.parts[partIndex];
|
|
6288
|
+
if (!part) continue;
|
|
6289
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6290
|
+
const measure = part.measures[measureIndex];
|
|
6291
|
+
const state = createPositionState();
|
|
6292
|
+
for (const entry of measure.entries) {
|
|
6293
|
+
if (entry.type === "note") {
|
|
6294
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6295
|
+
if (entry.notations?.some((n) => n.type === notationType)) {
|
|
6296
|
+
results.push({
|
|
6297
|
+
note: entry,
|
|
6298
|
+
part,
|
|
6299
|
+
partIndex,
|
|
6300
|
+
measure,
|
|
6301
|
+
measureIndex,
|
|
6302
|
+
position
|
|
6303
|
+
});
|
|
6304
|
+
}
|
|
6305
|
+
}
|
|
6306
|
+
updatePositionForEntry(state, entry);
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
return results;
|
|
6311
|
+
}
|
|
6312
|
+
function getHarmonies(score, options) {
|
|
6313
|
+
const results = [];
|
|
6314
|
+
const startPart = options?.partIndex ?? 0;
|
|
6315
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6316
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6317
|
+
const part = score.parts[partIndex];
|
|
6318
|
+
if (!part) continue;
|
|
6319
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6320
|
+
const measure = part.measures[measureIndex];
|
|
6321
|
+
const state = createPositionState();
|
|
6322
|
+
for (const entry of measure.entries) {
|
|
6323
|
+
if (entry.type === "harmony") {
|
|
6324
|
+
results.push({
|
|
6325
|
+
harmony: entry,
|
|
6326
|
+
part,
|
|
6327
|
+
partIndex,
|
|
6328
|
+
measure,
|
|
6329
|
+
measureIndex,
|
|
6330
|
+
position: state.position
|
|
6331
|
+
});
|
|
6332
|
+
}
|
|
6333
|
+
updatePositionForEntry(state, entry);
|
|
6334
|
+
}
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
return results;
|
|
6338
|
+
}
|
|
6339
|
+
function getHarmonyAtPosition(measure, position) {
|
|
6340
|
+
const state = createPositionState();
|
|
6341
|
+
let lastHarmony;
|
|
6342
|
+
for (const entry of measure.entries) {
|
|
6343
|
+
if (entry.type === "harmony") {
|
|
6344
|
+
if (state.position <= position) {
|
|
6345
|
+
lastHarmony = entry;
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
updatePositionForEntry(state, entry);
|
|
6349
|
+
if (state.position > position && lastHarmony) {
|
|
6350
|
+
break;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
return lastHarmony;
|
|
6354
|
+
}
|
|
6355
|
+
function getChordProgression(score, options) {
|
|
6356
|
+
const harmonies = getHarmonies(score, options);
|
|
6357
|
+
return harmonies.map((h) => {
|
|
6358
|
+
const rootAlter = h.harmony.root.rootAlter ?? 0;
|
|
6359
|
+
const rootAccidental = rootAlter > 0 ? "#".repeat(rootAlter) : "b".repeat(-rootAlter);
|
|
6360
|
+
const root = h.harmony.root.rootStep + rootAccidental;
|
|
6361
|
+
let bass;
|
|
6362
|
+
if (h.harmony.bass) {
|
|
6363
|
+
const bassAlter = h.harmony.bass.bassAlter ?? 0;
|
|
6364
|
+
const bassAccidental = bassAlter > 0 ? "#".repeat(bassAlter) : "b".repeat(-bassAlter);
|
|
6365
|
+
bass = h.harmony.bass.bassStep + bassAccidental;
|
|
6366
|
+
}
|
|
6367
|
+
return {
|
|
6368
|
+
root,
|
|
6369
|
+
kind: h.harmony.kind,
|
|
6370
|
+
bass,
|
|
6371
|
+
measureIndex: h.measureIndex,
|
|
6372
|
+
position: h.position
|
|
6373
|
+
};
|
|
6374
|
+
});
|
|
6375
|
+
}
|
|
6376
|
+
function getLyrics(score, options) {
|
|
6377
|
+
const results = [];
|
|
6378
|
+
const startPart = options?.partIndex ?? 0;
|
|
6379
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6380
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6381
|
+
const part = score.parts[partIndex];
|
|
6382
|
+
if (!part) continue;
|
|
6383
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6384
|
+
const measure = part.measures[measureIndex];
|
|
6385
|
+
const state = createPositionState();
|
|
6386
|
+
for (const entry of measure.entries) {
|
|
6387
|
+
if (entry.type === "note" && entry.lyrics) {
|
|
6388
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
6389
|
+
for (const lyric of entry.lyrics) {
|
|
6390
|
+
const verse = lyric.number ?? 1;
|
|
6391
|
+
if (options?.verse !== void 0 && verse !== options.verse) {
|
|
6392
|
+
continue;
|
|
6393
|
+
}
|
|
6394
|
+
results.push({
|
|
6395
|
+
lyric,
|
|
6396
|
+
note: entry,
|
|
6397
|
+
part,
|
|
6398
|
+
partIndex,
|
|
6399
|
+
measure,
|
|
6400
|
+
measureIndex,
|
|
6401
|
+
position,
|
|
6402
|
+
verse
|
|
6403
|
+
});
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
updatePositionForEntry(state, entry);
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
return results;
|
|
6411
|
+
}
|
|
6412
|
+
function getLyricText(score, options) {
|
|
6413
|
+
const lyrics = getLyrics(score, options);
|
|
6414
|
+
const verseMap = /* @__PURE__ */ new Map();
|
|
6415
|
+
for (const lyric of lyrics) {
|
|
6416
|
+
if (!verseMap.has(lyric.verse)) {
|
|
6417
|
+
verseMap.set(lyric.verse, []);
|
|
6418
|
+
}
|
|
6419
|
+
verseMap.get(lyric.verse).push(lyric);
|
|
6420
|
+
}
|
|
6421
|
+
const results = [];
|
|
6422
|
+
for (const [verse, verseLyrics] of verseMap) {
|
|
6423
|
+
const syllables = [];
|
|
6424
|
+
const textParts = [];
|
|
6425
|
+
for (const lyric of verseLyrics) {
|
|
6426
|
+
const text = lyric.lyric.text;
|
|
6427
|
+
syllables.push({
|
|
6428
|
+
text,
|
|
6429
|
+
position: lyric.position,
|
|
6430
|
+
measureIndex: lyric.measureIndex
|
|
6431
|
+
});
|
|
6432
|
+
const syllabic = lyric.lyric.syllabic;
|
|
6433
|
+
if (syllabic === "begin" || syllabic === "middle") {
|
|
6434
|
+
textParts.push(text + "-");
|
|
6435
|
+
} else if (syllabic === "end") {
|
|
6436
|
+
textParts.push(text + " ");
|
|
6437
|
+
} else {
|
|
6438
|
+
textParts.push(text + " ");
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
results.push({
|
|
6442
|
+
verse,
|
|
6443
|
+
text: textParts.join("").trim(),
|
|
6444
|
+
syllables
|
|
6445
|
+
});
|
|
6446
|
+
}
|
|
6447
|
+
return results.sort((a, b) => a.verse - b.verse);
|
|
6448
|
+
}
|
|
6449
|
+
function getVerseCount(score, options) {
|
|
6450
|
+
const verses = /* @__PURE__ */ new Set();
|
|
6451
|
+
const startPart = options?.partIndex ?? 0;
|
|
6452
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6453
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6454
|
+
const part = score.parts[partIndex];
|
|
6455
|
+
if (!part) continue;
|
|
6456
|
+
for (const measure of part.measures) {
|
|
6457
|
+
for (const entry of measure.entries) {
|
|
6458
|
+
if (entry.type === "note" && entry.lyrics) {
|
|
6459
|
+
for (const lyric of entry.lyrics) {
|
|
6460
|
+
verses.add(lyric.number ?? 1);
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
return verses.size;
|
|
6467
|
+
}
|
|
6468
|
+
function getRepeatStructure(score, options) {
|
|
6469
|
+
const results = [];
|
|
6470
|
+
const partIndex = options?.partIndex ?? 0;
|
|
6471
|
+
const part = score.parts[partIndex];
|
|
6472
|
+
if (!part) return results;
|
|
6473
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6474
|
+
const measure = part.measures[measureIndex];
|
|
6475
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6476
|
+
if (measure.barlines) {
|
|
6477
|
+
for (const barline of measure.barlines) {
|
|
6478
|
+
if (barline.repeat) {
|
|
6479
|
+
results.push({
|
|
6480
|
+
type: barline.repeat.direction,
|
|
6481
|
+
times: barline.repeat.times,
|
|
6482
|
+
measureIndex,
|
|
6483
|
+
measureNumber
|
|
6484
|
+
});
|
|
6485
|
+
}
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
return results;
|
|
6490
|
+
}
|
|
6491
|
+
function findBarlines(score, options) {
|
|
6492
|
+
const results = [];
|
|
6493
|
+
const startPart = options?.partIndex ?? 0;
|
|
6494
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6495
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6496
|
+
const part = score.parts[partIndex];
|
|
6497
|
+
if (!part) continue;
|
|
6498
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6499
|
+
const measure = part.measures[measureIndex];
|
|
6500
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6501
|
+
if (measure.barlines) {
|
|
6502
|
+
for (const barline of measure.barlines) {
|
|
6503
|
+
if (options?.style !== void 0 && barline.barStyle !== options.style) {
|
|
6504
|
+
continue;
|
|
6505
|
+
}
|
|
6506
|
+
if (options?.repeat !== void 0) {
|
|
6507
|
+
const hasRepeat = barline.repeat !== void 0;
|
|
6508
|
+
if (hasRepeat !== options.repeat) {
|
|
6509
|
+
continue;
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
results.push({
|
|
6513
|
+
barline,
|
|
6514
|
+
partIndex,
|
|
6515
|
+
measureIndex,
|
|
6516
|
+
measureNumber
|
|
6517
|
+
});
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
6522
|
+
return results;
|
|
6523
|
+
}
|
|
6524
|
+
function getEndings(score, options) {
|
|
6525
|
+
const results = [];
|
|
6526
|
+
const startPart = options?.partIndex ?? 0;
|
|
6527
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6528
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6529
|
+
const part = score.parts[partIndex];
|
|
6530
|
+
if (!part) continue;
|
|
6531
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6532
|
+
const measure = part.measures[measureIndex];
|
|
6533
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6534
|
+
if (measure.barlines) {
|
|
6535
|
+
for (const barline of measure.barlines) {
|
|
6536
|
+
if (barline.ending) {
|
|
6537
|
+
results.push({
|
|
6538
|
+
number: barline.ending.number,
|
|
6539
|
+
type: barline.ending.type,
|
|
6540
|
+
partIndex,
|
|
6541
|
+
measureIndex,
|
|
6542
|
+
measureNumber
|
|
6543
|
+
});
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
return results;
|
|
6550
|
+
}
|
|
6551
|
+
function getKeyChanges(score, options) {
|
|
6552
|
+
const results = [];
|
|
6553
|
+
const startPart = options?.partIndex ?? 0;
|
|
6554
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6555
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6556
|
+
const part = score.parts[partIndex];
|
|
6557
|
+
if (!part) continue;
|
|
6558
|
+
let lastKey;
|
|
6559
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6560
|
+
const measure = part.measures[measureIndex];
|
|
6561
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6562
|
+
const state = createPositionState();
|
|
6563
|
+
if (measure.attributes?.key) {
|
|
6564
|
+
const key = measure.attributes.key;
|
|
6565
|
+
if (!lastKey || lastKey.fifths !== key.fifths || lastKey.mode !== key.mode) {
|
|
6566
|
+
results.push({
|
|
6567
|
+
key,
|
|
6568
|
+
partIndex,
|
|
6569
|
+
measureIndex,
|
|
6570
|
+
measureNumber,
|
|
6571
|
+
position: 0
|
|
6572
|
+
});
|
|
6573
|
+
lastKey = key;
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
for (const entry of measure.entries) {
|
|
6577
|
+
if (entry.type === "attributes" && entry.attributes.key) {
|
|
6578
|
+
const key = entry.attributes.key;
|
|
6579
|
+
if (!lastKey || lastKey.fifths !== key.fifths || lastKey.mode !== key.mode) {
|
|
6580
|
+
results.push({
|
|
6581
|
+
key,
|
|
6582
|
+
partIndex,
|
|
6583
|
+
measureIndex,
|
|
6584
|
+
measureNumber,
|
|
6585
|
+
position: state.position
|
|
6586
|
+
});
|
|
6587
|
+
lastKey = key;
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
updatePositionForEntry(state, entry);
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
6594
|
+
return results;
|
|
6595
|
+
}
|
|
6596
|
+
function getTimeChanges(score, options) {
|
|
6597
|
+
const results = [];
|
|
6598
|
+
const startPart = options?.partIndex ?? 0;
|
|
6599
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6600
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6601
|
+
const part = score.parts[partIndex];
|
|
6602
|
+
if (!part) continue;
|
|
6603
|
+
let lastTime;
|
|
6604
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6605
|
+
const measure = part.measures[measureIndex];
|
|
6606
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6607
|
+
if (measure.attributes?.time) {
|
|
6608
|
+
const time = measure.attributes.time;
|
|
6609
|
+
if (!lastTime || lastTime.beats !== time.beats || lastTime.beatType !== time.beatType) {
|
|
6610
|
+
results.push({
|
|
6611
|
+
time,
|
|
6612
|
+
partIndex,
|
|
6613
|
+
measureIndex,
|
|
6614
|
+
measureNumber
|
|
6615
|
+
});
|
|
6616
|
+
lastTime = time;
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
for (const entry of measure.entries) {
|
|
6620
|
+
if (entry.type === "attributes" && entry.attributes.time) {
|
|
6621
|
+
const time = entry.attributes.time;
|
|
6622
|
+
if (!lastTime || lastTime.beats !== time.beats || lastTime.beatType !== time.beatType) {
|
|
6623
|
+
results.push({
|
|
6624
|
+
time,
|
|
6625
|
+
partIndex,
|
|
6626
|
+
measureIndex,
|
|
6627
|
+
measureNumber
|
|
6628
|
+
});
|
|
6629
|
+
lastTime = time;
|
|
6630
|
+
}
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
return results;
|
|
6636
|
+
}
|
|
6637
|
+
function getClefChanges(score, options) {
|
|
6638
|
+
const results = [];
|
|
6639
|
+
const startPart = options?.partIndex ?? 0;
|
|
6640
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
6641
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
6642
|
+
const part = score.parts[partIndex];
|
|
6643
|
+
if (!part) continue;
|
|
6644
|
+
const lastClefs = /* @__PURE__ */ new Map();
|
|
6645
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
6646
|
+
const measure = part.measures[measureIndex];
|
|
6647
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
6648
|
+
const state = createPositionState();
|
|
6649
|
+
if (measure.attributes?.clef) {
|
|
6650
|
+
for (const clef of measure.attributes.clef) {
|
|
6651
|
+
const staff = clef.staff ?? 1;
|
|
6652
|
+
if (options?.staff !== void 0 && staff !== options.staff) {
|
|
6653
|
+
continue;
|
|
6654
|
+
}
|
|
6655
|
+
const lastClef = lastClefs.get(staff);
|
|
6656
|
+
if (!lastClef || lastClef.sign !== clef.sign || lastClef.line !== clef.line || lastClef.clefOctaveChange !== clef.clefOctaveChange) {
|
|
6657
|
+
results.push({
|
|
6658
|
+
clef,
|
|
6659
|
+
staff,
|
|
6660
|
+
partIndex,
|
|
6661
|
+
measureIndex,
|
|
6662
|
+
measureNumber,
|
|
6663
|
+
position: 0
|
|
6664
|
+
});
|
|
6665
|
+
lastClefs.set(staff, clef);
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
for (const entry of measure.entries) {
|
|
6670
|
+
if (entry.type === "attributes" && entry.attributes.clef) {
|
|
6671
|
+
for (const clef of entry.attributes.clef) {
|
|
6672
|
+
const staff = clef.staff ?? 1;
|
|
6673
|
+
if (options?.staff !== void 0 && staff !== options.staff) {
|
|
6674
|
+
continue;
|
|
6675
|
+
}
|
|
6676
|
+
const lastClef = lastClefs.get(staff);
|
|
6677
|
+
if (!lastClef || lastClef.sign !== clef.sign || lastClef.line !== clef.line || lastClef.clefOctaveChange !== clef.clefOctaveChange) {
|
|
6678
|
+
results.push({
|
|
6679
|
+
clef,
|
|
6680
|
+
staff,
|
|
6681
|
+
partIndex,
|
|
6682
|
+
measureIndex,
|
|
6683
|
+
measureNumber,
|
|
6684
|
+
position: state.position
|
|
6685
|
+
});
|
|
6686
|
+
lastClefs.set(staff, clef);
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
}
|
|
6690
|
+
updatePositionForEntry(state, entry);
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
}
|
|
6694
|
+
return results;
|
|
6695
|
+
}
|
|
6696
|
+
function getStructuralChanges(score, options) {
|
|
6697
|
+
return {
|
|
6698
|
+
keyChanges: getKeyChanges(score, options),
|
|
6699
|
+
timeChanges: getTimeChanges(score, options),
|
|
6700
|
+
clefChanges: getClefChanges(score, options)
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
function getPartByIndex(score, index) {
|
|
6704
|
+
return score.parts[index];
|
|
6705
|
+
}
|
|
6706
|
+
function getPartCount(score) {
|
|
6707
|
+
return score.parts.length;
|
|
6708
|
+
}
|
|
6709
|
+
function getPartIds(score) {
|
|
6710
|
+
return score.parts.map((part) => part.id);
|
|
6711
|
+
}
|
|
6712
|
+
|
|
6713
|
+
// src/query/index.ts
|
|
6714
|
+
function getMeasure(score, options) {
|
|
6715
|
+
const part = score.parts[options.part];
|
|
6716
|
+
if (!part) return void 0;
|
|
6717
|
+
const targetMeasure = String(options.measure);
|
|
6718
|
+
return part.measures.find((m) => m.number === targetMeasure);
|
|
6719
|
+
}
|
|
6720
|
+
function getMeasureByIndex(score, options) {
|
|
6721
|
+
const part = score.parts[options.part];
|
|
6722
|
+
if (!part) return void 0;
|
|
6723
|
+
return part.measures[options.measureIndex];
|
|
6724
|
+
}
|
|
6725
|
+
function getMeasureCount(score) {
|
|
6726
|
+
if (score.parts.length === 0) return 0;
|
|
6727
|
+
return score.parts[0].measures.length;
|
|
6728
|
+
}
|
|
6729
|
+
function getDivisions(score, options) {
|
|
6730
|
+
const part = score.parts[options.part];
|
|
6731
|
+
if (!part) return 1;
|
|
6732
|
+
const targetMeasure = parseInt(String(options.measure), 10);
|
|
6733
|
+
if (isNaN(targetMeasure)) return 1;
|
|
6734
|
+
for (let i = 0; i < part.measures.length; i++) {
|
|
6735
|
+
const m = part.measures[i];
|
|
6736
|
+
const mNum = parseInt(m.number, 10);
|
|
6737
|
+
if (!isNaN(mNum) && mNum > targetMeasure) break;
|
|
6738
|
+
if (m.attributes?.divisions !== void 0) {
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
let divisions = 1;
|
|
6742
|
+
for (const m of part.measures) {
|
|
6743
|
+
const mNum = parseInt(m.number, 10);
|
|
6744
|
+
if (!isNaN(mNum) && mNum > targetMeasure) break;
|
|
6745
|
+
if (m.attributes?.divisions !== void 0) {
|
|
6746
|
+
divisions = m.attributes.divisions;
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
return divisions;
|
|
6750
|
+
}
|
|
6751
|
+
function getAttributesAtMeasure(score, options) {
|
|
6752
|
+
const part = score.parts[options.part];
|
|
6753
|
+
if (!part) return {};
|
|
6754
|
+
const targetMeasure = parseInt(String(options.measure), 10);
|
|
6755
|
+
const result = {};
|
|
6756
|
+
for (const m of part.measures) {
|
|
6757
|
+
const mNum = parseInt(m.number, 10);
|
|
6758
|
+
if (!isNaN(targetMeasure) && !isNaN(mNum) && mNum > targetMeasure) break;
|
|
6759
|
+
if (m.attributes) {
|
|
6760
|
+
if (m.attributes.divisions !== void 0) result.divisions = m.attributes.divisions;
|
|
6761
|
+
if (m.attributes.time !== void 0) result.time = m.attributes.time;
|
|
6762
|
+
if (m.attributes.key !== void 0) result.key = m.attributes.key;
|
|
6763
|
+
if (m.attributes.clef !== void 0) result.clef = m.attributes.clef;
|
|
6764
|
+
if (m.attributes.staves !== void 0) result.staves = m.attributes.staves;
|
|
6765
|
+
if (m.attributes.transpose !== void 0) result.transpose = m.attributes.transpose;
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
return result;
|
|
6769
|
+
}
|
|
6770
|
+
function findNotes(score, filter) {
|
|
6771
|
+
const results = [];
|
|
6772
|
+
for (const part of score.parts) {
|
|
6773
|
+
for (const measure of part.measures) {
|
|
6774
|
+
for (const entry of measure.entries) {
|
|
6775
|
+
if (entry.type !== "note") continue;
|
|
6776
|
+
if (filter.pitchRange && entry.pitch) {
|
|
6777
|
+
const noteValue = pitchToSemitone(entry.pitch);
|
|
6778
|
+
if (filter.pitchRange.min) {
|
|
6779
|
+
const minValue = pitchToSemitone(filter.pitchRange.min);
|
|
6780
|
+
if (noteValue < minValue) continue;
|
|
6781
|
+
}
|
|
6782
|
+
if (filter.pitchRange.max) {
|
|
6783
|
+
const maxValue = pitchToSemitone(filter.pitchRange.max);
|
|
6784
|
+
if (noteValue > maxValue) continue;
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
if (filter.voice !== void 0 && entry.voice !== filter.voice) continue;
|
|
6788
|
+
if (filter.staff !== void 0 && (entry.staff ?? 1) !== filter.staff) continue;
|
|
6789
|
+
if (filter.noteType !== void 0 && entry.noteType !== filter.noteType) continue;
|
|
6790
|
+
if (filter.hasTie !== void 0) {
|
|
6791
|
+
const hasTie = entry.tie !== void 0;
|
|
6792
|
+
if (filter.hasTie !== hasTie) continue;
|
|
6793
|
+
}
|
|
6794
|
+
results.push(entry);
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
return results;
|
|
6799
|
+
}
|
|
6800
|
+
function getDuration(score) {
|
|
6801
|
+
if (score.parts.length === 0) return 0;
|
|
6802
|
+
const part = score.parts[0];
|
|
6803
|
+
let totalDuration = 0;
|
|
6804
|
+
let divisions = 1;
|
|
6805
|
+
for (const measure of part.measures) {
|
|
6806
|
+
if (measure.attributes?.divisions !== void 0) {
|
|
6807
|
+
divisions = measure.attributes.divisions;
|
|
6808
|
+
}
|
|
6809
|
+
let measureDuration = 0;
|
|
6810
|
+
for (const entry of measure.entries) {
|
|
6811
|
+
if (entry.type === "note" && !entry.chord) {
|
|
6812
|
+
measureDuration = Math.max(measureDuration, entry.duration);
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
const attrs = getAttributesAtMeasure(score, { part: 0, measure: measure.number });
|
|
6816
|
+
if (attrs.time) {
|
|
6817
|
+
const beats = parseInt(attrs.time.beats, 10) || 4;
|
|
6818
|
+
const expectedDuration = beats / attrs.time.beatType * 4 * divisions;
|
|
6819
|
+
measureDuration = Math.max(measureDuration, expectedDuration);
|
|
6820
|
+
}
|
|
6821
|
+
totalDuration += measureDuration;
|
|
6822
|
+
}
|
|
6823
|
+
return totalDuration;
|
|
6824
|
+
}
|
|
6825
|
+
function getPartById(score, id) {
|
|
6826
|
+
return score.parts.find((p) => p.id === id);
|
|
6827
|
+
}
|
|
6828
|
+
function getPartIndex(score, id) {
|
|
6829
|
+
return score.parts.findIndex((p) => p.id === id);
|
|
6830
|
+
}
|
|
6831
|
+
function hasMultipleStaves(score, partIndex = 0) {
|
|
6832
|
+
const part = score.parts[partIndex];
|
|
6833
|
+
if (!part) return false;
|
|
6834
|
+
for (const measure of part.measures) {
|
|
6835
|
+
if (measure.attributes?.staves !== void 0 && measure.attributes.staves > 1) {
|
|
6836
|
+
return true;
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
return false;
|
|
6840
|
+
}
|
|
6841
|
+
function getStaveCount(score, partIndex = 0) {
|
|
6842
|
+
const part = score.parts[partIndex];
|
|
6843
|
+
if (!part) return 1;
|
|
6844
|
+
for (const measure of part.measures) {
|
|
6845
|
+
if (measure.attributes?.staves !== void 0) {
|
|
6846
|
+
return measure.attributes.staves;
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
return 1;
|
|
6850
|
+
}
|
|
6851
|
+
function measureRoundtrip(original, exported) {
|
|
6852
|
+
const notesOriginal = countNotes(original);
|
|
6853
|
+
const notesExported = countNotes(exported);
|
|
6854
|
+
const notesPreserved = Math.min(notesOriginal, notesExported);
|
|
6855
|
+
const measuresOriginal = getMeasureCount(original);
|
|
6856
|
+
const measuresExported = getMeasureCount(exported);
|
|
6857
|
+
const measuresPreserved = Math.min(measuresOriginal, measuresExported);
|
|
6858
|
+
const partsOriginal = original.parts.length;
|
|
6859
|
+
const partsExported = exported.parts.length;
|
|
6860
|
+
const partsPreserved = Math.min(partsOriginal, partsExported);
|
|
6861
|
+
const preservationRate = notesOriginal > 0 ? notesPreserved / notesOriginal : 1;
|
|
6862
|
+
return {
|
|
6863
|
+
notesOriginal,
|
|
6864
|
+
notesPreserved,
|
|
6865
|
+
measuresOriginal,
|
|
6866
|
+
measuresPreserved,
|
|
6867
|
+
partsOriginal,
|
|
6868
|
+
partsPreserved,
|
|
6869
|
+
preservationRate
|
|
6870
|
+
};
|
|
6871
|
+
}
|
|
6872
|
+
function countNotes(score) {
|
|
6873
|
+
let count = 0;
|
|
6874
|
+
for (const part of score.parts) {
|
|
6875
|
+
for (const measure of part.measures) {
|
|
6876
|
+
for (const entry of measure.entries) {
|
|
6877
|
+
if (entry.type === "note") {
|
|
6878
|
+
count++;
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
}
|
|
6882
|
+
}
|
|
6883
|
+
return count;
|
|
5530
6884
|
}
|
|
5531
6885
|
function scoresEqual(a, b) {
|
|
5532
6886
|
if (a.parts.length !== b.parts.length) return false;
|
|
@@ -5559,9 +6913,461 @@ function pitchesEqual(a, b) {
|
|
|
5559
6913
|
}
|
|
5560
6914
|
|
|
5561
6915
|
// src/operations/index.ts
|
|
6916
|
+
function success(data, warnings) {
|
|
6917
|
+
return { success: true, data, warnings };
|
|
6918
|
+
}
|
|
6919
|
+
function failure(errors) {
|
|
6920
|
+
return { success: false, errors };
|
|
6921
|
+
}
|
|
6922
|
+
function operationError(code, message, location = {}, details) {
|
|
6923
|
+
return {
|
|
6924
|
+
code,
|
|
6925
|
+
level: "error",
|
|
6926
|
+
message,
|
|
6927
|
+
location,
|
|
6928
|
+
details
|
|
6929
|
+
};
|
|
6930
|
+
}
|
|
5562
6931
|
function cloneScore(score) {
|
|
5563
6932
|
return JSON.parse(JSON.stringify(score));
|
|
5564
6933
|
}
|
|
6934
|
+
function getMeasureDuration(divisions, time) {
|
|
6935
|
+
const beats = parseInt(time.beats, 10);
|
|
6936
|
+
if (isNaN(beats)) return divisions * 4;
|
|
6937
|
+
return beats / time.beatType * 4 * divisions;
|
|
6938
|
+
}
|
|
6939
|
+
function getVoiceEntries(measure, voice, staff) {
|
|
6940
|
+
const result = [];
|
|
6941
|
+
let position = 0;
|
|
6942
|
+
for (let i = 0; i < measure.entries.length; i++) {
|
|
6943
|
+
const entry = measure.entries[i];
|
|
6944
|
+
if (entry.type === "note") {
|
|
6945
|
+
const noteStaff = entry.staff ?? 1;
|
|
6946
|
+
if (entry.voice === voice && (staff === void 0 || noteStaff === staff)) {
|
|
6947
|
+
if (!entry.chord) {
|
|
6948
|
+
result.push({
|
|
6949
|
+
entry,
|
|
6950
|
+
entryIndex: i,
|
|
6951
|
+
position,
|
|
6952
|
+
endPosition: position + entry.duration
|
|
6953
|
+
});
|
|
6954
|
+
position += entry.duration;
|
|
6955
|
+
} else {
|
|
6956
|
+
if (result.length > 0) {
|
|
6957
|
+
const prev = result[result.length - 1];
|
|
6958
|
+
result.push({
|
|
6959
|
+
entry,
|
|
6960
|
+
entryIndex: i,
|
|
6961
|
+
position: prev.position,
|
|
6962
|
+
endPosition: prev.endPosition
|
|
6963
|
+
});
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
} else if (!entry.chord) {
|
|
6967
|
+
position += entry.duration;
|
|
6968
|
+
}
|
|
6969
|
+
} else if (entry.type === "backup") {
|
|
6970
|
+
position -= entry.duration;
|
|
6971
|
+
} else if (entry.type === "forward") {
|
|
6972
|
+
if (entry.voice === voice) {
|
|
6973
|
+
result.push({
|
|
6974
|
+
entry,
|
|
6975
|
+
entryIndex: i,
|
|
6976
|
+
position,
|
|
6977
|
+
endPosition: position + entry.duration
|
|
6978
|
+
});
|
|
6979
|
+
}
|
|
6980
|
+
position += entry.duration;
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
return result;
|
|
6984
|
+
}
|
|
6985
|
+
function hasNotesInRange(voiceEntries, startPos, endPos) {
|
|
6986
|
+
const conflicting = voiceEntries.filter((e) => {
|
|
6987
|
+
if (e.entry.type !== "note") return false;
|
|
6988
|
+
const note = e.entry;
|
|
6989
|
+
if (note.rest) return false;
|
|
6990
|
+
return e.position < endPos && e.endPosition > startPos;
|
|
6991
|
+
});
|
|
6992
|
+
return { hasNotes: conflicting.length > 0, conflictingNotes: conflicting };
|
|
6993
|
+
}
|
|
6994
|
+
function createRest(duration, voice, staff) {
|
|
6995
|
+
return {
|
|
6996
|
+
type: "note",
|
|
6997
|
+
rest: { displayStep: void 0, displayOctave: void 0 },
|
|
6998
|
+
duration,
|
|
6999
|
+
voice,
|
|
7000
|
+
staff
|
|
7001
|
+
};
|
|
7002
|
+
}
|
|
7003
|
+
function rebuildMeasureWithVoice(measure, voice, newEntries, measureDuration, staff) {
|
|
7004
|
+
const otherEntries = [];
|
|
7005
|
+
let position = 0;
|
|
7006
|
+
for (const entry of measure.entries) {
|
|
7007
|
+
if (entry.type === "note") {
|
|
7008
|
+
if (entry.voice !== voice || staff !== void 0 && (entry.staff ?? 1) !== staff) {
|
|
7009
|
+
if (!entry.chord) {
|
|
7010
|
+
otherEntries.push({ position, entry });
|
|
7011
|
+
position += entry.duration;
|
|
7012
|
+
} else {
|
|
7013
|
+
otherEntries.push({ position, entry });
|
|
7014
|
+
}
|
|
7015
|
+
} else if (!entry.chord) {
|
|
7016
|
+
position += entry.duration;
|
|
7017
|
+
}
|
|
7018
|
+
} else if (entry.type === "backup") {
|
|
7019
|
+
position -= entry.duration;
|
|
7020
|
+
} else if (entry.type === "forward") {
|
|
7021
|
+
if (entry.voice !== voice) {
|
|
7022
|
+
otherEntries.push({ position, entry });
|
|
7023
|
+
}
|
|
7024
|
+
position += entry.duration;
|
|
7025
|
+
} else {
|
|
7026
|
+
otherEntries.push({ position, entry });
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
7029
|
+
const filledNewEntries = [];
|
|
7030
|
+
let currentPos = 0;
|
|
7031
|
+
const sortedNew = [...newEntries].sort((a, b) => a.position - b.position);
|
|
7032
|
+
for (const { position: notePos, entry } of sortedNew) {
|
|
7033
|
+
if (notePos > currentPos) {
|
|
7034
|
+
filledNewEntries.push({
|
|
7035
|
+
position: currentPos,
|
|
7036
|
+
entry: createRest(notePos - currentPos, voice, staff)
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
7039
|
+
filledNewEntries.push({ position: notePos, entry });
|
|
7040
|
+
if (!entry.chord) {
|
|
7041
|
+
currentPos = notePos + entry.duration;
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
if (currentPos < measureDuration) {
|
|
7045
|
+
filledNewEntries.push({
|
|
7046
|
+
position: currentPos,
|
|
7047
|
+
entry: createRest(measureDuration - currentPos, voice, staff)
|
|
7048
|
+
});
|
|
7049
|
+
}
|
|
7050
|
+
const allEntries = [...otherEntries, ...filledNewEntries];
|
|
7051
|
+
allEntries.sort((a, b) => a.position - b.position);
|
|
7052
|
+
const result = [];
|
|
7053
|
+
let currentPosition = 0;
|
|
7054
|
+
for (const { position: targetPos, entry } of allEntries) {
|
|
7055
|
+
const diff = targetPos - currentPosition;
|
|
7056
|
+
if (diff < 0) {
|
|
7057
|
+
result.push({ type: "backup", duration: -diff });
|
|
7058
|
+
currentPosition = targetPos;
|
|
7059
|
+
} else if (diff > 0) {
|
|
7060
|
+
result.push({
|
|
7061
|
+
type: "forward",
|
|
7062
|
+
duration: diff,
|
|
7063
|
+
voice: entry.type === "note" ? entry.voice : 1,
|
|
7064
|
+
staff: entry.type === "note" ? entry.staff : void 0
|
|
7065
|
+
});
|
|
7066
|
+
currentPosition = targetPos;
|
|
7067
|
+
}
|
|
7068
|
+
result.push(entry);
|
|
7069
|
+
if (entry.type === "note" && !entry.chord) {
|
|
7070
|
+
currentPosition += entry.duration;
|
|
7071
|
+
} else if (entry.type === "forward") {
|
|
7072
|
+
currentPosition += entry.duration;
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
return result;
|
|
7076
|
+
}
|
|
7077
|
+
function insertNote(score, options) {
|
|
7078
|
+
if (options.partIndex < 0 || options.partIndex >= score.parts.length) {
|
|
7079
|
+
return failure([operationError("PART_NOT_FOUND", `Part index ${options.partIndex} out of bounds`, { partIndex: options.partIndex })]);
|
|
7080
|
+
}
|
|
7081
|
+
const part = score.parts[options.partIndex];
|
|
7082
|
+
if (options.measureIndex < 0 || options.measureIndex >= part.measures.length) {
|
|
7083
|
+
return failure([operationError("MEASURE_NOT_FOUND", `Measure index ${options.measureIndex} out of bounds`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7084
|
+
}
|
|
7085
|
+
if (options.duration <= 0) {
|
|
7086
|
+
return failure([operationError("INVALID_DURATION", `Duration must be positive`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7087
|
+
}
|
|
7088
|
+
if (options.position < 0) {
|
|
7089
|
+
return failure([operationError("INVALID_POSITION", `Position cannot be negative`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7090
|
+
}
|
|
7091
|
+
const result = cloneScore(score);
|
|
7092
|
+
const measure = result.parts[options.partIndex].measures[options.measureIndex];
|
|
7093
|
+
const context = getMeasureContext(result, options.partIndex, options.measureIndex);
|
|
7094
|
+
const measureDuration = context.time ? getMeasureDuration(context.divisions, context.time) : context.divisions * 4;
|
|
7095
|
+
const noteEnd = options.position + options.duration;
|
|
7096
|
+
if (noteEnd > measureDuration) {
|
|
7097
|
+
return failure([operationError(
|
|
7098
|
+
"EXCEEDS_MEASURE",
|
|
7099
|
+
`Note ending at ${noteEnd} exceeds measure duration ${measureDuration}`,
|
|
7100
|
+
{ partIndex: options.partIndex, measureIndex: options.measureIndex },
|
|
7101
|
+
{ noteEnd, measureDuration }
|
|
7102
|
+
)]);
|
|
7103
|
+
}
|
|
7104
|
+
const voiceEntries = getVoiceEntries(measure, options.voice, options.staff);
|
|
7105
|
+
const { hasNotes: hasNotes2, conflictingNotes } = hasNotesInRange(voiceEntries, options.position, noteEnd);
|
|
7106
|
+
if (hasNotes2) {
|
|
7107
|
+
return failure([operationError(
|
|
7108
|
+
"NOTE_CONFLICT",
|
|
7109
|
+
`Position ${options.position}-${noteEnd} conflicts with existing note(s)`,
|
|
7110
|
+
{ partIndex: options.partIndex, measureIndex: options.measureIndex, voice: options.voice },
|
|
7111
|
+
{ conflictingPositions: conflictingNotes.map((n) => ({ start: n.position, end: n.endPosition })) }
|
|
7112
|
+
)]);
|
|
7113
|
+
}
|
|
7114
|
+
const newNote = {
|
|
7115
|
+
type: "note",
|
|
7116
|
+
pitch: options.pitch,
|
|
7117
|
+
duration: options.duration,
|
|
7118
|
+
voice: options.voice,
|
|
7119
|
+
staff: options.staff,
|
|
7120
|
+
noteType: options.noteType,
|
|
7121
|
+
dots: options.dots
|
|
7122
|
+
};
|
|
7123
|
+
const existingNotes = voiceEntries.filter((e) => {
|
|
7124
|
+
if (e.entry.type !== "note") return true;
|
|
7125
|
+
const note = e.entry;
|
|
7126
|
+
if (note.rest) {
|
|
7127
|
+
return !(e.position < noteEnd && e.endPosition > options.position);
|
|
7128
|
+
}
|
|
7129
|
+
return true;
|
|
7130
|
+
}).map((e) => ({ position: e.position, entry: e.entry }));
|
|
7131
|
+
existingNotes.push({ position: options.position, entry: newNote });
|
|
7132
|
+
measure.entries = rebuildMeasureWithVoice(
|
|
7133
|
+
measure,
|
|
7134
|
+
options.voice,
|
|
7135
|
+
existingNotes,
|
|
7136
|
+
measureDuration,
|
|
7137
|
+
options.staff
|
|
7138
|
+
);
|
|
7139
|
+
const errors = validateMeasureLocal(measure, context, {
|
|
7140
|
+
checkMeasureDuration: true,
|
|
7141
|
+
checkPosition: true,
|
|
7142
|
+
checkVoiceStaff: true
|
|
7143
|
+
});
|
|
7144
|
+
const criticalErrors = errors.filter((e) => e.level === "error");
|
|
7145
|
+
if (criticalErrors.length > 0) {
|
|
7146
|
+
return failure(criticalErrors);
|
|
7147
|
+
}
|
|
7148
|
+
return success(result, errors.filter((e) => e.level !== "error"));
|
|
7149
|
+
}
|
|
7150
|
+
function removeNote(score, options) {
|
|
7151
|
+
if (options.partIndex < 0 || options.partIndex >= score.parts.length) {
|
|
7152
|
+
return failure([operationError("PART_NOT_FOUND", `Part index ${options.partIndex} out of bounds`, { partIndex: options.partIndex })]);
|
|
7153
|
+
}
|
|
7154
|
+
const part = score.parts[options.partIndex];
|
|
7155
|
+
if (options.measureIndex < 0 || options.measureIndex >= part.measures.length) {
|
|
7156
|
+
return failure([operationError("MEASURE_NOT_FOUND", `Measure index ${options.measureIndex} out of bounds`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7157
|
+
}
|
|
7158
|
+
const result = cloneScore(score);
|
|
7159
|
+
const measure = result.parts[options.partIndex].measures[options.measureIndex];
|
|
7160
|
+
let noteCount = 0;
|
|
7161
|
+
let targetEntry = null;
|
|
7162
|
+
let targetIndex = -1;
|
|
7163
|
+
for (let i2 = 0; i2 < measure.entries.length; i2++) {
|
|
7164
|
+
const entry = measure.entries[i2];
|
|
7165
|
+
if (entry.type === "note" && !entry.rest) {
|
|
7166
|
+
if (noteCount === options.noteIndex) {
|
|
7167
|
+
targetEntry = entry;
|
|
7168
|
+
targetIndex = i2;
|
|
7169
|
+
break;
|
|
7170
|
+
}
|
|
7171
|
+
noteCount++;
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
if (!targetEntry || targetIndex === -1) {
|
|
7175
|
+
return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7176
|
+
}
|
|
7177
|
+
measure.entries[targetIndex] = createRest(
|
|
7178
|
+
targetEntry.duration,
|
|
7179
|
+
targetEntry.voice,
|
|
7180
|
+
targetEntry.staff
|
|
7181
|
+
);
|
|
7182
|
+
let i = targetIndex + 1;
|
|
7183
|
+
while (i < measure.entries.length) {
|
|
7184
|
+
const entry = measure.entries[i];
|
|
7185
|
+
if (entry.type === "note" && entry.chord) {
|
|
7186
|
+
measure.entries.splice(i, 1);
|
|
7187
|
+
} else {
|
|
7188
|
+
break;
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
return success(result);
|
|
7192
|
+
}
|
|
7193
|
+
function addChord(score, options) {
|
|
7194
|
+
if (options.partIndex < 0 || options.partIndex >= score.parts.length) {
|
|
7195
|
+
return failure([operationError("PART_NOT_FOUND", `Part index ${options.partIndex} out of bounds`, { partIndex: options.partIndex })]);
|
|
7196
|
+
}
|
|
7197
|
+
const part = score.parts[options.partIndex];
|
|
7198
|
+
if (options.measureIndex < 0 || options.measureIndex >= part.measures.length) {
|
|
7199
|
+
return failure([operationError("MEASURE_NOT_FOUND", `Measure index ${options.measureIndex} out of bounds`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7200
|
+
}
|
|
7201
|
+
const result = cloneScore(score);
|
|
7202
|
+
const measure = result.parts[options.partIndex].measures[options.measureIndex];
|
|
7203
|
+
let noteCount = 0;
|
|
7204
|
+
let targetEntry = null;
|
|
7205
|
+
let targetIndex = -1;
|
|
7206
|
+
for (let i = 0; i < measure.entries.length; i++) {
|
|
7207
|
+
const entry = measure.entries[i];
|
|
7208
|
+
if (entry.type === "note" && !entry.rest && !entry.chord) {
|
|
7209
|
+
if (noteCount === options.noteIndex) {
|
|
7210
|
+
targetEntry = entry;
|
|
7211
|
+
targetIndex = i;
|
|
7212
|
+
break;
|
|
7213
|
+
}
|
|
7214
|
+
noteCount++;
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
if (!targetEntry || targetIndex === -1) {
|
|
7218
|
+
return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7219
|
+
}
|
|
7220
|
+
const chordNote = {
|
|
7221
|
+
type: "note",
|
|
7222
|
+
pitch: options.pitch,
|
|
7223
|
+
duration: targetEntry.duration,
|
|
7224
|
+
voice: targetEntry.voice,
|
|
7225
|
+
staff: targetEntry.staff,
|
|
7226
|
+
chord: true,
|
|
7227
|
+
noteType: targetEntry.noteType,
|
|
7228
|
+
dots: targetEntry.dots
|
|
7229
|
+
};
|
|
7230
|
+
let insertIndex = targetIndex + 1;
|
|
7231
|
+
while (insertIndex < measure.entries.length) {
|
|
7232
|
+
const entry = measure.entries[insertIndex];
|
|
7233
|
+
if (entry.type === "note" && entry.chord) {
|
|
7234
|
+
insertIndex++;
|
|
7235
|
+
} else {
|
|
7236
|
+
break;
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
measure.entries.splice(insertIndex, 0, chordNote);
|
|
7240
|
+
return success(result);
|
|
7241
|
+
}
|
|
7242
|
+
function changeNoteDuration(score, options) {
|
|
7243
|
+
if (options.partIndex < 0 || options.partIndex >= score.parts.length) {
|
|
7244
|
+
return failure([operationError("PART_NOT_FOUND", `Part index ${options.partIndex} out of bounds`, { partIndex: options.partIndex })]);
|
|
7245
|
+
}
|
|
7246
|
+
const part = score.parts[options.partIndex];
|
|
7247
|
+
if (options.measureIndex < 0 || options.measureIndex >= part.measures.length) {
|
|
7248
|
+
return failure([operationError("MEASURE_NOT_FOUND", `Measure index ${options.measureIndex} out of bounds`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7249
|
+
}
|
|
7250
|
+
if (options.newDuration <= 0) {
|
|
7251
|
+
return failure([operationError("INVALID_DURATION", `Duration must be positive`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7252
|
+
}
|
|
7253
|
+
const result = cloneScore(score);
|
|
7254
|
+
const measure = result.parts[options.partIndex].measures[options.measureIndex];
|
|
7255
|
+
const context = getMeasureContext(result, options.partIndex, options.measureIndex);
|
|
7256
|
+
const measureDuration = context.time ? getMeasureDuration(context.divisions, context.time) : context.divisions * 4;
|
|
7257
|
+
let noteCount = 0;
|
|
7258
|
+
let targetEntry = null;
|
|
7259
|
+
let targetPosition = 0;
|
|
7260
|
+
let position = 0;
|
|
7261
|
+
for (const entry of measure.entries) {
|
|
7262
|
+
if (entry.type === "note") {
|
|
7263
|
+
if (!entry.rest && !entry.chord) {
|
|
7264
|
+
if (noteCount === options.noteIndex) {
|
|
7265
|
+
targetEntry = entry;
|
|
7266
|
+
targetPosition = position;
|
|
7267
|
+
break;
|
|
7268
|
+
}
|
|
7269
|
+
noteCount++;
|
|
7270
|
+
}
|
|
7271
|
+
if (!entry.chord) {
|
|
7272
|
+
position += entry.duration;
|
|
7273
|
+
}
|
|
7274
|
+
} else if (entry.type === "backup") {
|
|
7275
|
+
position -= entry.duration;
|
|
7276
|
+
} else if (entry.type === "forward") {
|
|
7277
|
+
position += entry.duration;
|
|
7278
|
+
}
|
|
7279
|
+
}
|
|
7280
|
+
if (!targetEntry) {
|
|
7281
|
+
return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7282
|
+
}
|
|
7283
|
+
const oldDuration = targetEntry.duration;
|
|
7284
|
+
const newEnd = targetPosition + options.newDuration;
|
|
7285
|
+
if (newEnd > measureDuration) {
|
|
7286
|
+
return failure([operationError(
|
|
7287
|
+
"EXCEEDS_MEASURE",
|
|
7288
|
+
`New duration would exceed measure (ends at ${newEnd}, measure is ${measureDuration})`,
|
|
7289
|
+
{ partIndex: options.partIndex, measureIndex: options.measureIndex },
|
|
7290
|
+
{ newEnd, measureDuration }
|
|
7291
|
+
)]);
|
|
7292
|
+
}
|
|
7293
|
+
const voiceEntries = getVoiceEntries(measure, targetEntry.voice, targetEntry.staff);
|
|
7294
|
+
if (options.newDuration > oldDuration) {
|
|
7295
|
+
const { hasNotes: hasNotes2, conflictingNotes } = hasNotesInRange(
|
|
7296
|
+
voiceEntries.filter((e) => e.position !== targetPosition),
|
|
7297
|
+
// Exclude current note
|
|
7298
|
+
targetPosition + oldDuration,
|
|
7299
|
+
newEnd
|
|
7300
|
+
);
|
|
7301
|
+
if (hasNotes2) {
|
|
7302
|
+
return failure([operationError(
|
|
7303
|
+
"NOTE_CONFLICT",
|
|
7304
|
+
`Cannot extend note: conflicts with existing note(s)`,
|
|
7305
|
+
{ partIndex: options.partIndex, measureIndex: options.measureIndex },
|
|
7306
|
+
{ conflictingPositions: conflictingNotes.map((n) => ({ start: n.position, end: n.endPosition })) }
|
|
7307
|
+
)]);
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
targetEntry.duration = options.newDuration;
|
|
7311
|
+
if (options.noteType !== void 0) {
|
|
7312
|
+
targetEntry.noteType = options.noteType;
|
|
7313
|
+
}
|
|
7314
|
+
if (options.dots !== void 0) {
|
|
7315
|
+
targetEntry.dots = options.dots;
|
|
7316
|
+
}
|
|
7317
|
+
const existingNotes = voiceEntries.filter((e) => {
|
|
7318
|
+
if (e.position === targetPosition) return true;
|
|
7319
|
+
const note = e.entry;
|
|
7320
|
+
if (note.rest) {
|
|
7321
|
+
if (options.newDuration > oldDuration) {
|
|
7322
|
+
return !(e.position >= targetPosition + oldDuration && e.position < newEnd);
|
|
7323
|
+
}
|
|
7324
|
+
}
|
|
7325
|
+
return true;
|
|
7326
|
+
}).map((e) => ({ position: e.position, entry: e.entry }));
|
|
7327
|
+
const modifiedIdx = existingNotes.findIndex((e) => e.position === targetPosition);
|
|
7328
|
+
if (modifiedIdx >= 0) {
|
|
7329
|
+
existingNotes[modifiedIdx].entry = targetEntry;
|
|
7330
|
+
}
|
|
7331
|
+
measure.entries = rebuildMeasureWithVoice(
|
|
7332
|
+
measure,
|
|
7333
|
+
targetEntry.voice,
|
|
7334
|
+
existingNotes,
|
|
7335
|
+
measureDuration,
|
|
7336
|
+
targetEntry.staff
|
|
7337
|
+
);
|
|
7338
|
+
const errors = validateMeasureLocal(measure, context, {
|
|
7339
|
+
checkMeasureDuration: true,
|
|
7340
|
+
checkPosition: true,
|
|
7341
|
+
checkVoiceStaff: true
|
|
7342
|
+
});
|
|
7343
|
+
const criticalErrors = errors.filter((e) => e.level === "error");
|
|
7344
|
+
if (criticalErrors.length > 0) {
|
|
7345
|
+
return failure(criticalErrors);
|
|
7346
|
+
}
|
|
7347
|
+
return success(result, errors.filter((e) => e.level !== "error"));
|
|
7348
|
+
}
|
|
7349
|
+
function setNotePitch(score, options) {
|
|
7350
|
+
if (options.partIndex < 0 || options.partIndex >= score.parts.length) {
|
|
7351
|
+
return failure([operationError("PART_NOT_FOUND", `Part index ${options.partIndex} out of bounds`, { partIndex: options.partIndex })]);
|
|
7352
|
+
}
|
|
7353
|
+
const part = score.parts[options.partIndex];
|
|
7354
|
+
if (options.measureIndex < 0 || options.measureIndex >= part.measures.length) {
|
|
7355
|
+
return failure([operationError("MEASURE_NOT_FOUND", `Measure index ${options.measureIndex} out of bounds`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7356
|
+
}
|
|
7357
|
+
const result = cloneScore(score);
|
|
7358
|
+
const measure = result.parts[options.partIndex].measures[options.measureIndex];
|
|
7359
|
+
let noteCount = 0;
|
|
7360
|
+
for (const entry of measure.entries) {
|
|
7361
|
+
if (entry.type === "note" && !entry.rest) {
|
|
7362
|
+
if (noteCount === options.noteIndex) {
|
|
7363
|
+
entry.pitch = options.pitch;
|
|
7364
|
+
return success(result);
|
|
7365
|
+
}
|
|
7366
|
+
noteCount++;
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
|
|
7370
|
+
}
|
|
5565
7371
|
function transposePitch(pitch, semitones) {
|
|
5566
7372
|
const currentSemitone = STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0) + pitch.octave * 12;
|
|
5567
7373
|
const targetSemitone = currentSemitone + semitones;
|
|
@@ -5588,7 +7394,7 @@ function transposePitch(pitch, semitones) {
|
|
|
5588
7394
|
};
|
|
5589
7395
|
}
|
|
5590
7396
|
function transpose(score, semitones) {
|
|
5591
|
-
if (semitones === 0) return score;
|
|
7397
|
+
if (semitones === 0) return success(score);
|
|
5592
7398
|
const result = cloneScore(score);
|
|
5593
7399
|
for (const part of result.parts) {
|
|
5594
7400
|
for (const measure of part.measures) {
|
|
@@ -5599,59 +7405,7 @@ function transpose(score, semitones) {
|
|
|
5599
7405
|
}
|
|
5600
7406
|
}
|
|
5601
7407
|
}
|
|
5602
|
-
return result;
|
|
5603
|
-
}
|
|
5604
|
-
function addNote(score, options) {
|
|
5605
|
-
const result = cloneScore(score);
|
|
5606
|
-
const part = result.parts[options.partIndex];
|
|
5607
|
-
if (!part) return result;
|
|
5608
|
-
const measure = part.measures[options.measureIndex];
|
|
5609
|
-
if (!measure) return result;
|
|
5610
|
-
const newNote = {
|
|
5611
|
-
type: "note",
|
|
5612
|
-
voice: options.voice,
|
|
5613
|
-
staff: options.staff,
|
|
5614
|
-
...options.note
|
|
5615
|
-
};
|
|
5616
|
-
const currentPosition = getMeasureEndPosition(measure);
|
|
5617
|
-
const positionDiff = options.position - currentPosition;
|
|
5618
|
-
if (positionDiff < 0) {
|
|
5619
|
-
measure.entries.push({
|
|
5620
|
-
type: "backup",
|
|
5621
|
-
duration: -positionDiff
|
|
5622
|
-
});
|
|
5623
|
-
} else if (positionDiff > 0) {
|
|
5624
|
-
measure.entries.push({
|
|
5625
|
-
type: "forward",
|
|
5626
|
-
duration: positionDiff,
|
|
5627
|
-
voice: options.voice,
|
|
5628
|
-
staff: options.staff
|
|
5629
|
-
});
|
|
5630
|
-
}
|
|
5631
|
-
measure.entries.push(newNote);
|
|
5632
|
-
return result;
|
|
5633
|
-
}
|
|
5634
|
-
function deleteNote(score, options) {
|
|
5635
|
-
const result = cloneScore(score);
|
|
5636
|
-
const part = result.parts[options.partIndex];
|
|
5637
|
-
if (!part) return result;
|
|
5638
|
-
const measure = part.measures[options.measureIndex];
|
|
5639
|
-
if (!measure) return result;
|
|
5640
|
-
let noteCount = 0;
|
|
5641
|
-
let entryIndex = -1;
|
|
5642
|
-
for (let i = 0; i < measure.entries.length; i++) {
|
|
5643
|
-
if (measure.entries[i].type === "note") {
|
|
5644
|
-
if (noteCount === options.noteIndex) {
|
|
5645
|
-
entryIndex = i;
|
|
5646
|
-
break;
|
|
5647
|
-
}
|
|
5648
|
-
noteCount++;
|
|
5649
|
-
}
|
|
5650
|
-
}
|
|
5651
|
-
if (entryIndex !== -1) {
|
|
5652
|
-
measure.entries.splice(entryIndex, 1);
|
|
5653
|
-
}
|
|
5654
|
-
return result;
|
|
7408
|
+
return success(result);
|
|
5655
7409
|
}
|
|
5656
7410
|
function changeKey(score, key, options) {
|
|
5657
7411
|
const result = cloneScore(score);
|
|
@@ -5659,9 +7413,7 @@ function changeKey(score, key, options) {
|
|
|
5659
7413
|
for (const part of result.parts) {
|
|
5660
7414
|
for (const measure of part.measures) {
|
|
5661
7415
|
if (measure.number === targetMeasure) {
|
|
5662
|
-
if (!measure.attributes) {
|
|
5663
|
-
measure.attributes = {};
|
|
5664
|
-
}
|
|
7416
|
+
if (!measure.attributes) measure.attributes = {};
|
|
5665
7417
|
measure.attributes.key = key;
|
|
5666
7418
|
}
|
|
5667
7419
|
}
|
|
@@ -5674,9 +7426,7 @@ function changeTime(score, time, options) {
|
|
|
5674
7426
|
for (const part of result.parts) {
|
|
5675
7427
|
for (const measure of part.measures) {
|
|
5676
7428
|
if (measure.number === targetMeasure) {
|
|
5677
|
-
if (!measure.attributes) {
|
|
5678
|
-
measure.attributes = {};
|
|
5679
|
-
}
|
|
7429
|
+
if (!measure.attributes) measure.attributes = {};
|
|
5680
7430
|
measure.attributes.time = time;
|
|
5681
7431
|
}
|
|
5682
7432
|
}
|
|
@@ -5691,15 +7441,9 @@ function insertMeasure(score, options) {
|
|
|
5691
7441
|
if (insertIndex === -1) continue;
|
|
5692
7442
|
const numericPart = parseInt(targetMeasure, 10);
|
|
5693
7443
|
const newMeasureNumber = String(isNaN(numericPart) ? insertIndex + 2 : numericPart + 1);
|
|
5694
|
-
const newMeasure = {
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
};
|
|
5698
|
-
if (options.copyAttributes) {
|
|
5699
|
-
const sourceMeasure = part.measures[insertIndex];
|
|
5700
|
-
if (sourceMeasure.attributes) {
|
|
5701
|
-
newMeasure.attributes = { ...sourceMeasure.attributes };
|
|
5702
|
-
}
|
|
7444
|
+
const newMeasure = { number: newMeasureNumber, entries: [] };
|
|
7445
|
+
if (options.copyAttributes && part.measures[insertIndex].attributes) {
|
|
7446
|
+
newMeasure.attributes = { ...part.measures[insertIndex].attributes };
|
|
5703
7447
|
}
|
|
5704
7448
|
part.measures.splice(insertIndex + 1, 0, newMeasure);
|
|
5705
7449
|
for (let i = insertIndex + 2; i < part.measures.length; i++) {
|
|
@@ -5727,95 +7471,36 @@ function deleteMeasure(score, measureNumber) {
|
|
|
5727
7471
|
}
|
|
5728
7472
|
return result;
|
|
5729
7473
|
}
|
|
5730
|
-
|
|
5731
|
-
const result =
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
}
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
}
|
|
5761
|
-
}
|
|
5762
|
-
if (entryIndex !== -1 && targetNote) {
|
|
5763
|
-
const chordNote = {
|
|
5764
|
-
type: "note",
|
|
5765
|
-
pitch: options.pitch,
|
|
5766
|
-
duration: targetNote.duration,
|
|
5767
|
-
voice: targetNote.voice,
|
|
5768
|
-
staff: targetNote.staff,
|
|
5769
|
-
chord: true,
|
|
5770
|
-
noteType: targetNote.noteType,
|
|
5771
|
-
dots: targetNote.dots
|
|
5772
|
-
};
|
|
5773
|
-
measure.entries.splice(entryIndex + 1, 0, chordNote);
|
|
5774
|
-
}
|
|
5775
|
-
return result;
|
|
5776
|
-
}
|
|
5777
|
-
function modifyNotePitch(score, options) {
|
|
5778
|
-
const result = cloneScore(score);
|
|
5779
|
-
const part = result.parts[options.partIndex];
|
|
5780
|
-
if (!part) return result;
|
|
5781
|
-
const measure = part.measures[options.measureIndex];
|
|
5782
|
-
if (!measure) return result;
|
|
5783
|
-
let noteCount = 0;
|
|
5784
|
-
for (const entry of measure.entries) {
|
|
5785
|
-
if (entry.type === "note") {
|
|
5786
|
-
if (noteCount === options.noteIndex) {
|
|
5787
|
-
entry.pitch = options.pitch;
|
|
5788
|
-
break;
|
|
5789
|
-
}
|
|
5790
|
-
noteCount++;
|
|
5791
|
-
}
|
|
5792
|
-
}
|
|
5793
|
-
return result;
|
|
5794
|
-
}
|
|
5795
|
-
function modifyNoteDuration(score, options) {
|
|
5796
|
-
const result = cloneScore(score);
|
|
5797
|
-
const part = result.parts[options.partIndex];
|
|
5798
|
-
if (!part) return result;
|
|
5799
|
-
const measure = part.measures[options.measureIndex];
|
|
5800
|
-
if (!measure) return result;
|
|
5801
|
-
let noteCount = 0;
|
|
5802
|
-
for (const entry of measure.entries) {
|
|
5803
|
-
if (entry.type === "note") {
|
|
5804
|
-
if (noteCount === options.noteIndex) {
|
|
5805
|
-
entry.duration = options.duration;
|
|
5806
|
-
if (options.noteType !== void 0) {
|
|
5807
|
-
entry.noteType = options.noteType;
|
|
5808
|
-
}
|
|
5809
|
-
if (options.dots !== void 0) {
|
|
5810
|
-
entry.dots = options.dots;
|
|
5811
|
-
}
|
|
5812
|
-
break;
|
|
5813
|
-
}
|
|
5814
|
-
noteCount++;
|
|
5815
|
-
}
|
|
5816
|
-
}
|
|
5817
|
-
return result;
|
|
5818
|
-
}
|
|
7474
|
+
var addNote = (score, options) => {
|
|
7475
|
+
const result = insertNote(score, {
|
|
7476
|
+
partIndex: options.partIndex,
|
|
7477
|
+
measureIndex: options.measureIndex,
|
|
7478
|
+
voice: options.voice,
|
|
7479
|
+
staff: options.staff,
|
|
7480
|
+
position: options.position,
|
|
7481
|
+
pitch: options.note.pitch ?? { step: "C", octave: 4 },
|
|
7482
|
+
duration: options.note.duration,
|
|
7483
|
+
noteType: options.note.noteType,
|
|
7484
|
+
dots: options.note.dots
|
|
7485
|
+
});
|
|
7486
|
+
return result.success ? result.data : score;
|
|
7487
|
+
};
|
|
7488
|
+
var deleteNote = (score, options) => {
|
|
7489
|
+
const result = removeNote(score, options);
|
|
7490
|
+
return result.success ? result.data : score;
|
|
7491
|
+
};
|
|
7492
|
+
var addChordNote = (score, options) => {
|
|
7493
|
+
const result = addChord(score, { ...options, noteIndex: options.afterNoteIndex });
|
|
7494
|
+
return result.success ? result.data : score;
|
|
7495
|
+
};
|
|
7496
|
+
var modifyNotePitch = (score, options) => {
|
|
7497
|
+
const result = setNotePitch(score, options);
|
|
7498
|
+
return result.success ? result.data : score;
|
|
7499
|
+
};
|
|
7500
|
+
var modifyNoteDuration = (score, options) => {
|
|
7501
|
+
const result = changeNoteDuration(score, { ...options, newDuration: options.duration });
|
|
7502
|
+
return result.success ? result.data : score;
|
|
7503
|
+
};
|
|
5819
7504
|
|
|
5820
7505
|
// src/file.ts
|
|
5821
7506
|
import { readFile, writeFile } from "fs/promises";
|
|
@@ -5860,6 +7545,8 @@ export {
|
|
|
5860
7545
|
addNote,
|
|
5861
7546
|
assertMeasureValid,
|
|
5862
7547
|
assertValid,
|
|
7548
|
+
buildVoiceToStaffMap,
|
|
7549
|
+
buildVoiceToStaffMapForPart,
|
|
5863
7550
|
changeKey,
|
|
5864
7551
|
changeTime,
|
|
5865
7552
|
countNotes,
|
|
@@ -5867,36 +7554,82 @@ export {
|
|
|
5867
7554
|
deleteMeasure,
|
|
5868
7555
|
deleteNote,
|
|
5869
7556
|
exportMidi,
|
|
7557
|
+
findBarlines,
|
|
7558
|
+
findDirectionsByType,
|
|
5870
7559
|
findNotes,
|
|
7560
|
+
findNotesWithNotation,
|
|
5871
7561
|
formatLocation,
|
|
5872
7562
|
getAbsolutePosition,
|
|
7563
|
+
getAdjacentNotes,
|
|
5873
7564
|
getAllNotes,
|
|
5874
7565
|
getAttributesAtMeasure,
|
|
7566
|
+
getBeamGroups,
|
|
7567
|
+
getChordProgression,
|
|
5875
7568
|
getChords,
|
|
7569
|
+
getClefChanges,
|
|
7570
|
+
getClefForStaff,
|
|
7571
|
+
getDirections,
|
|
7572
|
+
getDirectionsAtPosition,
|
|
5876
7573
|
getDivisions,
|
|
5877
7574
|
getDuration,
|
|
7575
|
+
getDynamics,
|
|
7576
|
+
getEffectiveStaff,
|
|
7577
|
+
getEndings,
|
|
7578
|
+
getEntriesAtPosition,
|
|
7579
|
+
getEntriesForStaff,
|
|
7580
|
+
getEntriesInRange,
|
|
7581
|
+
getHarmonies,
|
|
7582
|
+
getHarmonyAtPosition,
|
|
7583
|
+
getKeyChanges,
|
|
7584
|
+
getLyricText,
|
|
7585
|
+
getLyrics,
|
|
5878
7586
|
getMeasure,
|
|
5879
7587
|
getMeasureByIndex,
|
|
5880
7588
|
getMeasureContext,
|
|
5881
7589
|
getMeasureCount,
|
|
5882
7590
|
getMeasureEndPosition,
|
|
7591
|
+
getNextNote,
|
|
5883
7592
|
getNormalizedDuration,
|
|
5884
7593
|
getNormalizedPosition,
|
|
7594
|
+
getNotesAtPosition,
|
|
5885
7595
|
getNotesForStaff,
|
|
5886
7596
|
getNotesForVoice,
|
|
7597
|
+
getNotesInRange,
|
|
7598
|
+
getOctaveShifts,
|
|
5887
7599
|
getPartById,
|
|
7600
|
+
getPartByIndex,
|
|
7601
|
+
getPartCount,
|
|
7602
|
+
getPartIds,
|
|
5888
7603
|
getPartIndex,
|
|
7604
|
+
getPedalMarkings,
|
|
7605
|
+
getPrevNote,
|
|
7606
|
+
getRepeatStructure,
|
|
7607
|
+
getSlurSpans,
|
|
7608
|
+
getStaffRange,
|
|
5889
7609
|
getStaveCount,
|
|
5890
7610
|
getStaves,
|
|
7611
|
+
getStructuralChanges,
|
|
7612
|
+
getTempoMarkings,
|
|
7613
|
+
getTiedNoteGroups,
|
|
7614
|
+
getTimeChanges,
|
|
7615
|
+
getTupletGroups,
|
|
7616
|
+
getVerseCount,
|
|
7617
|
+
getVerticalSlice,
|
|
7618
|
+
getVoiceLine,
|
|
7619
|
+
getVoiceLineInRange,
|
|
5891
7620
|
getVoices,
|
|
7621
|
+
getVoicesForStaff,
|
|
7622
|
+
getWedges,
|
|
5892
7623
|
groupByStaff,
|
|
5893
7624
|
groupByVoice,
|
|
5894
7625
|
hasMultipleStaves,
|
|
5895
7626
|
hasNotes,
|
|
7627
|
+
inferStaff,
|
|
5896
7628
|
insertMeasure,
|
|
5897
7629
|
isCompressed,
|
|
5898
7630
|
isRestMeasure,
|
|
5899
7631
|
isValid,
|
|
7632
|
+
iterateEntries,
|
|
5900
7633
|
iterateNotes,
|
|
5901
7634
|
measureRoundtrip,
|
|
5902
7635
|
modifyNoteDuration,
|
|
@@ -5910,7 +7643,6 @@ export {
|
|
|
5910
7643
|
serialize,
|
|
5911
7644
|
serializeCompressed,
|
|
5912
7645
|
serializeToFile,
|
|
5913
|
-
setDivisions,
|
|
5914
7646
|
transpose,
|
|
5915
7647
|
validate,
|
|
5916
7648
|
validateBackupForward,
|
|
@@ -5929,4 +7661,3 @@ export {
|
|
|
5929
7661
|
validateVoiceStaff,
|
|
5930
7662
|
withAbsolutePositions
|
|
5931
7663
|
};
|
|
5932
|
-
//# sourceMappingURL=index.mjs.map
|