@viritura/score-engine 0.2.1-next.40 → 0.2.1-next.41
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 +164 -45
- package/dist/index.js.map +4 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -602,9 +602,9 @@ function parseChordSymbolText(input, position) {
|
|
|
602
602
|
|
|
603
603
|
// packages/core/src/operations/lyricExtenders.ts
|
|
604
604
|
init_define_import_meta_env();
|
|
605
|
-
function durationBeats(
|
|
606
|
-
let base = DURATION_BEATS[
|
|
607
|
-
for (let index = 0; index < (
|
|
605
|
+
function durationBeats(duration2) {
|
|
606
|
+
let base = DURATION_BEATS[duration2.base], dots = 1;
|
|
607
|
+
for (let index = 0; index < (duration2.dots ?? 0); index++) dots += 0.5 ** (index + 1);
|
|
608
608
|
return base * dots;
|
|
609
609
|
}
|
|
610
610
|
function quantityBeats(quantity) {
|
|
@@ -614,7 +614,7 @@ function walkTimedContent(content, startBeat, scale, partIndex, sequenceIndex, v
|
|
|
614
614
|
let cursor = startBeat;
|
|
615
615
|
for (let item of content) {
|
|
616
616
|
if (item.type === "event") {
|
|
617
|
-
let
|
|
617
|
+
let duration2 = durationBeats(item.duration) * scale;
|
|
618
618
|
output.push({
|
|
619
619
|
event: item,
|
|
620
620
|
partIndex,
|
|
@@ -623,10 +623,10 @@ function walkTimedContent(content, startBeat, scale, partIndex, sequenceIndex, v
|
|
|
623
623
|
staff,
|
|
624
624
|
measureIndex,
|
|
625
625
|
startBeat: cursor,
|
|
626
|
-
durationBeats:
|
|
626
|
+
durationBeats: duration2,
|
|
627
627
|
rest: isRest(item),
|
|
628
628
|
ordinal: ordinal.value++
|
|
629
|
-
}), cursor +=
|
|
629
|
+
}), cursor += duration2;
|
|
630
630
|
continue;
|
|
631
631
|
}
|
|
632
632
|
if (item.type === "space") {
|
|
@@ -2231,9 +2231,9 @@ function interpolateBeatAnchors(bounds, beat, endX) {
|
|
|
2231
2231
|
|
|
2232
2232
|
// packages/score-engine/src/buildInfo.ts
|
|
2233
2233
|
init_define_import_meta_env();
|
|
2234
|
-
var PACKAGE_VERSION = "0.2.1-next.
|
|
2234
|
+
var PACKAGE_VERSION = "0.2.1-next.41", BUNDLE_WORKER_FILE = "score-engine.worker.js";
|
|
2235
2235
|
function buildCommit() {
|
|
2236
|
-
return "
|
|
2236
|
+
return "715e88d4c5a7118150857b4e89598e4560d44b58";
|
|
2237
2237
|
}
|
|
2238
2238
|
function bundledAssetBase() {
|
|
2239
2239
|
let moduleUrl = import.meta.url;
|
|
@@ -16659,8 +16659,8 @@ function parseVideoMedia(raw) {
|
|
|
16659
16659
|
typeof contentHash == "string" && contentHash.length > 0 && (media.contentHash = contentHash);
|
|
16660
16660
|
let demoSourceId = raw?.demoSourceId;
|
|
16661
16661
|
typeof demoSourceId == "string" && demoSourceId.length > 0 && (media.demoSourceId = demoSourceId);
|
|
16662
|
-
let
|
|
16663
|
-
return typeof
|
|
16662
|
+
let duration2 = raw?.durationSeconds;
|
|
16663
|
+
return typeof duration2 == "number" && Number.isFinite(duration2) && duration2 > 0 && (media.durationSeconds = duration2), media;
|
|
16664
16664
|
}
|
|
16665
16665
|
function parseRootVideoSync(rootX) {
|
|
16666
16666
|
let raw = rootX?.viritura?.videoSync;
|
|
@@ -17153,7 +17153,7 @@ function buildTempoMap(globalMeasures, holds) {
|
|
|
17153
17153
|
if (gm.tempos && gm.tempos.length > 0) {
|
|
17154
17154
|
for (let tempo of gm.tempos) {
|
|
17155
17155
|
if (tempo.bpm === void 0) continue;
|
|
17156
|
-
let beatOffset = tempo.location ? tempo.location.fraction
|
|
17156
|
+
let beatOffset = tempo.location ? posBeats(tempo.location.fraction) : 0, qpm = effectiveQpm(tempo.bpm, tempo.value.base, tempo.value.dots);
|
|
17157
17157
|
changes.push({ beatOffset, qpm });
|
|
17158
17158
|
}
|
|
17159
17159
|
changes.sort((a, b) => a.beatOffset - b.beatOffset);
|
|
@@ -17186,10 +17186,10 @@ function buildTempoModel(globalMeasures, holds) {
|
|
|
17186
17186
|
nominalTime && (currentTime = { count: nominalTime.count, unit: nominalTime.unit });
|
|
17187
17187
|
let mBeats = measureBeatsFromTime(gm.time ?? currentTime);
|
|
17188
17188
|
if (measureStartBeats.push(cumBeats), gm.tempos && gm.tempos.length > 0) {
|
|
17189
|
-
let sorted = [...gm.tempos].sort((a, b) => tempoBeat(a
|
|
17189
|
+
let sorted = [...gm.tempos].sort((a, b) => tempoBeat(a) - tempoBeat(b));
|
|
17190
17190
|
for (let tempo of sorted) {
|
|
17191
17191
|
if (tempo.bpm === void 0) continue;
|
|
17192
|
-
let startBeat = cumBeats + tempoBeat(tempo
|
|
17192
|
+
let startBeat = cumBeats + tempoBeat(tempo), qpm = effectiveQpm(tempo.bpm, tempo.value.base, tempo.value.dots);
|
|
17193
17193
|
regions.push({ startBeat, endBeat: 1 / 0, startBpm: qpm, endBpm: qpm });
|
|
17194
17194
|
}
|
|
17195
17195
|
}
|
|
@@ -17243,8 +17243,8 @@ function resolveGradualEndBeat(end, fromIdx, globalMeasures, measureStartBeats,
|
|
|
17243
17243
|
if (start !== void 0)
|
|
17244
17244
|
return start + posBeats(end.position.fraction);
|
|
17245
17245
|
}
|
|
17246
|
-
function tempoBeat(tempo
|
|
17247
|
-
return tempo.location ? tempo.location.fraction
|
|
17246
|
+
function tempoBeat(tempo) {
|
|
17247
|
+
return tempo.location ? posBeats(tempo.location.fraction) : 0;
|
|
17248
17248
|
}
|
|
17249
17249
|
|
|
17250
17250
|
// packages/midi/src/cadenzaTiming.ts
|
|
@@ -17284,14 +17284,14 @@ function playbackGlobalMeasures(score, measureOrder) {
|
|
|
17284
17284
|
let activeBeats = 4, activeTime = { count: 4, unit: 4 }, activeTempo = {
|
|
17285
17285
|
bpm: DEFAULT_BPM,
|
|
17286
17286
|
value: { base: "quarter" }
|
|
17287
|
-
}, resolved = score.global.measures.map((original, measureIndex) => {
|
|
17288
|
-
original.time && (activeTime = original.time);
|
|
17287
|
+
}, inheritedTempos = [], resolved = score.global.measures.map((original, measureIndex) => {
|
|
17288
|
+
inheritedTempos.push(activeTempo), original.time && (activeTime = original.time);
|
|
17289
17289
|
let measure = {
|
|
17290
17290
|
...original,
|
|
17291
17291
|
time: { ...activeTime },
|
|
17292
|
-
tempos: original.tempos
|
|
17293
|
-
};
|
|
17294
|
-
if (
|
|
17292
|
+
tempos: original.tempos
|
|
17293
|
+
}, metronomicTempos = original.tempos?.filter((tempo) => tempo.bpm !== void 0);
|
|
17294
|
+
if (metronomicTempos?.length && (activeTempo = [...metronomicTempos].sort(
|
|
17295
17295
|
(a, b) => (a.location?.fraction[0] ?? 0) / (a.location?.fraction[1] ?? 1) - (b.location?.fraction[0] ?? 0) / (b.location?.fraction[1] ?? 1)
|
|
17296
17296
|
).at(-1)), measure.time && (activeBeats = measure.time.count * 4 / measure.time.unit), measureIndex === 0 && measure.number === 0) {
|
|
17297
17297
|
let pickupBeats = writtenMeasureBeats(score, measureIndex, 0);
|
|
@@ -17317,7 +17317,13 @@ function playbackGlobalMeasures(score, measureOrder) {
|
|
|
17317
17317
|
}
|
|
17318
17318
|
};
|
|
17319
17319
|
});
|
|
17320
|
-
return measureOrder.map((index) =>
|
|
17320
|
+
return measureOrder.map((index, expandedIndex) => {
|
|
17321
|
+
let measure = resolved[index];
|
|
17322
|
+
return expandedIndex === 0 || index === measureOrder[expandedIndex - 1] + 1 || measure.tempos?.some((tempo) => tempo.bpm !== void 0 && (!tempo.location || tempo.location.fraction[0] === 0)) ? measure : {
|
|
17323
|
+
...measure,
|
|
17324
|
+
tempos: [{ ...inheritedTempos[index], location: { fraction: [0, 1] } }, ...measure.tempos ?? []]
|
|
17325
|
+
};
|
|
17326
|
+
});
|
|
17321
17327
|
}
|
|
17322
17328
|
function suppressCadenzaFermataHolds(measures, holds) {
|
|
17323
17329
|
return holds.map(
|
|
@@ -17687,9 +17693,9 @@ function compileChordPlayback(score, includeGlobalChords = !0) {
|
|
|
17687
17693
|
function realizeChordPlayback(program, measureOrder, timing) {
|
|
17688
17694
|
let notes = [], pitches = [], startBeat = 0, occurrence = 0, finish = (endBeat) => {
|
|
17689
17695
|
if (endBeat <= startBeat) return;
|
|
17690
|
-
let startTime = timing.model.timeAtBeat(startBeat),
|
|
17696
|
+
let startTime = timing.model.timeAtBeat(startBeat), duration2 = timing.model.timeAtBeat(endBeat) - startTime;
|
|
17691
17697
|
for (let pitch of pitches)
|
|
17692
|
-
notes.push({ id: `${CHORDS_PART_ID}:${occurrence}:${pitch}`, pitch, startTime, duration });
|
|
17698
|
+
notes.push({ id: `${CHORDS_PART_ID}:${occurrence}:${pitch}`, pitch, startTime, duration: duration2 });
|
|
17693
17699
|
occurrence++;
|
|
17694
17700
|
};
|
|
17695
17701
|
for (let expandedIndex = 0; expandedIndex < measureOrder.length; expandedIndex++) {
|
|
@@ -17758,9 +17764,9 @@ var WHOLE_DURATIONS = {
|
|
|
17758
17764
|
"512th": 1 / 512,
|
|
17759
17765
|
"1024th": 1 / 1024
|
|
17760
17766
|
};
|
|
17761
|
-
function durationValue(
|
|
17762
|
-
let base = WHOLE_DURATIONS[
|
|
17763
|
-
for (let dot = 0; dot < (
|
|
17767
|
+
function durationValue(duration2) {
|
|
17768
|
+
let base = WHOLE_DURATIONS[duration2.base] ?? 0, value = base;
|
|
17769
|
+
for (let dot = 0; dot < (duration2.dots ?? 0); dot++) value += base / 2 ** (dot + 1);
|
|
17764
17770
|
return value;
|
|
17765
17771
|
}
|
|
17766
17772
|
function contentInfo(content, scale = 1) {
|
|
@@ -17769,8 +17775,8 @@ function contentInfo(content, scale = 1) {
|
|
|
17769
17775
|
if (item.type === "event") {
|
|
17770
17776
|
let sounding = !!item.notes?.length || !!item.kitNotes?.length;
|
|
17771
17777
|
sounding && (active = !0, onset ??= cursor);
|
|
17772
|
-
let
|
|
17773
|
-
return cursor +=
|
|
17778
|
+
let duration2 = durationValue(item.duration) * scale;
|
|
17779
|
+
return cursor += duration2, {
|
|
17774
17780
|
duration: item.duration,
|
|
17775
17781
|
sounding
|
|
17776
17782
|
};
|
|
@@ -17780,13 +17786,13 @@ function contentInfo(content, scale = 1) {
|
|
|
17780
17786
|
return nested.active && (active = !0, onset ??= cursor + nested.onset), cursor += nested.duration, { type: item.type, inner: item.inner, outer: item.outer, content: nested.signature };
|
|
17781
17787
|
}
|
|
17782
17788
|
if (item.type === "tremolo") {
|
|
17783
|
-
let
|
|
17784
|
-
return item.content.some((event) => !!event.notes?.length || !!event.kitNotes?.length) && (active = !0, onset ??= cursor), cursor +=
|
|
17789
|
+
let duration2 = durationValue(item.outer.duration) * item.outer.multiple * scale;
|
|
17790
|
+
return item.content.some((event) => !!event.notes?.length || !!event.kitNotes?.length) && (active = !0, onset ??= cursor), cursor += duration2, { type: item.type, outer: item.outer, content: item.content.map((event) => event.duration) };
|
|
17785
17791
|
}
|
|
17786
17792
|
if (item.type === "grace") return { type: item.type, content: item.content.map((event) => event.duration) };
|
|
17787
17793
|
if (item.type === "space") {
|
|
17788
|
-
let
|
|
17789
|
-
return cursor +=
|
|
17794
|
+
let duration2 = (item.duration[1] === 0 ? 0 : item.duration[0] / item.duration[1]) * scale;
|
|
17795
|
+
return cursor += duration2, { type: item.type, duration: item.duration };
|
|
17790
17796
|
}
|
|
17791
17797
|
return {};
|
|
17792
17798
|
}), active, onset, duration: cursor };
|
|
@@ -18128,8 +18134,8 @@ function resolveVoiceLegato(a) {
|
|
|
18128
18134
|
if (!bucket || bucket.length === 0) return;
|
|
18129
18135
|
let onset = Math.max(
|
|
18130
18136
|
0,
|
|
18131
|
-
a.model.timeAtBeat(a.measureStartBeat + a.beatOffset) + timingHumanize(a.measureStartTime, a.beatOffset, a.partIndex)
|
|
18132
|
-
), nextDur = a.model.secondsForBeats(a.measureStartBeat + a.beatOffset, a.beats);
|
|
18137
|
+
(a.soundingOnset ?? a.model.timeAtBeat(a.measureStartBeat + a.beatOffset)) + timingHumanize(a.measureStartTime, a.beatOffset, a.partIndex)
|
|
18138
|
+
), nextDur = a.soundingDuration ?? a.model.secondsForBeats(a.measureStartBeat + a.beatOffset, a.beats);
|
|
18133
18139
|
resolveLegato(bucket, a.out, onset, nextDur, a.notes);
|
|
18134
18140
|
}
|
|
18135
18141
|
|
|
@@ -18808,6 +18814,107 @@ function laneForScope(program, partIndex, staff, voice) {
|
|
|
18808
18814
|
return program.lanes.get(id) ?? program.lanes.values().next().value;
|
|
18809
18815
|
}
|
|
18810
18816
|
|
|
18817
|
+
// packages/midi/src/rhythmRealization/index.ts
|
|
18818
|
+
init_define_import_meta_env();
|
|
18819
|
+
var EPS3 = 1e-9;
|
|
18820
|
+
function duration(d) {
|
|
18821
|
+
return DURATION_BEATS[d.base] * (d.dots ? 2 - 2 ** -d.dots : 1);
|
|
18822
|
+
}
|
|
18823
|
+
function itemBeats(item) {
|
|
18824
|
+
switch (item.type) {
|
|
18825
|
+
case "event":
|
|
18826
|
+
return duration(item.duration);
|
|
18827
|
+
case "space":
|
|
18828
|
+
return item.duration[0] * 4 / item.duration[1];
|
|
18829
|
+
case "tuplet":
|
|
18830
|
+
case "tremolo":
|
|
18831
|
+
return duration(item.outer.duration) * item.outer.multiple;
|
|
18832
|
+
case "grace":
|
|
18833
|
+
return 0;
|
|
18834
|
+
}
|
|
18835
|
+
}
|
|
18836
|
+
function eligible(item, sequence) {
|
|
18837
|
+
return item?.type === "event" && !item.duration.dots && (item.staff === void 0 || item.staff === (sequence.staff ?? 1)) && item.markings?.tremolo === void 0 && item.markings?.trill === void 0;
|
|
18838
|
+
}
|
|
18839
|
+
function swingAt(anchors, beat) {
|
|
18840
|
+
let result = { mode: "straight" };
|
|
18841
|
+
for (let anchor of anchors) {
|
|
18842
|
+
if (anchor.beat > beat + EPS3) break;
|
|
18843
|
+
result = anchor.swing;
|
|
18844
|
+
}
|
|
18845
|
+
return result;
|
|
18846
|
+
}
|
|
18847
|
+
function containsGrace(content) {
|
|
18848
|
+
return content.some((item) => item.type === "grace" || item.type === "tuplet" && containsGrace(item.content));
|
|
18849
|
+
}
|
|
18850
|
+
function compileSequence(sequence, anchors, metricOffset, measureBeats3, holdBeats) {
|
|
18851
|
+
let timings = /* @__PURE__ */ new Map();
|
|
18852
|
+
if (sequence.fullMeasure || containsGrace(sequence.content)) return timings;
|
|
18853
|
+
let cursor = 0;
|
|
18854
|
+
for (let index = 0; index < sequence.content.length; index++) {
|
|
18855
|
+
let first = sequence.content[index], second = sequence.content[index + 1], swing2 = swingAt(anchors, cursor), beats = itemBeats(first), pairEnd = cursor + 2 * beats;
|
|
18856
|
+
if (swing2.mode === "swing" && eligible(first, sequence) && eligible(second, sequence) && first.duration.base === swing2.subdivision && second.duration.base === swing2.subdivision && Math.abs((cursor + metricOffset) / (2 * beats) - Math.round((cursor + metricOffset) / (2 * beats))) < EPS3 && pairEnd <= measureBeats3 + EPS3 && !holdBeats.some((beat) => beat > cursor + EPS3 && beat < pairEnd - EPS3) && !anchors.some((anchor) => anchor.beat > cursor + EPS3 && anchor.beat < pairEnd - EPS3)) {
|
|
18857
|
+
let split = cursor + 2 * beats * swing2.ratio[0] / (swing2.ratio[0] + swing2.ratio[1]);
|
|
18858
|
+
timings.set(first, { start: cursor, end: split, pairStart: cursor, pairEnd }), timings.set(second, { start: split, end: pairEnd, pairStart: cursor, pairEnd });
|
|
18859
|
+
}
|
|
18860
|
+
cursor += beats;
|
|
18861
|
+
}
|
|
18862
|
+
return timings;
|
|
18863
|
+
}
|
|
18864
|
+
function compileRhythmProgram(score) {
|
|
18865
|
+
let indices = score.global.measures.map((_, index) => index), playbackMeasures = playbackGlobalMeasures(score, indices), holds = buildHoldSchedule(score, indices, score.global.measures), inherited = { mode: "straight" }, anchorsByMeasure = score.global.measures.map((measure) => {
|
|
18866
|
+
let anchors = [{ beat: -1 / 0, swing: inherited }], ordered = (measure.tempos ?? []).map((tempo, index) => ({
|
|
18867
|
+
tempo,
|
|
18868
|
+
index,
|
|
18869
|
+
beat: (tempo.location?.fraction[0] ?? 0) * 4 / (tempo.location?.fraction[1] ?? 1)
|
|
18870
|
+
})).sort((a, b) => a.beat - b.beat || a.index - b.index);
|
|
18871
|
+
for (let { tempo, beat } of ordered)
|
|
18872
|
+
if (tempo.swing) {
|
|
18873
|
+
if (tempo.swing.mode === "swing" && !tempo.swing.ratio.every((value) => Number.isSafeInteger(value) && value > 0))
|
|
18874
|
+
throw new Error("Swing ratios must be positive safe integers.");
|
|
18875
|
+
inherited = tempo.swing, anchors.push({ beat, swing: inherited });
|
|
18876
|
+
}
|
|
18877
|
+
return anchors;
|
|
18878
|
+
}), sequences = /* @__PURE__ */ new Map(), diagnostics = [];
|
|
18879
|
+
return score.parts.forEach((part, partIndex) => {
|
|
18880
|
+
let staffMeters = resolvePartStaffMeterTable(part, score.global.measures);
|
|
18881
|
+
part.measures.forEach((measure, measureIndex) => {
|
|
18882
|
+
let anchors = anchorsByMeasure[measureIndex];
|
|
18883
|
+
if (!anchors) return;
|
|
18884
|
+
let time = resolveActiveTime(score.global.measures, measureIndex), playbackTime = playbackMeasures[measureIndex].time, measureBeats3 = playbackTime.count * 4 / playbackTime.unit, metricOffset = measureIndex === 0 && score.global.measures[0].number === 0 ? time.count * 4 / time.unit - measureBeats3 : 0, diagnosed = /* @__PURE__ */ new Set(), hasSwing = [
|
|
18885
|
+
0,
|
|
18886
|
+
...anchors.map((anchor) => anchor.beat).filter((beat) => beat > 0 && beat < measureBeats3)
|
|
18887
|
+
].some((beat) => swingAt(anchors, beat).mode === "swing");
|
|
18888
|
+
for (let sequence of measure.sequences) {
|
|
18889
|
+
let staff = sequence.staff ?? 1;
|
|
18890
|
+
if (staffMeters[measureIndex]?.has(staff)) {
|
|
18891
|
+
!diagnosed.has(staff) && hasSwing && (diagnostics.push({
|
|
18892
|
+
code: "swing-staff-meter-unsupported",
|
|
18893
|
+
source: { partIndex, measureIndex, staff },
|
|
18894
|
+
message: `Swing left literal in part ${partIndex}, measure ${measureIndex}, staff ${staff}: local staff meters are not supported.`,
|
|
18895
|
+
playbackLaneIds: []
|
|
18896
|
+
}), diagnosed.add(staff));
|
|
18897
|
+
continue;
|
|
18898
|
+
}
|
|
18899
|
+
time.display !== "senzaMisura" && sequences.set(
|
|
18900
|
+
sequence,
|
|
18901
|
+
compileSequence(
|
|
18902
|
+
sequence,
|
|
18903
|
+
anchors,
|
|
18904
|
+
metricOffset,
|
|
18905
|
+
measureBeats3,
|
|
18906
|
+
(holds[measureIndex] ?? []).map((hold) => hold.atBeat)
|
|
18907
|
+
)
|
|
18908
|
+
);
|
|
18909
|
+
}
|
|
18910
|
+
});
|
|
18911
|
+
}), { sequences, diagnostics };
|
|
18912
|
+
}
|
|
18913
|
+
function realizeRhythmEvent(timings, event, start, beats, span) {
|
|
18914
|
+
let timing = timings?.get(event);
|
|
18915
|
+
return !timing || span && (timing.pairStart < span.startBeat - EPS3 || timing.pairEnd > span.endBeat + EPS3) ? { start, beats } : { start: timing.start, beats: timing.end - timing.start };
|
|
18916
|
+
}
|
|
18917
|
+
|
|
18811
18918
|
// packages/midi/src/timeline.ts
|
|
18812
18919
|
var GM_DRUM_CHANNEL = 9;
|
|
18813
18920
|
function noteToMidi(note) {
|
|
@@ -18848,7 +18955,7 @@ function generateTimeline(inputScore, options) {
|
|
|
18848
18955
|
let expandedGlobal = playbackGlobalMeasures(score, measureOrder), holdSchedule = suppressCadenzaFermataHolds(
|
|
18849
18956
|
expandedGlobal,
|
|
18850
18957
|
buildHoldSchedule(score, measureOrder, globalMeasures)
|
|
18851
|
-
), { tempoMap } = buildTempoMap(expandedGlobal, holdSchedule), tempoBuild = buildTempoModel(expandedGlobal, holdSchedule), { model, measureStartTimes, measureStartBeats } = tempoBuild, timing = { tempoMap, measureStartTimes, model, measureStartBeats, playedSpans: spans }, allEvents = [], partChannels = allocatePartChannels(score.parts);
|
|
18958
|
+
), { tempoMap } = buildTempoMap(expandedGlobal, holdSchedule), tempoBuild = buildTempoModel(expandedGlobal, holdSchedule), { model, measureStartTimes, measureStartBeats } = tempoBuild, rhythm = compileRhythmProgram(score), timing = { tempoMap, measureStartTimes, model, measureStartBeats, playedSpans: spans, rhythm }, allEvents = [], partChannels = allocatePartChannels(score.parts);
|
|
18852
18959
|
for (let partIdx = 0; partIdx < score.parts.length; partIdx++)
|
|
18853
18960
|
processPart(
|
|
18854
18961
|
score,
|
|
@@ -18876,14 +18983,19 @@ function generateTimeline(inputScore, options) {
|
|
|
18876
18983
|
{ model, measureStartBeats, measureOrder },
|
|
18877
18984
|
options?.instrumentPrograms,
|
|
18878
18985
|
options?.partPrograms
|
|
18879
|
-
), diagnostics = [
|
|
18986
|
+
), diagnostics = [
|
|
18987
|
+
...assignPhysicalMidiChannels(allEvents, score),
|
|
18988
|
+
...instrumentDiagnostics,
|
|
18989
|
+
...rhythm.diagnostics
|
|
18990
|
+
];
|
|
18991
|
+
for (let diagnostic of diagnostics) options?.onDiagnostic?.(diagnostic);
|
|
18880
18992
|
allEvents.sort(compareEvents);
|
|
18881
|
-
let
|
|
18993
|
+
let duration2 = model.timeAtBeat(tempoBuild.totalBeats), measureTimeSignatures = measureOrder.map((origIdx) => {
|
|
18882
18994
|
let ts = resolveActiveTime(globalMeasures, origIdx);
|
|
18883
18995
|
return { count: ts.count, unit: ts.unit };
|
|
18884
18996
|
}), result = {
|
|
18885
18997
|
events: allEvents,
|
|
18886
|
-
duration,
|
|
18998
|
+
duration: duration2,
|
|
18887
18999
|
tempoMap,
|
|
18888
19000
|
measureStartTimes,
|
|
18889
19001
|
model,
|
|
@@ -19056,7 +19168,7 @@ function processPart(score, partIdx, options, partChannels, timing, measureOrder
|
|
|
19056
19168
|
let lane = laneForSequence(dynamicProgram, partIdx, seq, seqIdx);
|
|
19057
19169
|
ctx.sequenceStaff = seq.staff ?? 1, ctx.sequenceVoice = lane.voice, ctx.dynamicsEnvelope = lane.envelope, ctx.playbackLaneId = lane.id;
|
|
19058
19170
|
let effectiveStaffMeter = staffMeterTable[origMeasureIdx]?.get(ctx.sequenceStaff);
|
|
19059
|
-
ctx.timeSig = effectiveStaffMeter ? effectiveStaffMeter.timeSignature : activeTime, ctx.staffMeterRatio = staffMeterRatioAt(staffMeterTable, origMeasureIdx, ctx.sequenceStaff);
|
|
19171
|
+
ctx.timeSig = effectiveStaffMeter ? effectiveStaffMeter.timeSignature : activeTime, ctx.staffMeterRatio = staffMeterRatioAt(staffMeterTable, origMeasureIdx, ctx.sequenceStaff), ctx.rhythm = timing.rhythm.sequences.get(seq);
|
|
19060
19172
|
let firstNewEvent = allEvents.length;
|
|
19061
19173
|
processSequence(ctx, seq, measureStartTime, measureHolds, measureSpq), stampPlaybackLane(allEvents, firstNewEvent, lane.id), stampPendingLane(ctx, lane.id);
|
|
19062
19174
|
});
|
|
@@ -19093,7 +19205,11 @@ function processScopedNoteEvent(ctx, event, measureStartTime, beatOffset, tuplet
|
|
|
19093
19205
|
event,
|
|
19094
19206
|
measureStartTime,
|
|
19095
19207
|
beatOffset,
|
|
19096
|
-
dynamicVelocityAt(
|
|
19208
|
+
dynamicVelocityAt(
|
|
19209
|
+
ctx,
|
|
19210
|
+
measureStartTime,
|
|
19211
|
+
realizeRhythmEvent(ctx.rhythm, event, beatOffset, durationBeats4(event.duration) * tupletRatio, ctx.playedSpan).start
|
|
19212
|
+
),
|
|
19097
19213
|
tupletRatio
|
|
19098
19214
|
);
|
|
19099
19215
|
return stampPlaybackLane(ctx.out, firstNewEvent, lane.id), stampPendingLane(ctx, lane.id), ctx.dynamicsEnvelope = previousEnvelope, ctx.playbackLaneId = previousLaneId, beats;
|
|
@@ -19120,13 +19236,13 @@ function processContentItem(ctx, item, measureStartTime, beatOffset, tupletRatio
|
|
|
19120
19236
|
return 0;
|
|
19121
19237
|
}
|
|
19122
19238
|
}
|
|
19123
|
-
function resolveFermataDuration(ctx, event, naturalDurationSec, beatOffset) {
|
|
19239
|
+
function resolveFermataDuration(ctx, event, naturalDurationSec, beatOffset, soundingBeatOffset = beatOffset) {
|
|
19124
19240
|
if (event.fermata && event.fermata.duration !== "none" && ctx.fermataGroups.length > 0) {
|
|
19125
19241
|
let globalBeatOffset = beatOffset * ctx.staffMeterRatio, group = ctx.fermataGroups.find(
|
|
19126
19242
|
(g) => globalBeatOffset >= g.startBeat - 1e-9 && globalBeatOffset < g.spanEndBeat - 1e-9
|
|
19127
19243
|
);
|
|
19128
19244
|
if (group) {
|
|
19129
|
-
let startTime = eventTime(ctx,
|
|
19245
|
+
let startTime = eventTime(ctx, soundingBeatOffset), resumeTime = ctx.model.timeAtBeat(ctx.measureStartBeat + group.insertBeat);
|
|
19130
19246
|
return { durationSec: Math.max(naturalDurationSec, resumeTime - startTime), durationScale: 1, held: !0 };
|
|
19131
19247
|
}
|
|
19132
19248
|
}
|
|
@@ -19147,6 +19263,7 @@ function processNoteEvent(ctx, event, measureStartTime, beatOffset, baseVelocity
|
|
|
19147
19263
|
if (!beatInPlayedSpan(beatOffset * ctx.staffMeterRatio, ctx.playedSpan)) return beats;
|
|
19148
19264
|
if (isRest(event))
|
|
19149
19265
|
return flushVoiceLegato(ctx.pendingLegato, ctx.voiceKey, ctx.out), beats;
|
|
19266
|
+
let realized = realizeRhythmEvent(ctx.rhythm, event, beatOffset, beats, ctx.playedSpan), startTime = eventTime(ctx, realized.start), naturalDurationSec = eventSeconds(ctx, realized.start, realized.beats);
|
|
19150
19267
|
resolveVoiceLegato({
|
|
19151
19268
|
map: ctx.pendingLegato,
|
|
19152
19269
|
voiceKey: ctx.voiceKey,
|
|
@@ -19157,15 +19274,17 @@ function processNoteEvent(ctx, event, measureStartTime, beatOffset, baseVelocity
|
|
|
19157
19274
|
measureStartTime,
|
|
19158
19275
|
beatOffset,
|
|
19159
19276
|
beats,
|
|
19160
|
-
notes: event.notes ?? []
|
|
19277
|
+
notes: event.notes ?? [],
|
|
19278
|
+
soundingOnset: startTime,
|
|
19279
|
+
soundingDuration: naturalDurationSec
|
|
19161
19280
|
});
|
|
19162
19281
|
let specialBeats = tryProcessSpecialEvent(ctx, event, measureStartTime, beatOffset, baseVelocity, beats);
|
|
19163
19282
|
if (specialBeats !== null) return specialBeats;
|
|
19164
|
-
let
|
|
19283
|
+
let timingOffset = timingHumanize(measureStartTime, beatOffset, partIndex), {
|
|
19165
19284
|
durationSec,
|
|
19166
19285
|
durationScale,
|
|
19167
19286
|
held: fermataHeld
|
|
19168
|
-
} = resolveFermataDuration(ctx, event, naturalDurationSec, beatOffset), velocity = Math.min(
|
|
19287
|
+
} = resolveFermataDuration(ctx, event, naturalDurationSec, beatOffset, realized.start), velocity = Math.min(
|
|
19169
19288
|
127,
|
|
19170
19289
|
Math.max(
|
|
19171
19290
|
1,
|