@viritura/score-engine 0.1.1-next.27 → 0.1.1-next.29

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/dist/index.js CHANGED
@@ -582,6 +582,230 @@ function parseChordSymbolText(input, position) {
582
582
  return (!parsedQuality || parsedQuality.modifiers) && (chord.kindText = match[4]), parsedQuality?.extension !== void 0 && (chord.extension = parsedQuality.extension), match[5] && (chord.bass = parseRoot(match[5], match[6] ?? "")), chord;
583
583
  }
584
584
 
585
+ // packages/core/src/operations/lyricExtenders.ts
586
+ init_define_import_meta_env();
587
+ function durationBeats(duration) {
588
+ let base = DURATION_BEATS[duration.base], dots = 1;
589
+ for (let index = 0; index < (duration.dots ?? 0); index++) dots += 0.5 ** (index + 1);
590
+ return base * dots;
591
+ }
592
+ function quantityBeats(quantity) {
593
+ return durationBeats(quantity.duration) * quantity.multiple;
594
+ }
595
+ function walkTimedContent(content, startBeat, scale, partIndex, sequenceIndex, voice, staff, measureIndex, output, ordinal) {
596
+ let cursor = startBeat;
597
+ for (let item of content) {
598
+ if (item.type === "event") {
599
+ let duration = durationBeats(item.duration) * scale;
600
+ output.push({
601
+ event: item,
602
+ partIndex,
603
+ sequenceIndex,
604
+ voice,
605
+ staff,
606
+ measureIndex,
607
+ startBeat: cursor,
608
+ durationBeats: duration,
609
+ rest: isRest(item),
610
+ ordinal: ordinal.value++
611
+ }), cursor += duration;
612
+ continue;
613
+ }
614
+ if (item.type === "space") {
615
+ cursor += 4 * item.duration[0] / item.duration[1] * scale;
616
+ continue;
617
+ }
618
+ if (item.type === "grace") {
619
+ for (let event of item.content)
620
+ output.push({
621
+ event,
622
+ partIndex,
623
+ sequenceIndex,
624
+ voice,
625
+ staff,
626
+ measureIndex,
627
+ startBeat: cursor,
628
+ durationBeats: 0,
629
+ rest: isRest(event),
630
+ ordinal: ordinal.value++
631
+ });
632
+ continue;
633
+ }
634
+ if (item.type === "tuplet") {
635
+ let inner = quantityBeats(item.inner), ratio = inner > 0 ? quantityBeats(item.outer) / inner : 1;
636
+ cursor = walkTimedContent(
637
+ item.content,
638
+ cursor,
639
+ scale * ratio,
640
+ partIndex,
641
+ sequenceIndex,
642
+ voice,
643
+ staff,
644
+ measureIndex,
645
+ output,
646
+ ordinal
647
+ );
648
+ continue;
649
+ }
650
+ let subdivision = item.content.length ? quantityBeats(item.outer) * scale / item.content.length : 0;
651
+ for (let event of item.content)
652
+ output.push({
653
+ event,
654
+ partIndex,
655
+ sequenceIndex,
656
+ voice,
657
+ staff,
658
+ measureIndex,
659
+ startBeat: cursor,
660
+ durationBeats: subdivision,
661
+ rest: isRest(event),
662
+ ordinal: ordinal.value++
663
+ }), cursor += subdivision;
664
+ }
665
+ return cursor;
666
+ }
667
+ function collectEvents(score) {
668
+ let offsets = [], measureOffset = 0, measureCount = Math.max(score.global.measures.length, ...score.parts.map((part) => part.measures.length)), activeMeter = { count: 4, unit: 4 }, meters = [];
669
+ for (let index = 0; index < measureCount; index++) {
670
+ let measure = score.global.measures[index];
671
+ measure?.time && (activeMeter = measure.time), meters.push(activeMeter), offsets.push(measureOffset), measureOffset += measureBeats(activeMeter);
672
+ }
673
+ let events = [], writtenSpans = Array(measureCount).fill(0), ordinal = { value: 0 };
674
+ for (let partIndex = 0; partIndex < score.parts.length; partIndex++) {
675
+ let part = score.parts[partIndex], { table } = resolveStaffMeterTimeline(
676
+ part.measures.map((measure) => measure.staffMeters),
677
+ meters
678
+ );
679
+ for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
680
+ let measure = part.measures[measureIndex], start = offsets[measureIndex] ?? measureIndex * 4;
681
+ measure.sequences.forEach((sequence, sequenceIndex) => {
682
+ if (sequence.fullMeasure) {
683
+ events.push({
684
+ event: { type: "event", duration: sequence.fullMeasure.visualDuration, rest: {} },
685
+ partIndex,
686
+ sequenceIndex,
687
+ voice: sequence.voice,
688
+ staff: sequence.staff ?? 1,
689
+ measureIndex,
690
+ startBeat: start,
691
+ durationBeats: measureBeats(meters[measureIndex]),
692
+ rest: !0,
693
+ ordinal: ordinal.value++
694
+ });
695
+ return;
696
+ }
697
+ let end = walkTimedContent(
698
+ sequence.content,
699
+ start,
700
+ table[measureIndex]?.get(sequence.staff ?? 1) ? ratioToNumber(table[measureIndex].get(sequence.staff ?? 1).ratioToGlobal) : 1,
701
+ partIndex,
702
+ sequenceIndex,
703
+ sequence.voice,
704
+ sequence.staff ?? 1,
705
+ measureIndex,
706
+ events,
707
+ ordinal
708
+ );
709
+ sequence.fullMeasure || (writtenSpans[measureIndex] = Math.max(writtenSpans[measureIndex], end - start));
710
+ });
711
+ }
712
+ }
713
+ let correction = 0, corrections = [];
714
+ for (let index = 0; index < measureCount; index++) {
715
+ let nominal = measureBeats(meters[index]), measure = score.global.measures[index], written = writtenSpans[index];
716
+ corrections.push(correction), index === 0 && measure?.number === 0 && written > 0 ? correction += written - nominal : measure?.time?.display === "senzaMisura" && (correction += Math.max(nominal, written) - nominal);
717
+ }
718
+ for (let event of events) event.startBeat += corrections[event.measureIndex];
719
+ return events;
720
+ }
721
+ function hasLyrics(event, lineId) {
722
+ return event.event.lyrics?.lines?.[lineId] !== void 0;
723
+ }
724
+ function isEligibleSyllable(line) {
725
+ return line.type !== "start" && line.type !== "middle";
726
+ }
727
+ function sameLyricVoice(source, candidate) {
728
+ return source.partIndex === candidate.partIndex && source.staff === candidate.staff && (source.voice !== void 0 ? source.voice === candidate.voice : candidate.voice === void 0 && source.sequenceIndex === candidate.sequenceIndex);
729
+ }
730
+ function inferredTarget(source, lineId, events) {
731
+ let continuation = events.filter((event) => sameLyricVoice(source, event) && event.ordinal > source.ordinal).sort((left, right) => left.ordinal - right.ordinal), target;
732
+ for (let event of continuation) {
733
+ if (hasLyrics(event, lineId) || event.rest) break;
734
+ target = event;
735
+ }
736
+ return target;
737
+ }
738
+ function crossesLaterSyllable(source, target, lineId, events) {
739
+ return events.some(
740
+ (candidate) => sameLyricVoice(source, candidate) && candidate.ordinal > source.ordinal && candidate.startBeat <= target.startBeat + 1e-9 && candidate.ordinal !== target.ordinal && hasLyrics(candidate, lineId)
741
+ );
742
+ }
743
+ function resolveLyricExtenders(score) {
744
+ let events = collectEvents(score), byId = /* @__PURE__ */ new Map();
745
+ for (let event of events)
746
+ event.event.id && !byId.has(event.event.id) && byId.set(event.event.id, event);
747
+ let extenders = [], diagnostics = [], explicitPolicy = score.mnx.support?.useLyricExtenders === !0;
748
+ for (let source of events)
749
+ for (let [lineId, line] of Object.entries(source.event.lyrics?.lines ?? {})) {
750
+ let explicit = line.extender !== void 0;
751
+ if (line.extender?.show === !1 || !explicit && explicitPolicy || !explicit && !isEligibleSyllable(line))
752
+ continue;
753
+ let target;
754
+ if (line.extender?.target !== void 0) {
755
+ if (target = byId.get(line.extender.target), !target) {
756
+ diagnostics.push({
757
+ code: "missing-target",
758
+ sourceEventId: source.event.id,
759
+ lineId,
760
+ targetEventId: line.extender.target,
761
+ partIndex: source.partIndex,
762
+ measureIndex: source.measureIndex,
763
+ sequenceIndex: source.sequenceIndex
764
+ });
765
+ continue;
766
+ }
767
+ if (target.startBeat < source.startBeat - 1e-9) {
768
+ diagnostics.push({
769
+ code: "backward-target",
770
+ sourceEventId: source.event.id,
771
+ lineId,
772
+ targetEventId: line.extender.target,
773
+ partIndex: source.partIndex,
774
+ measureIndex: source.measureIndex,
775
+ sequenceIndex: source.sequenceIndex
776
+ });
777
+ continue;
778
+ }
779
+ if (target.ordinal !== source.ordinal && hasLyrics(target, lineId) || crossesLaterSyllable(source, target, lineId, events)) {
780
+ diagnostics.push({
781
+ code: "conflicting-target",
782
+ sourceEventId: source.event.id,
783
+ lineId,
784
+ targetEventId: line.extender.target,
785
+ partIndex: source.partIndex,
786
+ measureIndex: source.measureIndex,
787
+ sequenceIndex: source.sequenceIndex
788
+ });
789
+ continue;
790
+ }
791
+ } else
792
+ target = inferredTarget(source, lineId, events);
793
+ target && extenders.push({
794
+ sourceEventId: source.event.id,
795
+ targetEventId: target.event.id,
796
+ partIndex: source.partIndex,
797
+ sequenceIndex: source.sequenceIndex,
798
+ lineId,
799
+ startMeasure: source.measureIndex,
800
+ endMeasure: target.measureIndex,
801
+ startBeat: source.startBeat,
802
+ endBeat: target.startBeat + target.durationBeats,
803
+ explicit
804
+ });
805
+ }
806
+ return { extenders, diagnostics };
807
+ }
808
+
585
809
  // packages/core/src/diagnostics.ts
586
810
  init_define_import_meta_env();
587
811
  var DiagnosticCollector = class {
@@ -1989,9 +2213,9 @@ function interpolateBeatAnchors(bounds, beat, endX) {
1989
2213
 
1990
2214
  // packages/score-engine/src/buildInfo.ts
1991
2215
  init_define_import_meta_env();
1992
- var PACKAGE_VERSION = "0.1.1-next.27", BUNDLE_WORKER_FILE = "score-engine.worker.js";
2216
+ var PACKAGE_VERSION = "0.1.1-next.29", BUNDLE_WORKER_FILE = "score-engine.worker.js";
1993
2217
  function buildCommit() {
1994
- return "d8a389281839ea20c0cf0bd2b087c1fc253b43f5";
2218
+ return "861ac5447e15b8f748d78775a893a6bda2f11d6d";
1995
2219
  }
1996
2220
  function bundledAssetBase() {
1997
2221
  let moduleUrl = import.meta.url;
@@ -15105,7 +15329,7 @@ function parseLyrics(raw) {
15105
15329
  let linesObj = raw.lines, lines = {};
15106
15330
  for (let key of Object.keys(linesObj)) {
15107
15331
  let lineRaw = linesObj[key], line = { text: lineRaw.text };
15108
- lineRaw.type !== void 0 && (line.type = lineRaw.type), lines[key] = line;
15332
+ lineRaw.type !== void 0 && (line.type = lineRaw.type), lineRaw.extender !== void 0 && (line.extender = lineRaw.extender), lines[key] = line;
15109
15333
  }
15110
15334
  lyrics.lines = lines;
15111
15335
  }
@@ -15471,6 +15695,130 @@ function parseSystemDefinition(raw) {
15471
15695
  // packages/format/src/mnx/diagnostics.ts
15472
15696
  init_define_import_meta_env();
15473
15697
 
15698
+ // packages/format/src/mnx/knownFields.ts
15699
+ init_define_import_meta_env();
15700
+ var KEYS = {
15701
+ root: /* @__PURE__ */ new Set(["mnx", "global", "parts", "layouts", "scores", "_x"]),
15702
+ mnxMeta: /* @__PURE__ */ new Set(["version", "support"]),
15703
+ support: /* @__PURE__ */ new Set(["useAccidentalDisplay", "useBeams", "useLyricExtenders"]),
15704
+ global: /* @__PURE__ */ new Set(["measures", "lyrics"]),
15705
+ globalLyrics: /* @__PURE__ */ new Set(["lineMetadata", "lineOrder"]),
15706
+ globalMeasure: /* @__PURE__ */ new Set([
15707
+ "id",
15708
+ "number",
15709
+ "time",
15710
+ "key",
15711
+ "barline",
15712
+ "repeatStart",
15713
+ "repeatEnd",
15714
+ "ending",
15715
+ "tempos",
15716
+ "segno",
15717
+ "coda",
15718
+ "caesura",
15719
+ "fine",
15720
+ "jump",
15721
+ "_x"
15722
+ ]),
15723
+ time: /* @__PURE__ */ new Set(["count", "unit", "display", "_x"]),
15724
+ key: /* @__PURE__ */ new Set(["fifths", "color", "_x"]),
15725
+ barline: /* @__PURE__ */ new Set(["type"]),
15726
+ ending: /* @__PURE__ */ new Set(["duration", "numbers", "open", "color"]),
15727
+ tempo: /* @__PURE__ */ new Set(["bpm", "value", "location", "_x"]),
15728
+ tempoNoteValue: /* @__PURE__ */ new Set(["base", "dots"]),
15729
+ rhythmicPosition: /* @__PURE__ */ new Set(["fraction"]),
15730
+ segno: /* @__PURE__ */ new Set(["location", "glyph", "color"]),
15731
+ fine: /* @__PURE__ */ new Set(["location", "color"]),
15732
+ jump: /* @__PURE__ */ new Set(["type", "location"]),
15733
+ part: /* @__PURE__ */ new Set(["id", "name", "shortName", "staves", "transposition", "measures", "_x"]),
15734
+ transposition: /* @__PURE__ */ new Set(["interval", "keyFifthsFlipAt", "prefersWrittenPitches"]),
15735
+ partMeasure: /* @__PURE__ */ new Set([
15736
+ "sequences",
15737
+ "clefs",
15738
+ "staffConfigs",
15739
+ "arpeggios",
15740
+ "nonArpeggios",
15741
+ "beams",
15742
+ "dynamics",
15743
+ "ottavas",
15744
+ "measureRepeat",
15745
+ "_x"
15746
+ ]),
15747
+ positionedStaffConfig: /* @__PURE__ */ new Set(["config", "position", "staff", "id", "_c", "_x"]),
15748
+ staffConfig: /* @__PURE__ */ new Set(["lines", "id", "_c", "_x"]),
15749
+ measureRepeat: /* @__PURE__ */ new Set(["number", "counter", "displayNumber", "staffPosition", "_c", "_x"]),
15750
+ measureRepeatCounter: /* @__PURE__ */ new Set(["count", "placement", "_c", "_x"]),
15751
+ arpeggio: /* @__PURE__ */ new Set(["id", "position", "span", "direction", "arrow", "_c", "_x"]),
15752
+ nonArpeggio: /* @__PURE__ */ new Set(["id", "position", "span", "_c", "_x"]),
15753
+ idPair: /* @__PURE__ */ new Set(["start", "end"]),
15754
+ positionedClef: /* @__PURE__ */ new Set(["clef", "position", "staff"]),
15755
+ clef: /* @__PURE__ */ new Set(["sign", "staffPosition", "color", "glyph", "octave", "showOctave", "hide"]),
15756
+ beam: /* @__PURE__ */ new Set(["events", "beams", "direction"]),
15757
+ dynamic: /* @__PURE__ */ new Set(["position", "value", "glyph", "staff", "voice"]),
15758
+ ottava: /* @__PURE__ */ new Set(["position", "end", "value", "staff", "voice"]),
15759
+ measureRhythmicPosition: /* @__PURE__ */ new Set(["measure", "position"]),
15760
+ sequence: /* @__PURE__ */ new Set(["content", "fullMeasure", "staff", "voice", "directionHint"]),
15761
+ fullMeasure: /* @__PURE__ */ new Set(["visualDuration", "staffPosition"]),
15762
+ duration: /* @__PURE__ */ new Set(["base", "dots"]),
15763
+ event: /* @__PURE__ */ new Set([
15764
+ "type",
15765
+ "id",
15766
+ "duration",
15767
+ "staff",
15768
+ "rest",
15769
+ "notes",
15770
+ "slurs",
15771
+ "markings",
15772
+ "fermata",
15773
+ "lyrics",
15774
+ "stemDirection",
15775
+ "smufl",
15776
+ "_x"
15777
+ ]),
15778
+ eventLyrics: /* @__PURE__ */ new Set(["lines"]),
15779
+ eventLyricLine: /* @__PURE__ */ new Set(["type", "text", "extender"]),
15780
+ eventLyricLineExtender: /* @__PURE__ */ new Set(["show", "target", "id"]),
15781
+ rest: /* @__PURE__ */ new Set(["staffPosition"]),
15782
+ note: /* @__PURE__ */ new Set(["pitch", "id", "ties", "accidentalDisplay", "written", "markings"]),
15783
+ pitch: /* @__PURE__ */ new Set(["step", "octave", "alter"]),
15784
+ tie: /* @__PURE__ */ new Set(["target", "targetType", "side", "lv"]),
15785
+ accidentalDisplay: /* @__PURE__ */ new Set(["show", "force", "enclosure"]),
15786
+ accidentalEnclosure: /* @__PURE__ */ new Set(["symbol"]),
15787
+ written: /* @__PURE__ */ new Set(["diatonicDelta"]),
15788
+ slur: /* @__PURE__ */ new Set(["target", "side", "sideEnd", "lineType", "startNote", "endNote"]),
15789
+ markings: /* @__PURE__ */ new Set([
15790
+ "staccato",
15791
+ "staccatissimo",
15792
+ "spiccato",
15793
+ "staccatissimoWedge",
15794
+ "tenuto",
15795
+ "accent",
15796
+ "strongAccent",
15797
+ "tremolo",
15798
+ "softAccent",
15799
+ "stress",
15800
+ "unstress",
15801
+ "breath",
15802
+ "caesura",
15803
+ "bowDirection",
15804
+ "_x"
15805
+ ]),
15806
+ tuplet: /* @__PURE__ */ new Set(["type", "inner", "outer", "content", "bracket", "showNumber", "showValue", "placement"]),
15807
+ tupletDuration: /* @__PURE__ */ new Set(["duration", "multiple"]),
15808
+ grace: /* @__PURE__ */ new Set(["type", "content", "graceType", "slash", "color"]),
15809
+ multiTremolo: /* @__PURE__ */ new Set(["type", "content", "marks", "outer"]),
15810
+ space: /* @__PURE__ */ new Set(["type", "duration"]),
15811
+ layoutDefinition: /* @__PURE__ */ new Set(["id", "content"]),
15812
+ layoutGroup: /* @__PURE__ */ new Set(["type", "content", "symbol", "label", "barlineStyle"]),
15813
+ layoutStaff: /* @__PURE__ */ new Set(["type", "sources", "label", "labelref"]),
15814
+ layoutSource: /* @__PURE__ */ new Set(["part", "staff", "stem", "voice", "labelref"]),
15815
+ scoreDefinition: /* @__PURE__ */ new Set(["name", "layout", "multimeasureRests", "pages", "useWritten", "_x"]),
15816
+ multimeasureRest: /* @__PURE__ */ new Set(["start", "duration"]),
15817
+ page: /* @__PURE__ */ new Set(["systems"]),
15818
+ system: /* @__PURE__ */ new Set(["measure", "layout", "layoutChanges"]),
15819
+ layoutChange: /* @__PURE__ */ new Set(["layout", "location"])
15820
+ };
15821
+
15474
15822
  // packages/format/src/mnx/parserCore.ts
15475
15823
  function parseMnxUnvalidated(json) {
15476
15824
  return parseMnxInternal(json, new DiagnosticCollector());
@@ -15478,7 +15826,7 @@ function parseMnxUnvalidated(json) {
15478
15826
  function parseSupport(supportObj) {
15479
15827
  if (!supportObj) return;
15480
15828
  let support = {};
15481
- return typeof supportObj.useAccidentalDisplay == "boolean" && (support.useAccidentalDisplay = supportObj.useAccidentalDisplay), typeof supportObj.useBeams == "boolean" && (support.useBeams = supportObj.useBeams), support;
15829
+ return typeof supportObj.useAccidentalDisplay == "boolean" && (support.useAccidentalDisplay = supportObj.useAccidentalDisplay), typeof supportObj.useBeams == "boolean" && (support.useBeams = supportObj.useBeams), typeof supportObj.useLyricExtenders == "boolean" && (support.useLyricExtenders = supportObj.useLyricExtenders), support;
15482
15830
  }
15483
15831
  function parseRootMetadata(rootX) {
15484
15832
  let m = rootX?.viritura?.metadata;
@@ -15646,7 +15994,16 @@ function parseMnxInternal(json, diagnostics) {
15646
15994
  },
15647
15995
  parts: parseParts(partsArr)
15648
15996
  };
15649
- return globalObj?.lyrics && (score.global.lyrics = parseGlobalLyrics(globalObj.lyrics)), globalObj?.sounds && (score.global.sounds = parseGlobalSounds(globalObj.sounds)), layoutsArr && layoutsArr.length > 0 && (score.layouts = layoutsArr.map((l) => parseLayoutDefinition(l))), scoresArr && scoresArr.length > 0 && (score.scores = scoresArr.map((s) => parseScoreDefinition(s))), applyRootExtensions(score, obj._x), assignMissingIds(score), validateSupportConsistency(score, support, diagnostics), scanUnknownFields(obj, diagnostics), score;
15997
+ globalObj?.lyrics && (score.global.lyrics = parseGlobalLyrics(globalObj.lyrics)), globalObj?.sounds && (score.global.sounds = parseGlobalSounds(globalObj.sounds)), layoutsArr && layoutsArr.length > 0 && (score.layouts = layoutsArr.map((l) => parseLayoutDefinition(l))), scoresArr && scoresArr.length > 0 && (score.scores = scoresArr.map((s) => parseScoreDefinition(s))), applyRootExtensions(score, obj._x), assignMissingIds(score), validateSupportConsistency(score, support, diagnostics);
15998
+ for (let issue of resolveLyricExtenders(score).diagnostics) {
15999
+ let source = issue.sourceEventId ? ` on event "${issue.sourceEventId}"` : "", message = issue.code === "missing-target" ? `Lyric extender${source} targets missing event "${issue.targetEventId}"` : issue.code === "backward-target" ? `Lyric extender${source} targets earlier event "${issue.targetEventId}"` : `Lyric extender${source} ends on an event with another syllable in lyric line "${issue.lineId}"`;
16000
+ diagnostics.warn(
16001
+ ptr("parts", "?", "measures", "?", "sequences", "?", "content", "?", "lyrics", "lines", issue.lineId, "extender"),
16002
+ message,
16003
+ "lyric-extender-reference"
16004
+ );
16005
+ }
16006
+ return scanUnknownFields(obj, diagnostics), score;
15650
16007
  }
15651
16008
  function applyRootExtensions(score, rootX) {
15652
16009
  let metadata = parseRootMetadata(rootX);
@@ -15709,121 +16066,6 @@ function processEventIds(event, usedIds, collectOnly) {
15709
16066
  for (let n of event.notes)
15710
16067
  n.id || (n.id = mintId(usedIds));
15711
16068
  }
15712
- var KEYS = {
15713
- root: /* @__PURE__ */ new Set(["mnx", "global", "parts", "layouts", "scores", "_x"]),
15714
- mnxMeta: /* @__PURE__ */ new Set(["version", "support"]),
15715
- support: /* @__PURE__ */ new Set(["useAccidentalDisplay", "useBeams"]),
15716
- global: /* @__PURE__ */ new Set(["measures", "lyrics"]),
15717
- globalLyrics: /* @__PURE__ */ new Set(["lineMetadata", "lineOrder"]),
15718
- globalMeasure: /* @__PURE__ */ new Set([
15719
- "id",
15720
- "number",
15721
- "time",
15722
- "key",
15723
- "barline",
15724
- "repeatStart",
15725
- "repeatEnd",
15726
- "ending",
15727
- "tempos",
15728
- "segno",
15729
- "fine",
15730
- "jump",
15731
- "_x"
15732
- ]),
15733
- time: /* @__PURE__ */ new Set(["count", "unit", "display", "_x"]),
15734
- key: /* @__PURE__ */ new Set(["fifths", "color", "_x"]),
15735
- barline: /* @__PURE__ */ new Set(["type"]),
15736
- ending: /* @__PURE__ */ new Set(["duration", "numbers", "open", "color"]),
15737
- tempo: /* @__PURE__ */ new Set(["bpm", "value", "location", "_x"]),
15738
- tempoNoteValue: /* @__PURE__ */ new Set(["base", "dots"]),
15739
- rhythmicPosition: /* @__PURE__ */ new Set(["fraction"]),
15740
- segno: /* @__PURE__ */ new Set(["location", "glyph", "color"]),
15741
- fine: /* @__PURE__ */ new Set(["location", "color"]),
15742
- jump: /* @__PURE__ */ new Set(["type", "location"]),
15743
- part: /* @__PURE__ */ new Set(["id", "name", "shortName", "staves", "transposition", "measures", "_x"]),
15744
- transposition: /* @__PURE__ */ new Set(["interval", "keyFifthsFlipAt", "prefersWrittenPitches"]),
15745
- partMeasure: /* @__PURE__ */ new Set([
15746
- "sequences",
15747
- "clefs",
15748
- "staffConfigs",
15749
- "arpeggios",
15750
- "nonArpeggios",
15751
- "beams",
15752
- "dynamics",
15753
- "ottavas",
15754
- "measureRepeat",
15755
- "_x"
15756
- ]),
15757
- positionedStaffConfig: /* @__PURE__ */ new Set(["config", "position", "staff", "id", "_c", "_x"]),
15758
- staffConfig: /* @__PURE__ */ new Set(["lines", "id", "_c", "_x"]),
15759
- measureRepeat: /* @__PURE__ */ new Set(["number", "counter", "displayNumber", "staffPosition", "_c", "_x"]),
15760
- measureRepeatCounter: /* @__PURE__ */ new Set(["count", "placement", "_c", "_x"]),
15761
- arpeggio: /* @__PURE__ */ new Set(["id", "position", "span", "direction", "arrow", "_c", "_x"]),
15762
- nonArpeggio: /* @__PURE__ */ new Set(["id", "position", "span", "_c", "_x"]),
15763
- idPair: /* @__PURE__ */ new Set(["start", "end"]),
15764
- positionedClef: /* @__PURE__ */ new Set(["clef", "position", "staff"]),
15765
- clef: /* @__PURE__ */ new Set(["sign", "staffPosition", "color", "glyph", "octave", "showOctave", "hide"]),
15766
- beam: /* @__PURE__ */ new Set(["events", "beams", "direction"]),
15767
- dynamic: /* @__PURE__ */ new Set(["position", "value", "glyph", "staff", "voice"]),
15768
- ottava: /* @__PURE__ */ new Set(["position", "end", "value", "staff", "voice"]),
15769
- measureRhythmicPosition: /* @__PURE__ */ new Set(["measure", "position"]),
15770
- sequence: /* @__PURE__ */ new Set(["content", "fullMeasure", "staff", "voice", "directionHint"]),
15771
- fullMeasure: /* @__PURE__ */ new Set(["visualDuration", "staffPosition"]),
15772
- duration: /* @__PURE__ */ new Set(["base", "dots"]),
15773
- event: /* @__PURE__ */ new Set([
15774
- "type",
15775
- "id",
15776
- "duration",
15777
- "staff",
15778
- "rest",
15779
- "notes",
15780
- "slurs",
15781
- "markings",
15782
- "fermata",
15783
- "lyrics",
15784
- "stemDirection",
15785
- "smufl",
15786
- "_x"
15787
- ]),
15788
- rest: /* @__PURE__ */ new Set(["staffPosition"]),
15789
- note: /* @__PURE__ */ new Set(["pitch", "id", "ties", "accidentalDisplay", "written"]),
15790
- pitch: /* @__PURE__ */ new Set(["step", "octave", "alter"]),
15791
- tie: /* @__PURE__ */ new Set(["target", "targetType", "side", "lv"]),
15792
- accidentalDisplay: /* @__PURE__ */ new Set(["show", "force", "enclosure"]),
15793
- accidentalEnclosure: /* @__PURE__ */ new Set(["symbol"]),
15794
- written: /* @__PURE__ */ new Set(["diatonicDelta"]),
15795
- slur: /* @__PURE__ */ new Set(["target", "side", "sideEnd", "lineType", "startNote", "endNote"]),
15796
- markings: /* @__PURE__ */ new Set([
15797
- "staccato",
15798
- "staccatissimo",
15799
- "spiccato",
15800
- "staccatissimoWedge",
15801
- "tenuto",
15802
- "accent",
15803
- "strongAccent",
15804
- "tremolo",
15805
- "softAccent",
15806
- "stress",
15807
- "unstress",
15808
- "breath",
15809
- "bowDirection",
15810
- "_x"
15811
- ]),
15812
- tuplet: /* @__PURE__ */ new Set(["type", "inner", "outer", "content", "bracket", "showNumber", "showValue", "placement"]),
15813
- tupletDuration: /* @__PURE__ */ new Set(["duration", "multiple"]),
15814
- grace: /* @__PURE__ */ new Set(["type", "content", "graceType", "slash", "color"]),
15815
- multiTremolo: /* @__PURE__ */ new Set(["type", "content", "marks", "outer"]),
15816
- space: /* @__PURE__ */ new Set(["type", "duration"]),
15817
- layoutDefinition: /* @__PURE__ */ new Set(["id", "content"]),
15818
- layoutGroup: /* @__PURE__ */ new Set(["type", "content", "symbol", "label", "barlineStyle"]),
15819
- layoutStaff: /* @__PURE__ */ new Set(["type", "sources", "label", "labelref"]),
15820
- layoutSource: /* @__PURE__ */ new Set(["part", "staff", "stem", "voice", "labelref"]),
15821
- scoreDefinition: /* @__PURE__ */ new Set(["name", "layout", "multimeasureRests", "pages", "useWritten", "_x"]),
15822
- multimeasureRest: /* @__PURE__ */ new Set(["start", "duration"]),
15823
- page: /* @__PURE__ */ new Set(["systems"]),
15824
- system: /* @__PURE__ */ new Set(["measure", "layout", "layoutChanges"]),
15825
- layoutChange: /* @__PURE__ */ new Set(["layout", "location"])
15826
- };
15827
16069
  function scanUnknownFields(json, dx) {
15828
16070
  if (!json || typeof json != "object") return;
15829
16071
  let root = json;
@@ -15870,6 +16112,8 @@ function scanGlobalMeasure(gm, p, dx) {
15870
16112
  });
15871
16113
  let segno = gm.segno;
15872
16114
  segno && emitUnknown(segno, KEYS.segno, p + ptr("segno"), dx);
16115
+ let coda = gm.coda;
16116
+ coda && emitUnknown(coda, KEYS.segno, p + ptr("coda"), dx);
15873
16117
  let fine = gm.fine;
15874
16118
  fine && emitUnknown(fine, KEYS.fine, p + ptr("fine"), dx);
15875
16119
  let jump = gm.jump;
@@ -15937,6 +16181,19 @@ function scanContentItem(c, p, dx) {
15937
16181
  let type = c.type ?? "event";
15938
16182
  if (type === "event") {
15939
16183
  emitUnknown(c, KEYS.event, p, dx);
16184
+ let lyrics = c.lyrics;
16185
+ if (lyrics) {
16186
+ emitUnknown(lyrics, KEYS.eventLyrics, p + ptr("lyrics"), dx);
16187
+ let lines = lyrics.lines;
16188
+ if (lines && typeof lines == "object")
16189
+ for (let [lineId, line] of Object.entries(lines)) {
16190
+ if (!line || typeof line != "object") continue;
16191
+ let linePath = p + ptr("lyrics", "lines", lineId);
16192
+ emitUnknown(line, KEYS.eventLyricLine, linePath, dx);
16193
+ let extender = line.extender;
16194
+ extender && emitUnknown(extender, KEYS.eventLyricLineExtender, linePath + ptr("extender"), dx);
16195
+ }
16196
+ }
15940
16197
  let notes = c.notes;
15941
16198
  Array.isArray(notes) && notes.forEach((n, i) => {
15942
16199
  n && typeof n == "object" && emitUnknown(n, KEYS.note, p + ptr("notes", i), dx);
@@ -16419,7 +16676,7 @@ function fermataMultiplier(fermata) {
16419
16676
  function caesuraBeats(style) {
16420
16677
  return style === "short" ? CAESURA_SHORT_BEATS : CAESURA_BEATS;
16421
16678
  }
16422
- function durationBeats(d) {
16679
+ function durationBeats2(d) {
16423
16680
  let base = DURATION_BEATS[d.base] ?? 1;
16424
16681
  return d.dots ? base * (2 - Math.pow(2, -d.dots)) : base;
16425
16682
  }
@@ -16437,16 +16694,16 @@ function collectFromContent(content, startBeat, ratio, spans, caesuraByBeat) {
16437
16694
  let cursor = startBeat;
16438
16695
  for (let item of content)
16439
16696
  if (item.type === "event") {
16440
- let dur = durationBeats(item.duration) * ratio, endBeat = cursor + dur;
16697
+ let dur = durationBeats2(item.duration) * ratio, endBeat = cursor + dur;
16441
16698
  if (item.fermata && item.fermata.duration !== "none") {
16442
16699
  let mult = fermataMultiplier(item.fermata);
16443
16700
  dur > 0 && mult > 1 && spans.push({ onset: cursor, end: endBeat, duration: dur, multiplier: mult });
16444
16701
  }
16445
16702
  item.markings?.caesura && mergeMax(caesuraByBeat, endBeat, caesuraBeats(item.markings.caesura.style)), cursor = endBeat;
16446
16703
  } else if (item.type === "tuplet") {
16447
- let outerBeats = durationBeats(item.outer.duration) * item.outer.multiple, innerBeats = durationBeats(item.inner.duration) * item.inner.multiple, childRatio = innerBeats > 0 ? outerBeats / innerBeats * ratio : ratio;
16704
+ let outerBeats = durationBeats2(item.outer.duration) * item.outer.multiple, innerBeats = durationBeats2(item.inner.duration) * item.inner.multiple, childRatio = innerBeats > 0 ? outerBeats / innerBeats * ratio : ratio;
16448
16705
  collectFromContent(item.content, cursor, childRatio, spans, caesuraByBeat), cursor += outerBeats * ratio;
16449
- } else item.type === "tremolo" ? cursor += durationBeats(item.outer.duration) * item.outer.multiple * ratio : item.type === "space" && (cursor += item.duration[0] / item.duration[1] * 4 * ratio);
16706
+ } else item.type === "tremolo" ? cursor += durationBeats2(item.outer.duration) * item.outer.multiple * ratio : item.type === "space" && (cursor += item.duration[0] / item.duration[1] * 4 * ratio);
16450
16707
  return cursor - startBeat;
16451
16708
  }
16452
16709
  function collectPartMeasureHolds(partMeasure, originalMeasureIndex, staffMeterTable, spans, caesuraByBeat) {
@@ -16509,21 +16766,21 @@ function contentBeats(content) {
16509
16766
  for (let item of content)
16510
16767
  switch (item.type) {
16511
16768
  case "event":
16512
- beats += durationBeats2(item.duration.base, item.duration.dots);
16769
+ beats += durationBeats3(item.duration.base, item.duration.dots);
16513
16770
  break;
16514
16771
  case "space":
16515
16772
  beats += item.duration[0] / item.duration[1] * 4;
16516
16773
  break;
16517
16774
  case "tuplet":
16518
16775
  case "tremolo":
16519
- beats += durationBeats2(item.outer.duration.base, item.outer.duration.dots) * item.outer.multiple;
16776
+ beats += durationBeats3(item.outer.duration.base, item.outer.duration.dots) * item.outer.multiple;
16520
16777
  break;
16521
16778
  case "grace":
16522
16779
  break;
16523
16780
  }
16524
16781
  return beats;
16525
16782
  }
16526
- function durationBeats2(base, dots) {
16783
+ function durationBeats3(base, dots) {
16527
16784
  let beats = DURATION_BEATS[base];
16528
16785
  return dots ? beats * (2 - Math.pow(2, -dots)) : beats;
16529
16786
  }
@@ -17610,7 +17867,7 @@ function kitNoteToMidi(kn, kitMidiMap) {
17610
17867
  return kitMidiMap.get(kn.kitComponent) ?? -1;
17611
17868
  }
17612
17869
  var GRACE_NOTE_BEATS = 0.125;
17613
- function durationBeats3(d) {
17870
+ function durationBeats4(d) {
17614
17871
  let base = DURATION_BEATS[d.base] ?? 1;
17615
17872
  return d.dots ? base * (2 - Math.pow(2, -d.dots)) : base;
17616
17873
  }
@@ -17927,7 +18184,7 @@ function tryProcessSpecialEvent(ctx, event, measureStartTime, beatOffset, baseVe
17927
18184
  }
17928
18185
  function processNoteEvent(ctx, event, measureStartTime, beatOffset, baseVelocity, tupletRatio) {
17929
18186
  ctx.gmProgram = ctx.instrumentAtBeatOffset?.(beatOffset * ctx.staffMeterRatio).program ?? ctx.gmProgram;
17930
- let { partIndex, channel, tieTargets, out, timeSig, kitMidiMap, kitAltProgramMap } = ctx, beats = durationBeats3(event.duration) * tupletRatio;
18187
+ let { partIndex, channel, tieTargets, out, timeSig, kitMidiMap, kitAltProgramMap } = ctx, beats = durationBeats4(event.duration) * tupletRatio;
17931
18188
  if (isRest(event))
17932
18189
  return flushVoiceLegato(ctx.pendingLegato, ctx.voiceKey, ctx.out), beats;
17933
18190
  resolveVoiceLegato({
@@ -18240,30 +18497,30 @@ function processGrace(ctx, grace, measureStartTime, beatOffset, tupletRatio) {
18240
18497
  if (grace.graceType === "stealFollowing") {
18241
18498
  let cursor2 = beatOffset;
18242
18499
  for (let evt of events) {
18243
- let ratio = graceBeats / durationBeats3(evt.duration);
18500
+ let ratio = graceBeats / durationBeats4(evt.duration);
18244
18501
  processScopedNoteEvent(ctx, evt, measureStartTime, cursor2, ratio), cursor2 += graceBeats;
18245
18502
  }
18246
18503
  return totalGraceBeats;
18247
18504
  }
18248
18505
  let cursor = beatOffset - totalGraceBeats;
18249
18506
  for (let evt of events) {
18250
- let ratio = graceBeats / durationBeats3(evt.duration);
18507
+ let ratio = graceBeats / durationBeats4(evt.duration);
18251
18508
  processScopedNoteEvent(ctx, evt, measureStartTime, cursor, ratio), cursor += graceBeats;
18252
18509
  }
18253
18510
  return 0;
18254
18511
  }
18255
18512
  function processTuplet(ctx, tuplet, measureStartTime, beatOffset, parentTupletRatio) {
18256
- let outerBeats = durationBeats3(tuplet.outer.duration) * tuplet.outer.multiple, innerBeats = durationBeats3(tuplet.inner.duration) * tuplet.inner.multiple, ratio = outerBeats / innerBeats * parentTupletRatio, cursor = beatOffset;
18513
+ let outerBeats = durationBeats4(tuplet.outer.duration) * tuplet.outer.multiple, innerBeats = durationBeats4(tuplet.inner.duration) * tuplet.inner.multiple, ratio = outerBeats / innerBeats * parentTupletRatio, cursor = beatOffset;
18257
18514
  for (let item of tuplet.content)
18258
18515
  cursor += processContentItem(ctx, item, measureStartTime, cursor, ratio);
18259
18516
  return outerBeats * parentTupletRatio;
18260
18517
  }
18261
18518
  function processTremolo(ctx, tremolo, measureStartTime, beatOffset, tupletRatio) {
18262
- let outerBeats = durationBeats3(tremolo.outer.duration) * tremolo.outer.multiple * tupletRatio;
18519
+ let outerBeats = durationBeats4(tremolo.outer.duration) * tremolo.outer.multiple * tupletRatio;
18263
18520
  if (tremolo.content.length < 2) return outerBeats;
18264
18521
  let subdivisions = Math.pow(2, tremolo.marks), subdivBeats = outerBeats / subdivisions;
18265
18522
  for (let s = 0; s < subdivisions; s++) {
18266
- let noteEvent = tremolo.content[s % 2], subBeatOffset = beatOffset + s * subdivBeats, subRatio = subdivBeats / durationBeats3(noteEvent.duration);
18523
+ let noteEvent = tremolo.content[s % 2], subBeatOffset = beatOffset + s * subdivBeats, subRatio = subdivBeats / durationBeats4(noteEvent.duration);
18267
18524
  processScopedNoteEvent(ctx, noteEvent, measureStartTime, subBeatOffset, subRatio);
18268
18525
  }
18269
18526
  return outerBeats;