musicxml-io 0.1.0 → 0.2.5
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-CJUkJI2P.d.ts +1269 -0
- package/dist/index-Hm73jOKD.d.mts +1269 -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 +4735 -212
- package/dist/operations/index.mjs +4649 -210
- 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-D3LhKCDK.d.mts} +299 -4
- package/dist/{types-DC_TnJu_.d.mts → types-D3LhKCDK.d.ts} +299 -4
- 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/accessors/index.js
CHANGED
|
@@ -20,19 +20,67 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/accessors/index.ts
|
|
21
21
|
var accessors_exports = {};
|
|
22
22
|
__export(accessors_exports, {
|
|
23
|
+
buildVoiceToStaffMap: () => buildVoiceToStaffMap,
|
|
24
|
+
buildVoiceToStaffMapForPart: () => buildVoiceToStaffMapForPart,
|
|
25
|
+
findBarlines: () => findBarlines,
|
|
26
|
+
findDirectionsByType: () => findDirectionsByType,
|
|
27
|
+
findNotesWithNotation: () => findNotesWithNotation,
|
|
23
28
|
getAbsolutePosition: () => getAbsolutePosition,
|
|
29
|
+
getAdjacentNotes: () => getAdjacentNotes,
|
|
24
30
|
getAllNotes: () => getAllNotes,
|
|
31
|
+
getBeamGroups: () => getBeamGroups,
|
|
32
|
+
getChordProgression: () => getChordProgression,
|
|
25
33
|
getChords: () => getChords,
|
|
34
|
+
getClefChanges: () => getClefChanges,
|
|
35
|
+
getClefForStaff: () => getClefForStaff,
|
|
36
|
+
getDirections: () => getDirections,
|
|
37
|
+
getDirectionsAtPosition: () => getDirectionsAtPosition,
|
|
38
|
+
getDynamics: () => getDynamics,
|
|
39
|
+
getEffectiveStaff: () => getEffectiveStaff,
|
|
40
|
+
getEndings: () => getEndings,
|
|
41
|
+
getEntriesAtPosition: () => getEntriesAtPosition,
|
|
42
|
+
getEntriesForStaff: () => getEntriesForStaff,
|
|
43
|
+
getEntriesInRange: () => getEntriesInRange,
|
|
44
|
+
getHarmonies: () => getHarmonies,
|
|
45
|
+
getHarmonyAtPosition: () => getHarmonyAtPosition,
|
|
46
|
+
getKeyChanges: () => getKeyChanges,
|
|
47
|
+
getLyricText: () => getLyricText,
|
|
48
|
+
getLyrics: () => getLyrics,
|
|
49
|
+
getNextNote: () => getNextNote,
|
|
26
50
|
getNormalizedDuration: () => getNormalizedDuration,
|
|
27
51
|
getNormalizedPosition: () => getNormalizedPosition,
|
|
52
|
+
getNotesAtPosition: () => getNotesAtPosition,
|
|
28
53
|
getNotesForStaff: () => getNotesForStaff,
|
|
29
54
|
getNotesForVoice: () => getNotesForVoice,
|
|
55
|
+
getNotesInRange: () => getNotesInRange,
|
|
56
|
+
getOctaveShifts: () => getOctaveShifts,
|
|
57
|
+
getPartByIndex: () => getPartByIndex,
|
|
58
|
+
getPartCount: () => getPartCount,
|
|
59
|
+
getPartIds: () => getPartIds,
|
|
60
|
+
getPedalMarkings: () => getPedalMarkings,
|
|
61
|
+
getPrevNote: () => getPrevNote,
|
|
62
|
+
getRepeatStructure: () => getRepeatStructure,
|
|
63
|
+
getSlurSpans: () => getSlurSpans,
|
|
64
|
+
getStaffRange: () => getStaffRange,
|
|
30
65
|
getStaves: () => getStaves,
|
|
66
|
+
getStructuralChanges: () => getStructuralChanges,
|
|
67
|
+
getTempoMarkings: () => getTempoMarkings,
|
|
68
|
+
getTiedNoteGroups: () => getTiedNoteGroups,
|
|
69
|
+
getTimeChanges: () => getTimeChanges,
|
|
70
|
+
getTupletGroups: () => getTupletGroups,
|
|
71
|
+
getVerseCount: () => getVerseCount,
|
|
72
|
+
getVerticalSlice: () => getVerticalSlice,
|
|
73
|
+
getVoiceLine: () => getVoiceLine,
|
|
74
|
+
getVoiceLineInRange: () => getVoiceLineInRange,
|
|
31
75
|
getVoices: () => getVoices,
|
|
76
|
+
getVoicesForStaff: () => getVoicesForStaff,
|
|
77
|
+
getWedges: () => getWedges,
|
|
32
78
|
groupByStaff: () => groupByStaff,
|
|
33
79
|
groupByVoice: () => groupByVoice,
|
|
34
80
|
hasNotes: () => hasNotes,
|
|
81
|
+
inferStaff: () => inferStaff,
|
|
35
82
|
isRestMeasure: () => isRestMeasure,
|
|
83
|
+
iterateEntries: () => iterateEntries,
|
|
36
84
|
iterateNotes: () => iterateNotes,
|
|
37
85
|
withAbsolutePositions: () => withAbsolutePositions
|
|
38
86
|
});
|
|
@@ -218,22 +266,1272 @@ function getNormalizedDuration(note, options) {
|
|
|
218
266
|
const currentDivisions = options.currentDivisions ?? 1;
|
|
219
267
|
return note.duration * options.baseDivisions / currentDivisions;
|
|
220
268
|
}
|
|
269
|
+
function getEntriesForStaff(measure, staff) {
|
|
270
|
+
return measure.entries.filter((entry) => {
|
|
271
|
+
if (entry.type === "note") {
|
|
272
|
+
return (entry.staff ?? 1) === staff;
|
|
273
|
+
}
|
|
274
|
+
if (entry.type === "forward") {
|
|
275
|
+
return (entry.staff ?? 1) === staff;
|
|
276
|
+
}
|
|
277
|
+
if (entry.type === "direction") {
|
|
278
|
+
return (entry.staff ?? 1) === staff;
|
|
279
|
+
}
|
|
280
|
+
if (entry.type === "backup") {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function buildVoiceToStaffMap(measure) {
|
|
287
|
+
const map = /* @__PURE__ */ new Map();
|
|
288
|
+
for (const entry of measure.entries) {
|
|
289
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
290
|
+
const voice = entry.voice;
|
|
291
|
+
const staff = entry.staff;
|
|
292
|
+
if (!map.has(voice)) {
|
|
293
|
+
map.set(voice, staff);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
get: (voice) => map.get(voice),
|
|
299
|
+
has: (voice) => map.has(voice),
|
|
300
|
+
entries: () => map.entries(),
|
|
301
|
+
size: map.size
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function buildVoiceToStaffMapForPart(part) {
|
|
305
|
+
const map = /* @__PURE__ */ new Map();
|
|
306
|
+
for (const measure of part.measures) {
|
|
307
|
+
for (const entry of measure.entries) {
|
|
308
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
309
|
+
const voice = entry.voice;
|
|
310
|
+
const staff = entry.staff;
|
|
311
|
+
if (!map.has(voice)) {
|
|
312
|
+
map.set(voice, staff);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
get: (voice) => map.get(voice),
|
|
319
|
+
has: (voice) => map.has(voice),
|
|
320
|
+
entries: () => map.entries(),
|
|
321
|
+
size: map.size
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function inferStaff(entry, voiceToStaffMap) {
|
|
325
|
+
if (entry.staff !== void 0) {
|
|
326
|
+
return entry.staff;
|
|
327
|
+
}
|
|
328
|
+
const inferredStaff = voiceToStaffMap.get(entry.voice);
|
|
329
|
+
if (inferredStaff !== void 0) {
|
|
330
|
+
return inferredStaff;
|
|
331
|
+
}
|
|
332
|
+
return 1;
|
|
333
|
+
}
|
|
334
|
+
function getEffectiveStaff(entry, measure) {
|
|
335
|
+
if (entry.staff !== void 0) {
|
|
336
|
+
return entry.staff;
|
|
337
|
+
}
|
|
338
|
+
const map = buildVoiceToStaffMap(measure);
|
|
339
|
+
return inferStaff(entry, map);
|
|
340
|
+
}
|
|
341
|
+
function getClefForStaff(score, options) {
|
|
342
|
+
const part = score.parts[options.partIndex];
|
|
343
|
+
if (!part) return void 0;
|
|
344
|
+
for (let i = options.measureIndex; i >= 0; i--) {
|
|
345
|
+
const measure = part.measures[i];
|
|
346
|
+
for (const entry of measure.entries) {
|
|
347
|
+
if (entry.type === "attributes" && entry.attributes.clef) {
|
|
348
|
+
for (const clef of entry.attributes.clef) {
|
|
349
|
+
if ((clef.staff ?? 1) === options.staff) {
|
|
350
|
+
return clef;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (measure.attributes?.clef) {
|
|
356
|
+
for (const clef of measure.attributes.clef) {
|
|
357
|
+
if ((clef.staff ?? 1) === options.staff) {
|
|
358
|
+
return clef;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return void 0;
|
|
364
|
+
}
|
|
365
|
+
function getVoicesForStaff(measure, staff) {
|
|
366
|
+
const voices = /* @__PURE__ */ new Set();
|
|
367
|
+
for (const entry of measure.entries) {
|
|
368
|
+
if (entry.type === "note") {
|
|
369
|
+
const entryStaff = entry.staff ?? 1;
|
|
370
|
+
if (entryStaff === staff) {
|
|
371
|
+
voices.add(entry.voice);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return Array.from(voices).sort((a, b) => a - b);
|
|
376
|
+
}
|
|
377
|
+
function getStaffRange(score, partIndex) {
|
|
378
|
+
const part = score.parts[partIndex];
|
|
379
|
+
if (!part) return { min: 1, max: 1 };
|
|
380
|
+
let min = 1;
|
|
381
|
+
let max = 1;
|
|
382
|
+
for (const measure of part.measures) {
|
|
383
|
+
if (measure.attributes?.staves !== void 0) {
|
|
384
|
+
max = Math.max(max, measure.attributes.staves);
|
|
385
|
+
}
|
|
386
|
+
for (const entry of measure.entries) {
|
|
387
|
+
if (entry.type === "note" && entry.staff !== void 0) {
|
|
388
|
+
max = Math.max(max, entry.staff);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { min, max };
|
|
393
|
+
}
|
|
394
|
+
function getEntriesAtPosition(measure, position, options) {
|
|
395
|
+
const result = [];
|
|
396
|
+
const state = createPositionState();
|
|
397
|
+
for (const entry of measure.entries) {
|
|
398
|
+
const currentPosition = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
399
|
+
if (currentPosition === position) {
|
|
400
|
+
if (entry.type === "note") {
|
|
401
|
+
if (options?.staff !== void 0 && (entry.staff ?? 1) !== options.staff) {
|
|
402
|
+
updatePositionForEntry(state, entry);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (options?.voice !== void 0 && entry.voice !== options.voice) {
|
|
406
|
+
updatePositionForEntry(state, entry);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (options?.includeChordNotes === false && entry.chord) {
|
|
410
|
+
updatePositionForEntry(state, entry);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
result.push(entry);
|
|
415
|
+
}
|
|
416
|
+
updatePositionForEntry(state, entry);
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
function getNotesAtPosition(measure, position, options) {
|
|
421
|
+
return getEntriesAtPosition(measure, position, options).filter(
|
|
422
|
+
(entry) => entry.type === "note"
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
function getEntriesInRange(measure, range, options) {
|
|
426
|
+
const result = [];
|
|
427
|
+
const state = createPositionState();
|
|
428
|
+
for (const entry of measure.entries) {
|
|
429
|
+
const currentPosition = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
430
|
+
if (currentPosition >= range.start && currentPosition < range.end) {
|
|
431
|
+
if (entry.type === "note") {
|
|
432
|
+
if (options?.staff !== void 0 && (entry.staff ?? 1) !== options.staff) {
|
|
433
|
+
updatePositionForEntry(state, entry);
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (options?.voice !== void 0 && entry.voice !== options.voice) {
|
|
437
|
+
updatePositionForEntry(state, entry);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (options?.includeChordNotes === false && entry.chord) {
|
|
441
|
+
updatePositionForEntry(state, entry);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
result.push(entry);
|
|
446
|
+
}
|
|
447
|
+
updatePositionForEntry(state, entry);
|
|
448
|
+
}
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
function getNotesInRange(measure, range, options) {
|
|
452
|
+
return getEntriesInRange(measure, range, options).filter(
|
|
453
|
+
(entry) => entry.type === "note"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
function getVerticalSlice(score, options) {
|
|
457
|
+
const parts = /* @__PURE__ */ new Map();
|
|
458
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
459
|
+
const part = score.parts[partIndex];
|
|
460
|
+
const measure = part.measures[options.measureIndex];
|
|
461
|
+
if (!measure) continue;
|
|
462
|
+
const notes = getNotesAtPosition(measure, options.position);
|
|
463
|
+
if (notes.length > 0) {
|
|
464
|
+
parts.set(partIndex, notes);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
measureIndex: options.measureIndex,
|
|
469
|
+
position: options.position,
|
|
470
|
+
parts
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function getVoiceLine(score, options) {
|
|
474
|
+
const part = score.parts[options.partIndex];
|
|
475
|
+
if (!part) {
|
|
476
|
+
return { partIndex: options.partIndex, voice: options.voice, staff: options.staff, notes: [] };
|
|
477
|
+
}
|
|
478
|
+
const notes = [];
|
|
479
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
480
|
+
const measure = part.measures[measureIndex];
|
|
481
|
+
const state = createPositionState();
|
|
482
|
+
for (const entry of measure.entries) {
|
|
483
|
+
if (entry.type === "note") {
|
|
484
|
+
const entryStaff = entry.staff ?? 1;
|
|
485
|
+
const matchesVoice = entry.voice === options.voice;
|
|
486
|
+
const matchesStaff = options.staff === void 0 || entryStaff === options.staff;
|
|
487
|
+
if (matchesVoice && matchesStaff) {
|
|
488
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
489
|
+
notes.push({
|
|
490
|
+
note: entry,
|
|
491
|
+
part,
|
|
492
|
+
partIndex: options.partIndex,
|
|
493
|
+
measure,
|
|
494
|
+
measureIndex,
|
|
495
|
+
position
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
updatePositionForEntry(state, entry);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
partIndex: options.partIndex,
|
|
504
|
+
voice: options.voice,
|
|
505
|
+
staff: options.staff,
|
|
506
|
+
notes
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function getVoiceLineInRange(score, options) {
|
|
510
|
+
const part = score.parts[options.partIndex];
|
|
511
|
+
if (!part) {
|
|
512
|
+
return { partIndex: options.partIndex, voice: options.voice, staff: options.staff, notes: [] };
|
|
513
|
+
}
|
|
514
|
+
const notes = [];
|
|
515
|
+
for (let measureIndex = options.startMeasure; measureIndex <= options.endMeasure && measureIndex < part.measures.length; measureIndex++) {
|
|
516
|
+
const measure = part.measures[measureIndex];
|
|
517
|
+
const state = createPositionState();
|
|
518
|
+
for (const entry of measure.entries) {
|
|
519
|
+
if (entry.type === "note") {
|
|
520
|
+
const entryStaff = entry.staff ?? 1;
|
|
521
|
+
const matchesVoice = entry.voice === options.voice;
|
|
522
|
+
const matchesStaff = options.staff === void 0 || entryStaff === options.staff;
|
|
523
|
+
if (matchesVoice && matchesStaff) {
|
|
524
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
525
|
+
notes.push({
|
|
526
|
+
note: entry,
|
|
527
|
+
part,
|
|
528
|
+
partIndex: options.partIndex,
|
|
529
|
+
measure,
|
|
530
|
+
measureIndex,
|
|
531
|
+
position
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
updatePositionForEntry(state, entry);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
partIndex: options.partIndex,
|
|
540
|
+
voice: options.voice,
|
|
541
|
+
staff: options.staff,
|
|
542
|
+
notes
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function* iterateEntries(score) {
|
|
546
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
547
|
+
const part = score.parts[partIndex];
|
|
548
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
549
|
+
const measure = part.measures[measureIndex];
|
|
550
|
+
const state = createPositionState();
|
|
551
|
+
for (const entry of measure.entries) {
|
|
552
|
+
const position = entry.type === "note" && entry.chord ? state.lastNonChordPosition : state.position;
|
|
553
|
+
yield {
|
|
554
|
+
entry,
|
|
555
|
+
part,
|
|
556
|
+
partIndex,
|
|
557
|
+
measure,
|
|
558
|
+
measureIndex,
|
|
559
|
+
position
|
|
560
|
+
};
|
|
561
|
+
updatePositionForEntry(state, entry);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function getNextNote(score, context) {
|
|
567
|
+
const part = score.parts[context.partIndex];
|
|
568
|
+
if (!part) return null;
|
|
569
|
+
let foundCurrent = false;
|
|
570
|
+
for (let measureIndex = context.measureIndex; measureIndex < part.measures.length; measureIndex++) {
|
|
571
|
+
const measure = part.measures[measureIndex];
|
|
572
|
+
const state = createPositionState();
|
|
573
|
+
for (const entry of measure.entries) {
|
|
574
|
+
if (entry.type === "note") {
|
|
575
|
+
const entryPosition = entry.chord ? state.lastNonChordPosition : state.position;
|
|
576
|
+
if (measureIndex === context.measureIndex && entry === context.note) {
|
|
577
|
+
foundCurrent = true;
|
|
578
|
+
updatePositionForEntry(state, entry);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (foundCurrent && entry.voice === context.note.voice && !entry.chord) {
|
|
582
|
+
if (context.note.staff !== void 0) {
|
|
583
|
+
if ((entry.staff ?? 1) !== context.note.staff) {
|
|
584
|
+
updatePositionForEntry(state, entry);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return {
|
|
589
|
+
note: entry,
|
|
590
|
+
part,
|
|
591
|
+
partIndex: context.partIndex,
|
|
592
|
+
measure,
|
|
593
|
+
measureIndex,
|
|
594
|
+
position: entryPosition
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
updatePositionForEntry(state, entry);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
function getPrevNote(score, context) {
|
|
604
|
+
const part = score.parts[context.partIndex];
|
|
605
|
+
if (!part) return null;
|
|
606
|
+
let lastCandidate = null;
|
|
607
|
+
for (let measureIndex = 0; measureIndex <= context.measureIndex; measureIndex++) {
|
|
608
|
+
const measure = part.measures[measureIndex];
|
|
609
|
+
const state = createPositionState();
|
|
610
|
+
for (const entry of measure.entries) {
|
|
611
|
+
if (entry.type === "note") {
|
|
612
|
+
const entryPosition = entry.chord ? state.lastNonChordPosition : state.position;
|
|
613
|
+
if (measureIndex === context.measureIndex && entry === context.note) {
|
|
614
|
+
return lastCandidate;
|
|
615
|
+
}
|
|
616
|
+
if (entry.voice === context.note.voice && !entry.chord) {
|
|
617
|
+
if (context.note.staff !== void 0) {
|
|
618
|
+
if ((entry.staff ?? 1) !== context.note.staff) {
|
|
619
|
+
updatePositionForEntry(state, entry);
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
lastCandidate = {
|
|
624
|
+
note: entry,
|
|
625
|
+
part,
|
|
626
|
+
partIndex: context.partIndex,
|
|
627
|
+
measure,
|
|
628
|
+
measureIndex,
|
|
629
|
+
position: entryPosition
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
updatePositionForEntry(state, entry);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
function getAdjacentNotes(score, context) {
|
|
639
|
+
return {
|
|
640
|
+
prev: getPrevNote(score, context),
|
|
641
|
+
next: getNextNote(score, context)
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function getDirections(score, options) {
|
|
645
|
+
const results = [];
|
|
646
|
+
const startPart = options?.partIndex ?? 0;
|
|
647
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
648
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
649
|
+
const part = score.parts[partIndex];
|
|
650
|
+
if (!part) continue;
|
|
651
|
+
const startMeasure = options?.measureIndex ?? 0;
|
|
652
|
+
const endMeasure = options?.measureIndex !== void 0 ? options.measureIndex + 1 : part.measures.length;
|
|
653
|
+
for (let measureIndex = startMeasure; measureIndex < endMeasure; measureIndex++) {
|
|
654
|
+
const measure = part.measures[measureIndex];
|
|
655
|
+
if (!measure) continue;
|
|
656
|
+
const state = createPositionState();
|
|
657
|
+
for (const entry of measure.entries) {
|
|
658
|
+
if (entry.type === "direction") {
|
|
659
|
+
results.push({
|
|
660
|
+
direction: entry,
|
|
661
|
+
part,
|
|
662
|
+
partIndex,
|
|
663
|
+
measure,
|
|
664
|
+
measureIndex,
|
|
665
|
+
position: state.position
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
updatePositionForEntry(state, entry);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return results;
|
|
673
|
+
}
|
|
674
|
+
function getDirectionsAtPosition(measure, position) {
|
|
675
|
+
const results = [];
|
|
676
|
+
const state = createPositionState();
|
|
677
|
+
for (const entry of measure.entries) {
|
|
678
|
+
if (entry.type === "direction" && state.position === position) {
|
|
679
|
+
results.push(entry);
|
|
680
|
+
}
|
|
681
|
+
updatePositionForEntry(state, entry);
|
|
682
|
+
}
|
|
683
|
+
return results;
|
|
684
|
+
}
|
|
685
|
+
function findDirectionsByType(score, kind) {
|
|
686
|
+
const allDirections = getDirections(score);
|
|
687
|
+
return allDirections.filter(
|
|
688
|
+
(d) => d.direction.directionTypes.some((dt) => dt.kind === kind)
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
function getDynamics(score, options) {
|
|
692
|
+
const results = [];
|
|
693
|
+
const startPart = options?.partIndex ?? 0;
|
|
694
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
695
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
696
|
+
const part = score.parts[partIndex];
|
|
697
|
+
if (!part) continue;
|
|
698
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
699
|
+
const measure = part.measures[measureIndex];
|
|
700
|
+
const state = createPositionState();
|
|
701
|
+
for (const entry of measure.entries) {
|
|
702
|
+
if (entry.type === "direction") {
|
|
703
|
+
for (const dirType of entry.directionTypes) {
|
|
704
|
+
if (dirType.kind === "dynamics") {
|
|
705
|
+
results.push({
|
|
706
|
+
dynamic: dirType.value,
|
|
707
|
+
direction: entry,
|
|
708
|
+
part,
|
|
709
|
+
partIndex,
|
|
710
|
+
measure,
|
|
711
|
+
measureIndex,
|
|
712
|
+
position: state.position
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
updatePositionForEntry(state, entry);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return results;
|
|
722
|
+
}
|
|
723
|
+
function getTempoMarkings(score) {
|
|
724
|
+
const results = [];
|
|
725
|
+
for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
|
|
726
|
+
const part = score.parts[partIndex];
|
|
727
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
728
|
+
const measure = part.measures[measureIndex];
|
|
729
|
+
const state = createPositionState();
|
|
730
|
+
for (const entry of measure.entries) {
|
|
731
|
+
if (entry.type === "direction") {
|
|
732
|
+
for (const dirType of entry.directionTypes) {
|
|
733
|
+
if (dirType.kind === "metronome") {
|
|
734
|
+
results.push({
|
|
735
|
+
beatUnit: dirType.beatUnit,
|
|
736
|
+
perMinute: dirType.perMinute,
|
|
737
|
+
beatUnitDot: dirType.beatUnitDot,
|
|
738
|
+
direction: entry,
|
|
739
|
+
partIndex,
|
|
740
|
+
measureIndex,
|
|
741
|
+
position: state.position
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
updatePositionForEntry(state, entry);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return results;
|
|
751
|
+
}
|
|
752
|
+
function getPedalMarkings(score, options) {
|
|
753
|
+
const results = [];
|
|
754
|
+
const startPart = options?.partIndex ?? 0;
|
|
755
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
756
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
757
|
+
const part = score.parts[partIndex];
|
|
758
|
+
if (!part) continue;
|
|
759
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
760
|
+
const measure = part.measures[measureIndex];
|
|
761
|
+
const state = createPositionState();
|
|
762
|
+
for (const entry of measure.entries) {
|
|
763
|
+
if (entry.type === "direction") {
|
|
764
|
+
for (const dirType of entry.directionTypes) {
|
|
765
|
+
if (dirType.kind === "pedal") {
|
|
766
|
+
results.push({
|
|
767
|
+
pedalType: dirType.type,
|
|
768
|
+
direction: entry,
|
|
769
|
+
partIndex,
|
|
770
|
+
measureIndex,
|
|
771
|
+
position: state.position
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
updatePositionForEntry(state, entry);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return results;
|
|
781
|
+
}
|
|
782
|
+
function getWedges(score, options) {
|
|
783
|
+
const results = [];
|
|
784
|
+
const startPart = options?.partIndex ?? 0;
|
|
785
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
786
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
787
|
+
const part = score.parts[partIndex];
|
|
788
|
+
if (!part) continue;
|
|
789
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
790
|
+
const measure = part.measures[measureIndex];
|
|
791
|
+
const state = createPositionState();
|
|
792
|
+
for (const entry of measure.entries) {
|
|
793
|
+
if (entry.type === "direction") {
|
|
794
|
+
for (const dirType of entry.directionTypes) {
|
|
795
|
+
if (dirType.kind === "wedge") {
|
|
796
|
+
results.push({
|
|
797
|
+
wedgeType: dirType.type,
|
|
798
|
+
direction: entry,
|
|
799
|
+
partIndex,
|
|
800
|
+
measureIndex,
|
|
801
|
+
position: state.position
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
updatePositionForEntry(state, entry);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return results;
|
|
811
|
+
}
|
|
812
|
+
function getOctaveShifts(score, options) {
|
|
813
|
+
const results = [];
|
|
814
|
+
const startPart = options?.partIndex ?? 0;
|
|
815
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
816
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
817
|
+
const part = score.parts[partIndex];
|
|
818
|
+
if (!part) continue;
|
|
819
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
820
|
+
const measure = part.measures[measureIndex];
|
|
821
|
+
const state = createPositionState();
|
|
822
|
+
for (const entry of measure.entries) {
|
|
823
|
+
if (entry.type === "direction") {
|
|
824
|
+
for (const dirType of entry.directionTypes) {
|
|
825
|
+
if (dirType.kind === "octave-shift") {
|
|
826
|
+
results.push({
|
|
827
|
+
shiftType: dirType.type,
|
|
828
|
+
size: dirType.size,
|
|
829
|
+
direction: entry,
|
|
830
|
+
partIndex,
|
|
831
|
+
measureIndex,
|
|
832
|
+
position: state.position
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
updatePositionForEntry(state, entry);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return results;
|
|
842
|
+
}
|
|
843
|
+
function getTiedNoteGroups(score, options) {
|
|
844
|
+
const results = [];
|
|
845
|
+
const startPart = options?.partIndex ?? 0;
|
|
846
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
847
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
848
|
+
const part = score.parts[partIndex];
|
|
849
|
+
if (!part) continue;
|
|
850
|
+
const pendingTies = /* @__PURE__ */ new Map();
|
|
851
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
852
|
+
const measure = part.measures[measureIndex];
|
|
853
|
+
const state = createPositionState();
|
|
854
|
+
for (const entry of measure.entries) {
|
|
855
|
+
if (entry.type === "note" && entry.pitch) {
|
|
856
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
857
|
+
const pitchKey = `${entry.pitch.step}${entry.pitch.octave}-${entry.voice}`;
|
|
858
|
+
const context = {
|
|
859
|
+
note: entry,
|
|
860
|
+
part,
|
|
861
|
+
partIndex,
|
|
862
|
+
measure,
|
|
863
|
+
measureIndex,
|
|
864
|
+
position
|
|
865
|
+
};
|
|
866
|
+
const hasTieStart = entry.tie?.type === "start" || entry.ties?.some((t) => t.type === "start") || entry.notations?.some((n) => n.type === "tied" && n.tiedType === "start");
|
|
867
|
+
const hasTieStop = entry.tie?.type === "stop" || entry.ties?.some((t) => t.type === "stop") || entry.notations?.some((n) => n.type === "tied" && n.tiedType === "stop");
|
|
868
|
+
if (hasTieStop && pendingTies.has(pitchKey)) {
|
|
869
|
+
const group = pendingTies.get(pitchKey);
|
|
870
|
+
group.push(context);
|
|
871
|
+
if (!hasTieStart) {
|
|
872
|
+
const totalDuration = group.reduce((sum, nc) => sum + nc.note.duration, 0);
|
|
873
|
+
results.push({ notes: group, totalDuration });
|
|
874
|
+
pendingTies.delete(pitchKey);
|
|
875
|
+
}
|
|
876
|
+
} else if (hasTieStart) {
|
|
877
|
+
pendingTies.set(pitchKey, [context]);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
updatePositionForEntry(state, entry);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return results;
|
|
885
|
+
}
|
|
886
|
+
function getSlurSpans(score, options) {
|
|
887
|
+
const results = [];
|
|
888
|
+
const startPart = options?.partIndex ?? 0;
|
|
889
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
890
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
891
|
+
const part = score.parts[partIndex];
|
|
892
|
+
if (!part) continue;
|
|
893
|
+
const pendingSlurs = /* @__PURE__ */ new Map();
|
|
894
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
895
|
+
const measure = part.measures[measureIndex];
|
|
896
|
+
const state = createPositionState();
|
|
897
|
+
for (const entry of measure.entries) {
|
|
898
|
+
if (entry.type === "note") {
|
|
899
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
900
|
+
const context = {
|
|
901
|
+
note: entry,
|
|
902
|
+
part,
|
|
903
|
+
partIndex,
|
|
904
|
+
measure,
|
|
905
|
+
measureIndex,
|
|
906
|
+
position
|
|
907
|
+
};
|
|
908
|
+
if (entry.notations) {
|
|
909
|
+
for (const notation of entry.notations) {
|
|
910
|
+
if (notation.type === "slur") {
|
|
911
|
+
const slurNumber = notation.number ?? 1;
|
|
912
|
+
if (notation.slurType === "start") {
|
|
913
|
+
pendingSlurs.set(slurNumber, { startNote: context, notes: [context] });
|
|
914
|
+
} else if (notation.slurType === "stop" && pendingSlurs.has(slurNumber)) {
|
|
915
|
+
const pending = pendingSlurs.get(slurNumber);
|
|
916
|
+
pending.notes.push(context);
|
|
917
|
+
results.push({
|
|
918
|
+
number: slurNumber,
|
|
919
|
+
startNote: pending.startNote,
|
|
920
|
+
endNote: context,
|
|
921
|
+
notes: pending.notes
|
|
922
|
+
});
|
|
923
|
+
pendingSlurs.delete(slurNumber);
|
|
924
|
+
} else if (notation.slurType === "continue" && pendingSlurs.has(slurNumber)) {
|
|
925
|
+
pendingSlurs.get(slurNumber).notes.push(context);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
for (const [, pending] of pendingSlurs) {
|
|
931
|
+
const lastNote = pending.notes[pending.notes.length - 1];
|
|
932
|
+
if (lastNote !== context) {
|
|
933
|
+
pending.notes.push(context);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
updatePositionForEntry(state, entry);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return results;
|
|
942
|
+
}
|
|
943
|
+
function getTupletGroups(score, options) {
|
|
944
|
+
const results = [];
|
|
945
|
+
const startPart = options?.partIndex ?? 0;
|
|
946
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
947
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
948
|
+
const part = score.parts[partIndex];
|
|
949
|
+
if (!part) continue;
|
|
950
|
+
const pendingTuplets = /* @__PURE__ */ new Map();
|
|
951
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
952
|
+
const measure = part.measures[measureIndex];
|
|
953
|
+
const state = createPositionState();
|
|
954
|
+
for (const entry of measure.entries) {
|
|
955
|
+
if (entry.type === "note") {
|
|
956
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
957
|
+
const context = {
|
|
958
|
+
note: entry,
|
|
959
|
+
part,
|
|
960
|
+
partIndex,
|
|
961
|
+
measure,
|
|
962
|
+
measureIndex,
|
|
963
|
+
position
|
|
964
|
+
};
|
|
965
|
+
if (entry.notations) {
|
|
966
|
+
for (const notation of entry.notations) {
|
|
967
|
+
if (notation.type === "tuplet") {
|
|
968
|
+
const tupletNumber = notation.number ?? 1;
|
|
969
|
+
if (notation.tupletType === "start") {
|
|
970
|
+
const actualNotes = entry.timeModification?.actualNotes ?? 3;
|
|
971
|
+
const normalNotes = entry.timeModification?.normalNotes ?? 2;
|
|
972
|
+
pendingTuplets.set(tupletNumber, {
|
|
973
|
+
notes: [context],
|
|
974
|
+
actualNotes,
|
|
975
|
+
normalNotes
|
|
976
|
+
});
|
|
977
|
+
} else if (notation.tupletType === "stop" && pendingTuplets.has(tupletNumber)) {
|
|
978
|
+
const pending = pendingTuplets.get(tupletNumber);
|
|
979
|
+
pending.notes.push(context);
|
|
980
|
+
results.push({
|
|
981
|
+
number: tupletNumber,
|
|
982
|
+
notes: pending.notes,
|
|
983
|
+
actualNotes: pending.actualNotes,
|
|
984
|
+
normalNotes: pending.normalNotes
|
|
985
|
+
});
|
|
986
|
+
pendingTuplets.delete(tupletNumber);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (entry.timeModification && !entry.notations?.some((n) => n.type === "tuplet")) {
|
|
992
|
+
for (const [, pending] of pendingTuplets) {
|
|
993
|
+
if (entry.timeModification.actualNotes === pending.actualNotes && entry.timeModification.normalNotes === pending.normalNotes) {
|
|
994
|
+
pending.notes.push(context);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
updatePositionForEntry(state, entry);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return results;
|
|
1004
|
+
}
|
|
1005
|
+
function getBeamGroups(measure) {
|
|
1006
|
+
const results = [];
|
|
1007
|
+
const state = createPositionState();
|
|
1008
|
+
const pendingBeams = /* @__PURE__ */ new Map();
|
|
1009
|
+
for (const entry of measure.entries) {
|
|
1010
|
+
if (entry.type === "note" && entry.beam) {
|
|
1011
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
1012
|
+
const context = {
|
|
1013
|
+
note: entry,
|
|
1014
|
+
part: {},
|
|
1015
|
+
// Will be set by caller if needed
|
|
1016
|
+
partIndex: 0,
|
|
1017
|
+
measure,
|
|
1018
|
+
measureIndex: 0,
|
|
1019
|
+
position
|
|
1020
|
+
};
|
|
1021
|
+
for (const beam of entry.beam) {
|
|
1022
|
+
const beamNumber = beam.number;
|
|
1023
|
+
if (beam.type === "begin") {
|
|
1024
|
+
pendingBeams.set(beamNumber, [context]);
|
|
1025
|
+
} else if (beam.type === "continue" && pendingBeams.has(beamNumber)) {
|
|
1026
|
+
pendingBeams.get(beamNumber).push(context);
|
|
1027
|
+
} else if (beam.type === "end" && pendingBeams.has(beamNumber)) {
|
|
1028
|
+
const group = pendingBeams.get(beamNumber);
|
|
1029
|
+
group.push(context);
|
|
1030
|
+
results.push({
|
|
1031
|
+
notes: group,
|
|
1032
|
+
beamLevel: beamNumber
|
|
1033
|
+
});
|
|
1034
|
+
pendingBeams.delete(beamNumber);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
updatePositionForEntry(state, entry);
|
|
1039
|
+
}
|
|
1040
|
+
return results;
|
|
1041
|
+
}
|
|
1042
|
+
function findNotesWithNotation(score, notationType, options) {
|
|
1043
|
+
const results = [];
|
|
1044
|
+
const startPart = options?.partIndex ?? 0;
|
|
1045
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1046
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1047
|
+
const part = score.parts[partIndex];
|
|
1048
|
+
if (!part) continue;
|
|
1049
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1050
|
+
const measure = part.measures[measureIndex];
|
|
1051
|
+
const state = createPositionState();
|
|
1052
|
+
for (const entry of measure.entries) {
|
|
1053
|
+
if (entry.type === "note") {
|
|
1054
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
1055
|
+
if (entry.notations?.some((n) => n.type === notationType)) {
|
|
1056
|
+
results.push({
|
|
1057
|
+
note: entry,
|
|
1058
|
+
part,
|
|
1059
|
+
partIndex,
|
|
1060
|
+
measure,
|
|
1061
|
+
measureIndex,
|
|
1062
|
+
position
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
updatePositionForEntry(state, entry);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return results;
|
|
1071
|
+
}
|
|
1072
|
+
function getHarmonies(score, options) {
|
|
1073
|
+
const results = [];
|
|
1074
|
+
const startPart = options?.partIndex ?? 0;
|
|
1075
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1076
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1077
|
+
const part = score.parts[partIndex];
|
|
1078
|
+
if (!part) continue;
|
|
1079
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1080
|
+
const measure = part.measures[measureIndex];
|
|
1081
|
+
const state = createPositionState();
|
|
1082
|
+
for (const entry of measure.entries) {
|
|
1083
|
+
if (entry.type === "harmony") {
|
|
1084
|
+
results.push({
|
|
1085
|
+
harmony: entry,
|
|
1086
|
+
part,
|
|
1087
|
+
partIndex,
|
|
1088
|
+
measure,
|
|
1089
|
+
measureIndex,
|
|
1090
|
+
position: state.position
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
updatePositionForEntry(state, entry);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return results;
|
|
1098
|
+
}
|
|
1099
|
+
function getHarmonyAtPosition(measure, position) {
|
|
1100
|
+
const state = createPositionState();
|
|
1101
|
+
let lastHarmony;
|
|
1102
|
+
for (const entry of measure.entries) {
|
|
1103
|
+
if (entry.type === "harmony") {
|
|
1104
|
+
if (state.position <= position) {
|
|
1105
|
+
lastHarmony = entry;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
updatePositionForEntry(state, entry);
|
|
1109
|
+
if (state.position > position && lastHarmony) {
|
|
1110
|
+
break;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return lastHarmony;
|
|
1114
|
+
}
|
|
1115
|
+
function getChordProgression(score, options) {
|
|
1116
|
+
const harmonies = getHarmonies(score, options);
|
|
1117
|
+
return harmonies.map((h) => {
|
|
1118
|
+
const rootAlter = h.harmony.root.rootAlter ?? 0;
|
|
1119
|
+
const rootAccidental = rootAlter > 0 ? "#".repeat(rootAlter) : "b".repeat(-rootAlter);
|
|
1120
|
+
const root = h.harmony.root.rootStep + rootAccidental;
|
|
1121
|
+
let bass;
|
|
1122
|
+
if (h.harmony.bass) {
|
|
1123
|
+
const bassAlter = h.harmony.bass.bassAlter ?? 0;
|
|
1124
|
+
const bassAccidental = bassAlter > 0 ? "#".repeat(bassAlter) : "b".repeat(-bassAlter);
|
|
1125
|
+
bass = h.harmony.bass.bassStep + bassAccidental;
|
|
1126
|
+
}
|
|
1127
|
+
return {
|
|
1128
|
+
root,
|
|
1129
|
+
kind: h.harmony.kind,
|
|
1130
|
+
bass,
|
|
1131
|
+
measureIndex: h.measureIndex,
|
|
1132
|
+
position: h.position
|
|
1133
|
+
};
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
function getLyrics(score, options) {
|
|
1137
|
+
const results = [];
|
|
1138
|
+
const startPart = options?.partIndex ?? 0;
|
|
1139
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1140
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1141
|
+
const part = score.parts[partIndex];
|
|
1142
|
+
if (!part) continue;
|
|
1143
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1144
|
+
const measure = part.measures[measureIndex];
|
|
1145
|
+
const state = createPositionState();
|
|
1146
|
+
for (const entry of measure.entries) {
|
|
1147
|
+
if (entry.type === "note" && entry.lyrics) {
|
|
1148
|
+
const position = entry.chord ? state.lastNonChordPosition : state.position;
|
|
1149
|
+
for (const lyric of entry.lyrics) {
|
|
1150
|
+
const verse = lyric.number ?? 1;
|
|
1151
|
+
if (options?.verse !== void 0 && verse !== options.verse) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
results.push({
|
|
1155
|
+
lyric,
|
|
1156
|
+
note: entry,
|
|
1157
|
+
part,
|
|
1158
|
+
partIndex,
|
|
1159
|
+
measure,
|
|
1160
|
+
measureIndex,
|
|
1161
|
+
position,
|
|
1162
|
+
verse
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
updatePositionForEntry(state, entry);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return results;
|
|
1171
|
+
}
|
|
1172
|
+
function getLyricText(score, options) {
|
|
1173
|
+
const lyrics = getLyrics(score, options);
|
|
1174
|
+
const verseMap = /* @__PURE__ */ new Map();
|
|
1175
|
+
for (const lyric of lyrics) {
|
|
1176
|
+
if (!verseMap.has(lyric.verse)) {
|
|
1177
|
+
verseMap.set(lyric.verse, []);
|
|
1178
|
+
}
|
|
1179
|
+
verseMap.get(lyric.verse).push(lyric);
|
|
1180
|
+
}
|
|
1181
|
+
const results = [];
|
|
1182
|
+
for (const [verse, verseLyrics] of verseMap) {
|
|
1183
|
+
const syllables = [];
|
|
1184
|
+
const textParts = [];
|
|
1185
|
+
for (const lyric of verseLyrics) {
|
|
1186
|
+
const text = lyric.lyric.text;
|
|
1187
|
+
syllables.push({
|
|
1188
|
+
text,
|
|
1189
|
+
position: lyric.position,
|
|
1190
|
+
measureIndex: lyric.measureIndex
|
|
1191
|
+
});
|
|
1192
|
+
const syllabic = lyric.lyric.syllabic;
|
|
1193
|
+
if (syllabic === "begin" || syllabic === "middle") {
|
|
1194
|
+
textParts.push(text + "-");
|
|
1195
|
+
} else if (syllabic === "end") {
|
|
1196
|
+
textParts.push(text + " ");
|
|
1197
|
+
} else {
|
|
1198
|
+
textParts.push(text + " ");
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
results.push({
|
|
1202
|
+
verse,
|
|
1203
|
+
text: textParts.join("").trim(),
|
|
1204
|
+
syllables
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
return results.sort((a, b) => a.verse - b.verse);
|
|
1208
|
+
}
|
|
1209
|
+
function getVerseCount(score, options) {
|
|
1210
|
+
const verses = /* @__PURE__ */ new Set();
|
|
1211
|
+
const startPart = options?.partIndex ?? 0;
|
|
1212
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1213
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1214
|
+
const part = score.parts[partIndex];
|
|
1215
|
+
if (!part) continue;
|
|
1216
|
+
for (const measure of part.measures) {
|
|
1217
|
+
for (const entry of measure.entries) {
|
|
1218
|
+
if (entry.type === "note" && entry.lyrics) {
|
|
1219
|
+
for (const lyric of entry.lyrics) {
|
|
1220
|
+
verses.add(lyric.number ?? 1);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return verses.size;
|
|
1227
|
+
}
|
|
1228
|
+
function getRepeatStructure(score, options) {
|
|
1229
|
+
const results = [];
|
|
1230
|
+
const partIndex = options?.partIndex ?? 0;
|
|
1231
|
+
const part = score.parts[partIndex];
|
|
1232
|
+
if (!part) return results;
|
|
1233
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1234
|
+
const measure = part.measures[measureIndex];
|
|
1235
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1236
|
+
if (measure.barlines) {
|
|
1237
|
+
for (const barline of measure.barlines) {
|
|
1238
|
+
if (barline.repeat) {
|
|
1239
|
+
results.push({
|
|
1240
|
+
type: barline.repeat.direction,
|
|
1241
|
+
times: barline.repeat.times,
|
|
1242
|
+
measureIndex,
|
|
1243
|
+
measureNumber
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return results;
|
|
1250
|
+
}
|
|
1251
|
+
function findBarlines(score, options) {
|
|
1252
|
+
const results = [];
|
|
1253
|
+
const startPart = options?.partIndex ?? 0;
|
|
1254
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1255
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1256
|
+
const part = score.parts[partIndex];
|
|
1257
|
+
if (!part) continue;
|
|
1258
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1259
|
+
const measure = part.measures[measureIndex];
|
|
1260
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1261
|
+
if (measure.barlines) {
|
|
1262
|
+
for (const barline of measure.barlines) {
|
|
1263
|
+
if (options?.style !== void 0 && barline.barStyle !== options.style) {
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
if (options?.repeat !== void 0) {
|
|
1267
|
+
const hasRepeat = barline.repeat !== void 0;
|
|
1268
|
+
if (hasRepeat !== options.repeat) {
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
results.push({
|
|
1273
|
+
barline,
|
|
1274
|
+
partIndex,
|
|
1275
|
+
measureIndex,
|
|
1276
|
+
measureNumber
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return results;
|
|
1283
|
+
}
|
|
1284
|
+
function getEndings(score, options) {
|
|
1285
|
+
const results = [];
|
|
1286
|
+
const startPart = options?.partIndex ?? 0;
|
|
1287
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1288
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1289
|
+
const part = score.parts[partIndex];
|
|
1290
|
+
if (!part) continue;
|
|
1291
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1292
|
+
const measure = part.measures[measureIndex];
|
|
1293
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1294
|
+
if (measure.barlines) {
|
|
1295
|
+
for (const barline of measure.barlines) {
|
|
1296
|
+
if (barline.ending) {
|
|
1297
|
+
results.push({
|
|
1298
|
+
number: barline.ending.number,
|
|
1299
|
+
type: barline.ending.type,
|
|
1300
|
+
partIndex,
|
|
1301
|
+
measureIndex,
|
|
1302
|
+
measureNumber
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return results;
|
|
1310
|
+
}
|
|
1311
|
+
function getKeyChanges(score, options) {
|
|
1312
|
+
const results = [];
|
|
1313
|
+
const startPart = options?.partIndex ?? 0;
|
|
1314
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1315
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1316
|
+
const part = score.parts[partIndex];
|
|
1317
|
+
if (!part) continue;
|
|
1318
|
+
let lastKey;
|
|
1319
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1320
|
+
const measure = part.measures[measureIndex];
|
|
1321
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1322
|
+
const state = createPositionState();
|
|
1323
|
+
if (measure.attributes?.key) {
|
|
1324
|
+
const key = measure.attributes.key;
|
|
1325
|
+
if (!lastKey || lastKey.fifths !== key.fifths || lastKey.mode !== key.mode) {
|
|
1326
|
+
results.push({
|
|
1327
|
+
key,
|
|
1328
|
+
partIndex,
|
|
1329
|
+
measureIndex,
|
|
1330
|
+
measureNumber,
|
|
1331
|
+
position: 0
|
|
1332
|
+
});
|
|
1333
|
+
lastKey = key;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
for (const entry of measure.entries) {
|
|
1337
|
+
if (entry.type === "attributes" && entry.attributes.key) {
|
|
1338
|
+
const key = entry.attributes.key;
|
|
1339
|
+
if (!lastKey || lastKey.fifths !== key.fifths || lastKey.mode !== key.mode) {
|
|
1340
|
+
results.push({
|
|
1341
|
+
key,
|
|
1342
|
+
partIndex,
|
|
1343
|
+
measureIndex,
|
|
1344
|
+
measureNumber,
|
|
1345
|
+
position: state.position
|
|
1346
|
+
});
|
|
1347
|
+
lastKey = key;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
updatePositionForEntry(state, entry);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return results;
|
|
1355
|
+
}
|
|
1356
|
+
function getTimeChanges(score, options) {
|
|
1357
|
+
const results = [];
|
|
1358
|
+
const startPart = options?.partIndex ?? 0;
|
|
1359
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1360
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1361
|
+
const part = score.parts[partIndex];
|
|
1362
|
+
if (!part) continue;
|
|
1363
|
+
let lastTime;
|
|
1364
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1365
|
+
const measure = part.measures[measureIndex];
|
|
1366
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1367
|
+
if (measure.attributes?.time) {
|
|
1368
|
+
const time = measure.attributes.time;
|
|
1369
|
+
if (!lastTime || lastTime.beats !== time.beats || lastTime.beatType !== time.beatType) {
|
|
1370
|
+
results.push({
|
|
1371
|
+
time,
|
|
1372
|
+
partIndex,
|
|
1373
|
+
measureIndex,
|
|
1374
|
+
measureNumber
|
|
1375
|
+
});
|
|
1376
|
+
lastTime = time;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
for (const entry of measure.entries) {
|
|
1380
|
+
if (entry.type === "attributes" && entry.attributes.time) {
|
|
1381
|
+
const time = entry.attributes.time;
|
|
1382
|
+
if (!lastTime || lastTime.beats !== time.beats || lastTime.beatType !== time.beatType) {
|
|
1383
|
+
results.push({
|
|
1384
|
+
time,
|
|
1385
|
+
partIndex,
|
|
1386
|
+
measureIndex,
|
|
1387
|
+
measureNumber
|
|
1388
|
+
});
|
|
1389
|
+
lastTime = time;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return results;
|
|
1396
|
+
}
|
|
1397
|
+
function getClefChanges(score, options) {
|
|
1398
|
+
const results = [];
|
|
1399
|
+
const startPart = options?.partIndex ?? 0;
|
|
1400
|
+
const endPart = options?.partIndex !== void 0 ? options.partIndex + 1 : score.parts.length;
|
|
1401
|
+
for (let partIndex = startPart; partIndex < endPart; partIndex++) {
|
|
1402
|
+
const part = score.parts[partIndex];
|
|
1403
|
+
if (!part) continue;
|
|
1404
|
+
const lastClefs = /* @__PURE__ */ new Map();
|
|
1405
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
1406
|
+
const measure = part.measures[measureIndex];
|
|
1407
|
+
const measureNumber = measure.number ?? String(measureIndex + 1);
|
|
1408
|
+
const state = createPositionState();
|
|
1409
|
+
if (measure.attributes?.clef) {
|
|
1410
|
+
for (const clef of measure.attributes.clef) {
|
|
1411
|
+
const staff = clef.staff ?? 1;
|
|
1412
|
+
if (options?.staff !== void 0 && staff !== options.staff) {
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
const lastClef = lastClefs.get(staff);
|
|
1416
|
+
if (!lastClef || lastClef.sign !== clef.sign || lastClef.line !== clef.line || lastClef.clefOctaveChange !== clef.clefOctaveChange) {
|
|
1417
|
+
results.push({
|
|
1418
|
+
clef,
|
|
1419
|
+
staff,
|
|
1420
|
+
partIndex,
|
|
1421
|
+
measureIndex,
|
|
1422
|
+
measureNumber,
|
|
1423
|
+
position: 0
|
|
1424
|
+
});
|
|
1425
|
+
lastClefs.set(staff, clef);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
for (const entry of measure.entries) {
|
|
1430
|
+
if (entry.type === "attributes" && entry.attributes.clef) {
|
|
1431
|
+
for (const clef of entry.attributes.clef) {
|
|
1432
|
+
const staff = clef.staff ?? 1;
|
|
1433
|
+
if (options?.staff !== void 0 && staff !== options.staff) {
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
const lastClef = lastClefs.get(staff);
|
|
1437
|
+
if (!lastClef || lastClef.sign !== clef.sign || lastClef.line !== clef.line || lastClef.clefOctaveChange !== clef.clefOctaveChange) {
|
|
1438
|
+
results.push({
|
|
1439
|
+
clef,
|
|
1440
|
+
staff,
|
|
1441
|
+
partIndex,
|
|
1442
|
+
measureIndex,
|
|
1443
|
+
measureNumber,
|
|
1444
|
+
position: state.position
|
|
1445
|
+
});
|
|
1446
|
+
lastClefs.set(staff, clef);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
updatePositionForEntry(state, entry);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return results;
|
|
1455
|
+
}
|
|
1456
|
+
function getStructuralChanges(score, options) {
|
|
1457
|
+
return {
|
|
1458
|
+
keyChanges: getKeyChanges(score, options),
|
|
1459
|
+
timeChanges: getTimeChanges(score, options),
|
|
1460
|
+
clefChanges: getClefChanges(score, options)
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function getPartByIndex(score, index) {
|
|
1464
|
+
return score.parts[index];
|
|
1465
|
+
}
|
|
1466
|
+
function getPartCount(score) {
|
|
1467
|
+
return score.parts.length;
|
|
1468
|
+
}
|
|
1469
|
+
function getPartIds(score) {
|
|
1470
|
+
return score.parts.map((part) => part.id);
|
|
1471
|
+
}
|
|
221
1472
|
// Annotate the CommonJS export names for ESM import in node:
|
|
222
1473
|
0 && (module.exports = {
|
|
1474
|
+
buildVoiceToStaffMap,
|
|
1475
|
+
buildVoiceToStaffMapForPart,
|
|
1476
|
+
findBarlines,
|
|
1477
|
+
findDirectionsByType,
|
|
1478
|
+
findNotesWithNotation,
|
|
223
1479
|
getAbsolutePosition,
|
|
1480
|
+
getAdjacentNotes,
|
|
224
1481
|
getAllNotes,
|
|
1482
|
+
getBeamGroups,
|
|
1483
|
+
getChordProgression,
|
|
225
1484
|
getChords,
|
|
1485
|
+
getClefChanges,
|
|
1486
|
+
getClefForStaff,
|
|
1487
|
+
getDirections,
|
|
1488
|
+
getDirectionsAtPosition,
|
|
1489
|
+
getDynamics,
|
|
1490
|
+
getEffectiveStaff,
|
|
1491
|
+
getEndings,
|
|
1492
|
+
getEntriesAtPosition,
|
|
1493
|
+
getEntriesForStaff,
|
|
1494
|
+
getEntriesInRange,
|
|
1495
|
+
getHarmonies,
|
|
1496
|
+
getHarmonyAtPosition,
|
|
1497
|
+
getKeyChanges,
|
|
1498
|
+
getLyricText,
|
|
1499
|
+
getLyrics,
|
|
1500
|
+
getNextNote,
|
|
226
1501
|
getNormalizedDuration,
|
|
227
1502
|
getNormalizedPosition,
|
|
1503
|
+
getNotesAtPosition,
|
|
228
1504
|
getNotesForStaff,
|
|
229
1505
|
getNotesForVoice,
|
|
1506
|
+
getNotesInRange,
|
|
1507
|
+
getOctaveShifts,
|
|
1508
|
+
getPartByIndex,
|
|
1509
|
+
getPartCount,
|
|
1510
|
+
getPartIds,
|
|
1511
|
+
getPedalMarkings,
|
|
1512
|
+
getPrevNote,
|
|
1513
|
+
getRepeatStructure,
|
|
1514
|
+
getSlurSpans,
|
|
1515
|
+
getStaffRange,
|
|
230
1516
|
getStaves,
|
|
1517
|
+
getStructuralChanges,
|
|
1518
|
+
getTempoMarkings,
|
|
1519
|
+
getTiedNoteGroups,
|
|
1520
|
+
getTimeChanges,
|
|
1521
|
+
getTupletGroups,
|
|
1522
|
+
getVerseCount,
|
|
1523
|
+
getVerticalSlice,
|
|
1524
|
+
getVoiceLine,
|
|
1525
|
+
getVoiceLineInRange,
|
|
231
1526
|
getVoices,
|
|
1527
|
+
getVoicesForStaff,
|
|
1528
|
+
getWedges,
|
|
232
1529
|
groupByStaff,
|
|
233
1530
|
groupByVoice,
|
|
234
1531
|
hasNotes,
|
|
1532
|
+
inferStaff,
|
|
235
1533
|
isRestMeasure,
|
|
1534
|
+
iterateEntries,
|
|
236
1535
|
iterateNotes,
|
|
237
1536
|
withAbsolutePositions
|
|
238
1537
|
});
|
|
239
|
-
//# sourceMappingURL=index.js.map
|