musicxml-io 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -8
- package/dist/abc-HXITSXCS.js +9 -0
- package/dist/abc-IAD53SFI.mjs +11 -0
- package/dist/abc-MTGVOYKS.js +11 -0
- package/dist/abc-R7DIJSHG.mjs +9 -0
- package/dist/abc.d.mts +67 -0
- package/dist/abc.d.ts +67 -0
- package/dist/abc.js +17 -0
- package/dist/abc.mjs +17 -0
- package/dist/accessors/index.d.mts +1 -1
- package/dist/accessors/index.d.ts +1 -1
- package/dist/browser.d.mts +11 -187
- package/dist/browser.d.ts +11 -187
- package/dist/browser.js +30 -6
- package/dist/browser.mjs +96 -72
- package/dist/chunk-2EH2MGBY.js +1799 -0
- package/dist/chunk-33IQXDOH.js +1 -0
- package/dist/chunk-5EK7K5X3.js +29 -0
- package/dist/chunk-5VF442RA.mjs +29 -0
- package/dist/chunk-626TXMVP.js +706 -0
- package/dist/chunk-63UT3IEY.mjs +2788 -0
- package/dist/chunk-6EKVRPFF.js +407 -0
- package/dist/chunk-776XPK66.js +45 -0
- package/dist/chunk-7D4MLQKO.js +69 -0
- package/dist/chunk-A2CGW2UF.js +25 -0
- package/dist/chunk-AOSK4RGV.mjs +45 -0
- package/dist/{chunk-FQ3NEYIV.js → chunk-ASPCIBWD.js} +127 -1249
- package/dist/chunk-BDHPMB7Q.mjs +61 -0
- package/dist/chunk-BHHOS2E6.mjs +407 -0
- package/dist/chunk-F2UCOAO6.mjs +155 -0
- package/dist/chunk-HP4DAUGT.js +2788 -0
- package/dist/chunk-JF5TM534.js +1065 -0
- package/dist/chunk-K6DSQPVF.mjs +706 -0
- package/dist/chunk-KJK2IGNN.mjs +0 -0
- package/dist/chunk-KYGLANYG.mjs +2354 -0
- package/dist/chunk-LFQXT7PV.mjs +69 -0
- package/dist/chunk-LS5SHP75.js +155 -0
- package/dist/chunk-MMIZQMIO.mjs +25 -0
- package/dist/chunk-NIUEAHPN.js +2354 -0
- package/dist/{chunk-EWLB5X64.js → chunk-PMPROVJW.js} +66 -770
- package/dist/chunk-QFYOH4DY.mjs +0 -0
- package/dist/chunk-QKUNJEVW.mjs +1401 -0
- package/dist/chunk-SBT2RMUW.mjs +1065 -0
- package/dist/chunk-TCXDBRRT.mjs +1799 -0
- package/dist/chunk-WNQIUYWY.js +61 -0
- package/dist/{chunk-I4X4GRMP.mjs → chunk-WS5LSW3S.mjs} +13 -1135
- package/dist/chunk-YKMNCYOD.js +1 -0
- package/dist/{chunk-UMEH3ENQ.mjs → chunk-ZRKJKEP7.mjs} +0 -704
- package/dist/chunk-ZSEJQPBO.js +1401 -0
- package/dist/core.d.mts +33 -0
- package/dist/core.d.ts +33 -0
- package/dist/core.js +524 -0
- package/dist/core.mjs +524 -0
- package/dist/file.d.mts +37 -0
- package/dist/file.d.ts +37 -0
- package/dist/file.js +14 -0
- package/dist/file.mjs +14 -0
- package/dist/index-C9lLZwol.d.mts +185 -0
- package/dist/index-DNA970t1.d.ts +185 -0
- package/dist/index.d.mts +12 -38
- package/dist/index.d.ts +12 -38
- package/dist/index.js +34 -49
- package/dist/index.mjs +101 -116
- package/dist/midi.d.mts +54 -0
- package/dist/midi.d.ts +54 -0
- package/dist/midi.js +10 -0
- package/dist/midi.mjs +10 -0
- package/dist/musicxml-DWPClEqa.d.ts +25 -0
- package/dist/musicxml-DzuFr2c3.d.mts +25 -0
- package/dist/musicxml-compressed-7NUEGLSF.mjs +8 -0
- package/dist/musicxml-compressed-7RL6QRF7.js +12 -0
- package/dist/musicxml-compressed-WPEIBOZ3.js +8 -0
- package/dist/musicxml-compressed-ZHZYFJ7H.mjs +12 -0
- package/dist/musicxml-compressed.d.mts +33 -0
- package/dist/musicxml-compressed.d.ts +33 -0
- package/dist/musicxml-compressed.js +18 -0
- package/dist/musicxml-compressed.mjs +18 -0
- package/dist/musicxml.d.mts +8 -0
- package/dist/musicxml.d.ts +8 -0
- package/dist/musicxml.js +14 -0
- package/dist/musicxml.mjs +14 -0
- package/dist/operations/index.d.mts +1234 -2
- package/dist/operations/index.d.ts +1234 -2
- package/dist/operations/index.js +7 -3
- package/dist/operations/index.mjs +6 -2
- package/dist/playback-timeline-CVRHsikj.d.mts +129 -0
- package/dist/playback-timeline-CmWA8YDy.d.ts +129 -0
- package/dist/query/index.d.mts +3 -128
- package/dist/query/index.d.ts +3 -128
- package/dist/query/index.js +4 -2
- package/dist/query/index.mjs +7 -5
- package/dist/{types-Cy0veafI.d.mts → types-B-RGHY0c.d.mts} +1 -1
- package/dist/{types-Cy0veafI.d.ts → types-B-RGHY0c.d.ts} +1 -1
- package/package.json +36 -2
- package/dist/chunk-T2USSS5E.mjs +0 -8939
- package/dist/chunk-UT7LQW6V.js +0 -8939
- package/dist/index-CbUtBHLD.d.ts +0 -1415
- package/dist/index-CuMPBK7U.d.mts +0 -1415
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/query/playback-sequence.ts
|
|
2
|
+
function parseEndingNumbers(numberStr) {
|
|
3
|
+
const numbers = [];
|
|
4
|
+
const parts = numberStr.split(/[,\s]+/);
|
|
5
|
+
for (const part of parts) {
|
|
6
|
+
if (part.includes("-")) {
|
|
7
|
+
const [start, end] = part.split("-").map((s) => parseInt(s.trim(), 10));
|
|
8
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
9
|
+
for (let i = start; i <= end; i++) {
|
|
10
|
+
numbers.push(i);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
} else {
|
|
14
|
+
const num = parseInt(part.trim(), 10);
|
|
15
|
+
if (!isNaN(num)) {
|
|
16
|
+
numbers.push(num);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return numbers.length > 0 ? numbers : [1];
|
|
21
|
+
}
|
|
22
|
+
function parseJumpText(text) {
|
|
23
|
+
const t = text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
24
|
+
if (t.includes("d.c. al coda") || t.includes("da capo al coda")) return "dc_al_coda";
|
|
25
|
+
if (t.includes("d.c. al fine") || t.includes("da capo al fine")) return "dc_al_fine";
|
|
26
|
+
if (t.includes("d.c.") || t.includes("da capo")) return "dc";
|
|
27
|
+
if (t.includes("d.s. al coda") || t.includes("dal segno al coda")) return "ds_al_coda";
|
|
28
|
+
if (t.includes("d.s. al fine") || t.includes("dal segno al fine")) return "ds_al_fine";
|
|
29
|
+
if (t.includes("d.s.") || t.includes("dal segno")) return "ds";
|
|
30
|
+
if (t === "fine") return "fine";
|
|
31
|
+
if (t.includes("to coda") || t.includes("al coda")) return "to_coda";
|
|
32
|
+
if (t === "coda" || t === "\u{1D14C}") return "coda";
|
|
33
|
+
if (t === "segno" || t === "\u{1D10B}") return "segno";
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function getFirstWordsText(entry) {
|
|
37
|
+
for (const dt of entry.directionTypes) {
|
|
38
|
+
if (dt.kind === "words" && dt.text) return dt.text;
|
|
39
|
+
}
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
function hasDirectionKind(entry, kind) {
|
|
43
|
+
return entry.directionTypes.some((dt) => dt.kind === kind);
|
|
44
|
+
}
|
|
45
|
+
function extractPlaybackControls(part) {
|
|
46
|
+
const controls = {
|
|
47
|
+
repeatStarts: [],
|
|
48
|
+
repeatEnds: [],
|
|
49
|
+
voltas: [],
|
|
50
|
+
jumps: [],
|
|
51
|
+
segnoIndex: null,
|
|
52
|
+
codaIndex: null,
|
|
53
|
+
fineIndex: null,
|
|
54
|
+
toCodaIndex: null
|
|
55
|
+
};
|
|
56
|
+
const soundJumps = [];
|
|
57
|
+
for (let measureIndex = 0; measureIndex < part.measures.length; measureIndex++) {
|
|
58
|
+
const measure = part.measures[measureIndex];
|
|
59
|
+
if (measure.barlines) {
|
|
60
|
+
for (const barline of measure.barlines) {
|
|
61
|
+
if (barline.repeat) {
|
|
62
|
+
if (barline.repeat.direction === "forward") {
|
|
63
|
+
controls.repeatStarts.push(measureIndex);
|
|
64
|
+
} else if (barline.repeat.direction === "backward") {
|
|
65
|
+
controls.repeatEnds.push({
|
|
66
|
+
measureIndex,
|
|
67
|
+
times: _nullishCoalesce(barline.repeat.times, () => ( 2))
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (barline.ending) {
|
|
72
|
+
const numbers = parseEndingNumbers(barline.ending.number);
|
|
73
|
+
controls.voltas.push({
|
|
74
|
+
measureIndex,
|
|
75
|
+
numbers,
|
|
76
|
+
type: barline.ending.type
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const entry of measure.entries) {
|
|
82
|
+
if (entry.type === "direction") {
|
|
83
|
+
const dir = entry;
|
|
84
|
+
if (hasDirectionKind(dir, "segno") && controls.segnoIndex === null) {
|
|
85
|
+
controls.segnoIndex = measureIndex;
|
|
86
|
+
}
|
|
87
|
+
if (hasDirectionKind(dir, "coda") && controls.codaIndex === null) {
|
|
88
|
+
controls.codaIndex = measureIndex;
|
|
89
|
+
}
|
|
90
|
+
const words = getFirstWordsText(dir);
|
|
91
|
+
if (words) {
|
|
92
|
+
const jt = parseJumpText(words);
|
|
93
|
+
if (jt === "fine") {
|
|
94
|
+
if (controls.fineIndex === null) controls.fineIndex = measureIndex;
|
|
95
|
+
} else if (jt === "to_coda") {
|
|
96
|
+
if (controls.toCodaIndex === null) controls.toCodaIndex = measureIndex;
|
|
97
|
+
} else if (jt === "coda") {
|
|
98
|
+
if (controls.codaIndex === null) controls.codaIndex = measureIndex;
|
|
99
|
+
} else if (jt === "segno") {
|
|
100
|
+
if (controls.segnoIndex === null) controls.segnoIndex = measureIndex;
|
|
101
|
+
} else if (jt) {
|
|
102
|
+
controls.jumps.push({ measureIndex, type: jt });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else if (entry.type === "sound") {
|
|
106
|
+
const sound = entry;
|
|
107
|
+
if (sound.segno && controls.segnoIndex === null) controls.segnoIndex = measureIndex;
|
|
108
|
+
if (sound.coda && controls.codaIndex === null) controls.codaIndex = measureIndex;
|
|
109
|
+
if (sound.tocoda && controls.toCodaIndex === null) controls.toCodaIndex = measureIndex;
|
|
110
|
+
if (sound.fine && controls.fineIndex === null) controls.fineIndex = measureIndex;
|
|
111
|
+
if (sound.dacapo) soundJumps.push({ measureIndex, base: "dc" });
|
|
112
|
+
if (sound.dalsegno) soundJumps.push({ measureIndex, base: "ds" });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const sj of soundJumps) {
|
|
117
|
+
if (controls.jumps.some((j) => j.measureIndex === sj.measureIndex)) continue;
|
|
118
|
+
let type;
|
|
119
|
+
if (controls.fineIndex !== null) {
|
|
120
|
+
type = sj.base === "dc" ? "dc_al_fine" : "ds_al_fine";
|
|
121
|
+
} else if (controls.toCodaIndex !== null || controls.codaIndex !== null) {
|
|
122
|
+
type = sj.base === "dc" ? "dc_al_coda" : "ds_al_coda";
|
|
123
|
+
} else {
|
|
124
|
+
type = sj.base;
|
|
125
|
+
}
|
|
126
|
+
controls.jumps.push({ measureIndex: sj.measureIndex, type });
|
|
127
|
+
}
|
|
128
|
+
return controls;
|
|
129
|
+
}
|
|
130
|
+
function buildVoltaRanges(voltas, measureCount) {
|
|
131
|
+
const voltaRanges = /* @__PURE__ */ new Map();
|
|
132
|
+
let currentVoltaStart = null;
|
|
133
|
+
let currentVoltaNumbers = [];
|
|
134
|
+
for (const volta of voltas) {
|
|
135
|
+
if (volta.type === "start") {
|
|
136
|
+
currentVoltaStart = volta.measureIndex;
|
|
137
|
+
currentVoltaNumbers = volta.numbers;
|
|
138
|
+
} else if ((volta.type === "stop" || volta.type === "discontinue") && currentVoltaStart !== null) {
|
|
139
|
+
for (let i = currentVoltaStart; i <= volta.measureIndex; i++) {
|
|
140
|
+
voltaRanges.set(i, currentVoltaNumbers);
|
|
141
|
+
}
|
|
142
|
+
currentVoltaStart = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (currentVoltaStart !== null) {
|
|
146
|
+
for (let i = currentVoltaStart; i < measureCount; i++) {
|
|
147
|
+
voltaRanges.set(i, currentVoltaNumbers);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return voltaRanges;
|
|
151
|
+
}
|
|
152
|
+
function generatePlaybackSequence(score, options) {
|
|
153
|
+
const partIndex = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.partIndex]), () => ( 0));
|
|
154
|
+
const part = score.parts[partIndex];
|
|
155
|
+
if (!part) return [];
|
|
156
|
+
const measureCount = part.measures.length;
|
|
157
|
+
const sequence = [];
|
|
158
|
+
if (measureCount === 0) return sequence;
|
|
159
|
+
const controls = extractPlaybackControls(part);
|
|
160
|
+
if (controls.repeatStarts.length === 0 && controls.repeatEnds.length === 0 && controls.voltas.length === 0 && controls.jumps.length === 0) {
|
|
161
|
+
for (let i = 0; i < measureCount; i++) {
|
|
162
|
+
sequence.push({ measureIndex: i, repeatIteration: 0 });
|
|
163
|
+
}
|
|
164
|
+
return sequence;
|
|
165
|
+
}
|
|
166
|
+
const voltaRanges = buildVoltaRanges(controls.voltas, measureCount);
|
|
167
|
+
const sortedRepeatStarts = [...controls.repeatStarts].sort((a, b) => a - b);
|
|
168
|
+
const sortedRepeatEnds = [...controls.repeatEnds].sort((a, b) => a.measureIndex - b.measureIndex);
|
|
169
|
+
const repeatSections = /* @__PURE__ */ new Map();
|
|
170
|
+
const voltaToRepeatEnd = /* @__PURE__ */ new Map();
|
|
171
|
+
const usedRepeatStarts = /* @__PURE__ */ new Set();
|
|
172
|
+
let lastRepeatEndMeasure = -1;
|
|
173
|
+
for (const repeatEnd of sortedRepeatEnds) {
|
|
174
|
+
let startIndex = null;
|
|
175
|
+
for (const startMeasure of sortedRepeatStarts) {
|
|
176
|
+
if (startMeasure <= repeatEnd.measureIndex && startMeasure > lastRepeatEndMeasure && !usedRepeatStarts.has(startMeasure)) {
|
|
177
|
+
startIndex = startMeasure;
|
|
178
|
+
} else if (startMeasure > repeatEnd.measureIndex) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (startIndex === null) {
|
|
183
|
+
startIndex = lastRepeatEndMeasure + 1;
|
|
184
|
+
} else {
|
|
185
|
+
usedRepeatStarts.add(startIndex);
|
|
186
|
+
}
|
|
187
|
+
repeatSections.set(repeatEnd.measureIndex, {
|
|
188
|
+
startIndex,
|
|
189
|
+
times: repeatEnd.times
|
|
190
|
+
});
|
|
191
|
+
lastRepeatEndMeasure = repeatEnd.measureIndex;
|
|
192
|
+
voltaRanges.forEach((_iterations, voltaMeasure) => {
|
|
193
|
+
const isInsideRepeat = voltaMeasure >= startIndex && voltaMeasure <= repeatEnd.measureIndex;
|
|
194
|
+
const isImmediatelyAfter = voltaMeasure > repeatEnd.measureIndex && !voltaToRepeatEnd.has(voltaMeasure);
|
|
195
|
+
if (isInsideRepeat) {
|
|
196
|
+
voltaToRepeatEnd.set(voltaMeasure, repeatEnd.measureIndex);
|
|
197
|
+
} else if (isImmediatelyAfter) {
|
|
198
|
+
let belongsToLaterRepeat = false;
|
|
199
|
+
for (const otherEnd of sortedRepeatEnds) {
|
|
200
|
+
if (otherEnd.measureIndex > repeatEnd.measureIndex) {
|
|
201
|
+
const otherSection = repeatSections.get(otherEnd.measureIndex);
|
|
202
|
+
if (otherSection && voltaMeasure >= otherSection.startIndex && voltaMeasure <= otherEnd.measureIndex) {
|
|
203
|
+
belongsToLaterRepeat = true;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (!belongsToLaterRepeat) {
|
|
209
|
+
voltaToRepeatEnd.set(voltaMeasure, repeatEnd.measureIndex);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
let currentMeasure = 0;
|
|
215
|
+
const repeatCounts = /* @__PURE__ */ new Map();
|
|
216
|
+
let lastRepeatEndIndex = null;
|
|
217
|
+
let lastRepeatIteration = 0;
|
|
218
|
+
let inRepeatJump = false;
|
|
219
|
+
let stopAtFine = false;
|
|
220
|
+
let jumpToCoda = false;
|
|
221
|
+
const maxIterations = measureCount * 10 + 10;
|
|
222
|
+
let iterations = 0;
|
|
223
|
+
while (currentMeasure < measureCount && iterations < maxIterations) {
|
|
224
|
+
iterations++;
|
|
225
|
+
const voltaIterations = voltaRanges.get(currentMeasure);
|
|
226
|
+
let repeatIteration = 0;
|
|
227
|
+
let inRepeatSection = false;
|
|
228
|
+
let currentRepeatEndIndex = null;
|
|
229
|
+
for (const [endIndex, section] of repeatSections) {
|
|
230
|
+
if (currentMeasure >= section.startIndex && currentMeasure <= endIndex) {
|
|
231
|
+
inRepeatSection = true;
|
|
232
|
+
currentRepeatEndIndex = endIndex;
|
|
233
|
+
repeatIteration = repeatCounts.get(endIndex) || 1;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (inRepeatSection && currentRepeatEndIndex !== null) {
|
|
238
|
+
lastRepeatEndIndex = currentRepeatEndIndex;
|
|
239
|
+
lastRepeatIteration = repeatIteration;
|
|
240
|
+
}
|
|
241
|
+
if (voltaIterations) {
|
|
242
|
+
let effectiveIteration = repeatIteration;
|
|
243
|
+
if (!inRepeatSection) {
|
|
244
|
+
const associatedRepeatEnd = voltaToRepeatEnd.get(currentMeasure);
|
|
245
|
+
if (associatedRepeatEnd !== void 0) {
|
|
246
|
+
effectiveIteration = repeatCounts.get(associatedRepeatEnd) || 1;
|
|
247
|
+
} else if (lastRepeatEndIndex !== null) {
|
|
248
|
+
effectiveIteration = lastRepeatIteration;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!voltaIterations.includes(effectiveIteration)) {
|
|
252
|
+
currentMeasure++;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
sequence.push({
|
|
257
|
+
measureIndex: currentMeasure,
|
|
258
|
+
repeatIteration: inRepeatSection ? repeatIteration : 0
|
|
259
|
+
});
|
|
260
|
+
if (stopAtFine && controls.fineIndex === currentMeasure) {
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
if (jumpToCoda && controls.toCodaIndex === currentMeasure && controls.codaIndex !== null) {
|
|
264
|
+
currentMeasure = controls.codaIndex;
|
|
265
|
+
jumpToCoda = false;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const repeatSection = repeatSections.get(currentMeasure);
|
|
269
|
+
if (repeatSection) {
|
|
270
|
+
const currentCount = repeatCounts.get(currentMeasure) || 1;
|
|
271
|
+
if (currentCount < repeatSection.times) {
|
|
272
|
+
repeatCounts.set(currentMeasure, currentCount + 1);
|
|
273
|
+
currentMeasure = repeatSection.startIndex;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (voltaIterations && !inRepeatSection) {
|
|
278
|
+
const associatedRepeatEnd = voltaToRepeatEnd.get(currentMeasure);
|
|
279
|
+
if (associatedRepeatEnd !== void 0) {
|
|
280
|
+
const repeatSectionForVolta = repeatSections.get(associatedRepeatEnd);
|
|
281
|
+
if (repeatSectionForVolta) {
|
|
282
|
+
const currentCount = repeatCounts.get(associatedRepeatEnd) || 1;
|
|
283
|
+
if (currentCount < repeatSectionForVolta.times) {
|
|
284
|
+
repeatCounts.set(associatedRepeatEnd, currentCount + 1);
|
|
285
|
+
currentMeasure = repeatSectionForVolta.startIndex;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const jumpAtMeasure = controls.jumps.find((j) => j.measureIndex === currentMeasure);
|
|
292
|
+
if (jumpAtMeasure && !inRepeatJump) {
|
|
293
|
+
inRepeatJump = true;
|
|
294
|
+
switch (jumpAtMeasure.type) {
|
|
295
|
+
case "dc":
|
|
296
|
+
currentMeasure = 0;
|
|
297
|
+
continue;
|
|
298
|
+
case "dc_al_fine":
|
|
299
|
+
currentMeasure = 0;
|
|
300
|
+
stopAtFine = true;
|
|
301
|
+
continue;
|
|
302
|
+
case "dc_al_coda":
|
|
303
|
+
currentMeasure = 0;
|
|
304
|
+
jumpToCoda = true;
|
|
305
|
+
continue;
|
|
306
|
+
case "ds":
|
|
307
|
+
if (controls.segnoIndex !== null) {
|
|
308
|
+
currentMeasure = controls.segnoIndex;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
break;
|
|
312
|
+
case "ds_al_fine":
|
|
313
|
+
if (controls.segnoIndex !== null) {
|
|
314
|
+
currentMeasure = controls.segnoIndex;
|
|
315
|
+
stopAtFine = true;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case "ds_al_coda":
|
|
320
|
+
if (controls.segnoIndex !== null) {
|
|
321
|
+
currentMeasure = controls.segnoIndex;
|
|
322
|
+
jumpToCoda = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
currentMeasure++;
|
|
329
|
+
}
|
|
330
|
+
return sequence;
|
|
331
|
+
}
|
|
332
|
+
function hasPlaybackControls(score, options) {
|
|
333
|
+
const part = score.parts[_nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.partIndex]), () => ( 0))];
|
|
334
|
+
if (!part) return false;
|
|
335
|
+
const controls = extractPlaybackControls(part);
|
|
336
|
+
return controls.repeatStarts.length > 0 || controls.repeatEnds.length > 0 || controls.voltas.length > 0 || controls.jumps.length > 0 || controls.segnoIndex !== null || controls.codaIndex !== null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/query/playback-timeline.ts
|
|
340
|
+
var DEFAULT_FERMATA_HOLD = 1.75;
|
|
341
|
+
var DEFAULT_CAESURA_SECONDS = 0.4;
|
|
342
|
+
var DEFAULT_BREATH_SECONDS = 0.25;
|
|
343
|
+
var DEFAULT_RAMP_STEPS = 12;
|
|
344
|
+
function metronomeBpm(perMinute) {
|
|
345
|
+
if (perMinute === void 0) return null;
|
|
346
|
+
const bpm = typeof perMinute === "number" ? perMinute : parseFloat(perMinute);
|
|
347
|
+
return isNaN(bpm) ? null : bpm;
|
|
348
|
+
}
|
|
349
|
+
function measureMaxPosition(measure) {
|
|
350
|
+
let position = 0;
|
|
351
|
+
let max = 0;
|
|
352
|
+
for (const entry of measure.entries) {
|
|
353
|
+
if (entry.type === "note") {
|
|
354
|
+
if (!entry.chord && !entry.grace) {
|
|
355
|
+
position += entry.duration;
|
|
356
|
+
if (position > max) max = position;
|
|
357
|
+
}
|
|
358
|
+
} else if (entry.type === "backup") {
|
|
359
|
+
position -= entry.duration;
|
|
360
|
+
} else if (entry.type === "forward") {
|
|
361
|
+
position += entry.duration;
|
|
362
|
+
if (position > max) max = position;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return max;
|
|
366
|
+
}
|
|
367
|
+
function findTimeSignature(part, measureNumber) {
|
|
368
|
+
const targetMeasure = parseInt(String(measureNumber), 10);
|
|
369
|
+
let time;
|
|
370
|
+
for (const measure of part.measures) {
|
|
371
|
+
const mNum = parseInt(measure.number, 10);
|
|
372
|
+
if (!isNaN(targetMeasure) && !isNaN(mNum) && mNum > targetMeasure) break;
|
|
373
|
+
if (_optionalChain([measure, 'access', _3 => _3.attributes, 'optionalAccess', _4 => _4.time])) {
|
|
374
|
+
time = {
|
|
375
|
+
beats: parseInt(measure.attributes.time.beats, 10) || 4,
|
|
376
|
+
beatType: measure.attributes.time.beatType
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return time;
|
|
381
|
+
}
|
|
382
|
+
function measureEndTick(measure, part, divisions, measureStartTick, maxPosition, ticksPerQuarterNote) {
|
|
383
|
+
const actualTicks = Math.round(maxPosition * ticksPerQuarterNote / divisions);
|
|
384
|
+
if (actualTicks > 0) {
|
|
385
|
+
return measureStartTick + actualTicks;
|
|
386
|
+
}
|
|
387
|
+
if (!measure.implicit) {
|
|
388
|
+
const timeAttrs = findTimeSignature(part, measure.number);
|
|
389
|
+
if (timeAttrs) {
|
|
390
|
+
const measureDuration = timeAttrs.beats / timeAttrs.beatType * 4 * divisions;
|
|
391
|
+
const calculatedTicks = Math.round(measureDuration * ticksPerQuarterNote / divisions);
|
|
392
|
+
return measureStartTick + calculatedTicks;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return measureStartTick;
|
|
396
|
+
}
|
|
397
|
+
function hasFermata(note) {
|
|
398
|
+
return _nullishCoalesce(_optionalChain([note, 'access', _5 => _5.notations, 'optionalAccess', _6 => _6.some, 'call', _7 => _7((n) => n.type === "fermata")]), () => ( false));
|
|
399
|
+
}
|
|
400
|
+
function pauseArticulation(note) {
|
|
401
|
+
if (!note.notations) return null;
|
|
402
|
+
for (const n of note.notations) {
|
|
403
|
+
if (n.type === "articulation") {
|
|
404
|
+
if (n.articulation === "caesura") return "caesura";
|
|
405
|
+
if (n.articulation === "breath-mark") return "breath";
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
function tempoRampWord(text) {
|
|
411
|
+
const t = text.trim().toLowerCase().replace(/[.\s]+$/, "");
|
|
412
|
+
if (!t) return null;
|
|
413
|
+
if (/^(rit|riten|rall|allarg|slarg|cala|smorz|morend)/.test(t)) return "rit";
|
|
414
|
+
if (/^(accel|string|affret|incalz|pi[uù]\s*mosso)/.test(t)) return "accel";
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
function buildGridTimeline(score, ticksPerQuarterNote, sequence, options = {}) {
|
|
418
|
+
const rawTempo = [];
|
|
419
|
+
const measures = [];
|
|
420
|
+
const rampMarkers = [];
|
|
421
|
+
const stretches = [];
|
|
422
|
+
if (score.parts.length === 0) {
|
|
423
|
+
return { tempoEvents: [], measures, totalTicks: 0, expressions: [] };
|
|
424
|
+
}
|
|
425
|
+
const fermataHold = _nullishCoalesce(options.fermataHoldMultiplier, () => ( DEFAULT_FERMATA_HOLD));
|
|
426
|
+
const caesuraSeconds = _nullishCoalesce(options.caesuraSeconds, () => ( DEFAULT_CAESURA_SECONDS));
|
|
427
|
+
const breathSeconds = _nullishCoalesce(options.breathSeconds, () => ( DEFAULT_BREATH_SECONDS));
|
|
428
|
+
const rampSteps = Math.max(1, Math.round(_nullishCoalesce(options.tempoRampSteps, () => ( DEFAULT_RAMP_STEPS))));
|
|
429
|
+
const defaultTempo = _nullishCoalesce(options.defaultTempo, () => ( 120));
|
|
430
|
+
const part = score.parts[0];
|
|
431
|
+
let divisions = 1;
|
|
432
|
+
let currentTick = 0;
|
|
433
|
+
let prevailingBpm = defaultTempo;
|
|
434
|
+
const pauseFactor = (windowTicks, bpm, pauseSeconds) => {
|
|
435
|
+
const baseSeconds = windowTicks / ticksPerQuarterNote * (60 / bpm);
|
|
436
|
+
if (baseSeconds <= 0) return 1;
|
|
437
|
+
return (baseSeconds + pauseSeconds) / baseSeconds;
|
|
438
|
+
};
|
|
439
|
+
for (const { measureIndex, repeatIteration } of sequence) {
|
|
440
|
+
const measure = part.measures[measureIndex];
|
|
441
|
+
if (!measure) continue;
|
|
442
|
+
if (_optionalChain([measure, 'access', _8 => _8.attributes, 'optionalAccess', _9 => _9.divisions])) {
|
|
443
|
+
divisions = measure.attributes.divisions;
|
|
444
|
+
}
|
|
445
|
+
const measureStartTick = currentTick;
|
|
446
|
+
let position = 0;
|
|
447
|
+
let chordBasePosition = 0;
|
|
448
|
+
const tickAt = (pos) => measureStartTick + Math.round(pos * ticksPerQuarterNote / divisions);
|
|
449
|
+
for (const entry of measure.entries) {
|
|
450
|
+
if (entry.type === "direction") {
|
|
451
|
+
for (const dirType of entry.directionTypes) {
|
|
452
|
+
if (dirType.kind === "metronome") {
|
|
453
|
+
const bpm = metronomeBpm(dirType.perMinute);
|
|
454
|
+
if (bpm !== null) {
|
|
455
|
+
rawTempo.push({ tick: tickAt(position), bpm });
|
|
456
|
+
prevailingBpm = bpm;
|
|
457
|
+
}
|
|
458
|
+
} else if (dirType.kind === "words") {
|
|
459
|
+
const kind = tempoRampWord(dirType.text);
|
|
460
|
+
if (kind) rampMarkers.push({ tick: tickAt(position), kind });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (_optionalChain([entry, 'access', _10 => _10.sound, 'optionalAccess', _11 => _11.tempo])) {
|
|
464
|
+
rawTempo.push({ tick: tickAt(position), bpm: entry.sound.tempo });
|
|
465
|
+
prevailingBpm = entry.sound.tempo;
|
|
466
|
+
}
|
|
467
|
+
} else if (entry.type === "sound") {
|
|
468
|
+
if (entry.tempo) {
|
|
469
|
+
rawTempo.push({ tick: tickAt(position), bpm: entry.tempo });
|
|
470
|
+
prevailingBpm = entry.tempo;
|
|
471
|
+
}
|
|
472
|
+
} else if (entry.type === "note") {
|
|
473
|
+
const notePos = entry.chord ? chordBasePosition : position;
|
|
474
|
+
const noteStartTick = tickAt(notePos);
|
|
475
|
+
const noteEndTick = tickAt(notePos + entry.duration);
|
|
476
|
+
if (noteEndTick > noteStartTick) {
|
|
477
|
+
if (fermataHold !== 1 && hasFermata(entry)) {
|
|
478
|
+
stretches.push({
|
|
479
|
+
startTick: noteStartTick,
|
|
480
|
+
endTick: noteEndTick,
|
|
481
|
+
factor: fermataHold,
|
|
482
|
+
type: "fermata"
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
const pause = pauseArticulation(entry);
|
|
486
|
+
if (pause) {
|
|
487
|
+
const seconds = pause === "caesura" ? caesuraSeconds : breathSeconds;
|
|
488
|
+
if (seconds > 0) {
|
|
489
|
+
const window = Math.min(noteEndTick - noteStartTick, ticksPerQuarterNote);
|
|
490
|
+
stretches.push({
|
|
491
|
+
startTick: noteEndTick - window,
|
|
492
|
+
endTick: noteEndTick,
|
|
493
|
+
factor: pauseFactor(window, prevailingBpm, seconds),
|
|
494
|
+
type: pause
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (!entry.chord) {
|
|
500
|
+
chordBasePosition = position;
|
|
501
|
+
position += entry.duration;
|
|
502
|
+
}
|
|
503
|
+
} else if (entry.type === "backup") {
|
|
504
|
+
position -= entry.duration;
|
|
505
|
+
} else if (entry.type === "forward") {
|
|
506
|
+
position += entry.duration;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const endTick = measureEndTick(
|
|
510
|
+
measure,
|
|
511
|
+
part,
|
|
512
|
+
divisions,
|
|
513
|
+
measureStartTick,
|
|
514
|
+
measureMaxPosition(measure),
|
|
515
|
+
ticksPerQuarterNote
|
|
516
|
+
);
|
|
517
|
+
measures.push({
|
|
518
|
+
measureIndex,
|
|
519
|
+
repeatIteration,
|
|
520
|
+
measureNumber: measure.number,
|
|
521
|
+
startTick: measureStartTick,
|
|
522
|
+
endTick
|
|
523
|
+
});
|
|
524
|
+
currentTick = endTick;
|
|
525
|
+
}
|
|
526
|
+
const { tempoEvents, expressions } = bakeTempoMap(
|
|
527
|
+
rawTempo,
|
|
528
|
+
rampMarkers,
|
|
529
|
+
stretches,
|
|
530
|
+
defaultTempo,
|
|
531
|
+
rampSteps
|
|
532
|
+
);
|
|
533
|
+
return { tempoEvents, measures, totalTicks: currentTick, expressions };
|
|
534
|
+
}
|
|
535
|
+
function bakeTempoMap(rawTempo, rampMarkers, stretches, defaultTempo, rampSteps) {
|
|
536
|
+
const sortedRaw = [...rawTempo].sort((a, b) => a.tick - b.tick);
|
|
537
|
+
const startBpm = sortedRaw.length > 0 && sortedRaw[0].tick === 0 ? sortedRaw[0].bpm : defaultTempo;
|
|
538
|
+
const baseChanges = [{ tick: 0, bpm: startBpm }];
|
|
539
|
+
let lastBpm = startBpm;
|
|
540
|
+
for (const ev of sortedRaw) {
|
|
541
|
+
if (ev.tick === 0) continue;
|
|
542
|
+
if (ev.bpm === lastBpm) continue;
|
|
543
|
+
if (baseChanges[baseChanges.length - 1].tick === ev.tick) {
|
|
544
|
+
baseChanges[baseChanges.length - 1].bpm = ev.bpm;
|
|
545
|
+
} else {
|
|
546
|
+
baseChanges.push({ tick: ev.tick, bpm: ev.bpm });
|
|
547
|
+
}
|
|
548
|
+
lastBpm = ev.bpm;
|
|
549
|
+
}
|
|
550
|
+
const baseBpmAt = (tick) => {
|
|
551
|
+
let i = baseChanges.length - 1;
|
|
552
|
+
while (i > 0 && baseChanges[i].tick > tick) i--;
|
|
553
|
+
return baseChanges[i].bpm;
|
|
554
|
+
};
|
|
555
|
+
const rampExpressions = [];
|
|
556
|
+
const rampPoints = [];
|
|
557
|
+
for (const marker of rampMarkers) {
|
|
558
|
+
const from = baseBpmAt(marker.tick);
|
|
559
|
+
const next = baseChanges.find((c) => c.tick > marker.tick && c.bpm !== from);
|
|
560
|
+
if (!next) continue;
|
|
561
|
+
const span = next.tick - marker.tick;
|
|
562
|
+
if (span <= 0) continue;
|
|
563
|
+
for (let k = 1; k < rampSteps; k++) {
|
|
564
|
+
const tick = Math.round(marker.tick + span * k / rampSteps);
|
|
565
|
+
if (tick <= marker.tick || tick >= next.tick) continue;
|
|
566
|
+
const bpm = from + (next.bpm - from) * k / rampSteps;
|
|
567
|
+
rampPoints.push({ tick, bpm });
|
|
568
|
+
}
|
|
569
|
+
rampExpressions.push({ type: marker.kind, startTick: marker.tick, endTick: next.tick });
|
|
570
|
+
}
|
|
571
|
+
const baseTicks = new Set(baseChanges.map((c) => c.tick));
|
|
572
|
+
const stepList = [...baseChanges];
|
|
573
|
+
for (const p of rampPoints) {
|
|
574
|
+
if (!baseTicks.has(p.tick)) stepList.push(p);
|
|
575
|
+
}
|
|
576
|
+
stepList.sort((a, b) => a.tick - b.tick);
|
|
577
|
+
const stepBpmAt = (tick) => {
|
|
578
|
+
let i = stepList.length - 1;
|
|
579
|
+
while (i > 0 && stepList[i].tick > tick) i--;
|
|
580
|
+
return stepList[i].bpm;
|
|
581
|
+
};
|
|
582
|
+
const boundaries = new Set(stepList.map((s) => s.tick));
|
|
583
|
+
for (const s of stretches) {
|
|
584
|
+
boundaries.add(s.startTick);
|
|
585
|
+
boundaries.add(s.endTick);
|
|
586
|
+
}
|
|
587
|
+
const sortedBoundaries = [...boundaries].sort((a, b) => a - b);
|
|
588
|
+
const tempoEvents = [];
|
|
589
|
+
let emittedBpm = null;
|
|
590
|
+
for (const tick of sortedBoundaries) {
|
|
591
|
+
let bpm = stepBpmAt(tick);
|
|
592
|
+
for (const s of stretches) {
|
|
593
|
+
if (tick >= s.startTick && tick < s.endTick) bpm /= s.factor;
|
|
594
|
+
}
|
|
595
|
+
if (emittedBpm === null || bpm !== emittedBpm) {
|
|
596
|
+
tempoEvents.push({ tick, bpm });
|
|
597
|
+
emittedBpm = bpm;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const expressions = [
|
|
601
|
+
...stretches.map((s) => ({ type: s.type, startTick: s.startTick, endTick: s.endTick })),
|
|
602
|
+
...rampExpressions
|
|
603
|
+
].sort((a, b) => a.startTick - b.startTick || a.endTick - b.endTick);
|
|
604
|
+
return { tempoEvents, expressions };
|
|
605
|
+
}
|
|
606
|
+
function bpmToUsPerQuarter(bpm) {
|
|
607
|
+
return Math.round(6e7 / bpm);
|
|
608
|
+
}
|
|
609
|
+
function makeTickToSec(tempoEvents, defaultTempo, ticksPerQuarterNote) {
|
|
610
|
+
const sorted = [...tempoEvents].sort((a, b) => a.tick - b.tick);
|
|
611
|
+
const startBpm = sorted.length > 0 && sorted[0].tick === 0 ? sorted[0].bpm : defaultTempo;
|
|
612
|
+
const changes = [
|
|
613
|
+
{ tick: 0, usPerQuarter: bpmToUsPerQuarter(startBpm) }
|
|
614
|
+
];
|
|
615
|
+
let lastBpm = startBpm;
|
|
616
|
+
for (const ev of sorted) {
|
|
617
|
+
if (ev.tick === 0) continue;
|
|
618
|
+
if (ev.bpm === lastBpm) continue;
|
|
619
|
+
changes.push({ tick: ev.tick, usPerQuarter: bpmToUsPerQuarter(ev.bpm) });
|
|
620
|
+
lastBpm = ev.bpm;
|
|
621
|
+
}
|
|
622
|
+
const secPerTick = (usPerQuarter) => usPerQuarter / 1e6 / ticksPerQuarterNote;
|
|
623
|
+
const cumSec = [0];
|
|
624
|
+
for (let i = 1; i < changes.length; i++) {
|
|
625
|
+
const dTick = changes[i].tick - changes[i - 1].tick;
|
|
626
|
+
cumSec[i] = cumSec[i - 1] + dTick * secPerTick(changes[i - 1].usPerQuarter);
|
|
627
|
+
}
|
|
628
|
+
return (tick) => {
|
|
629
|
+
let i = changes.length - 1;
|
|
630
|
+
while (i > 0 && changes[i].tick > tick) i--;
|
|
631
|
+
return cumSec[i] + (tick - changes[i].tick) * secPerTick(changes[i].usPerQuarter);
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function buildTimingSidecar(grid, defaultTempo, ticksPerQuarterNote) {
|
|
635
|
+
const tickToSec = makeTickToSec(grid.tempoEvents, defaultTempo, ticksPerQuarterNote);
|
|
636
|
+
const breakpoints = [];
|
|
637
|
+
const measureStartTicks = new Set(grid.measures.map((m) => m.startTick));
|
|
638
|
+
for (const m of grid.measures) {
|
|
639
|
+
breakpoints.push({
|
|
640
|
+
midiSec: tickToSec(m.startTick),
|
|
641
|
+
quarterPos: m.startTick / ticksPerQuarterNote,
|
|
642
|
+
measureNumber: m.measureNumber,
|
|
643
|
+
beatInMeasure: 0,
|
|
644
|
+
repeatIteration: m.repeatIteration
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
for (const ev of grid.tempoEvents) {
|
|
648
|
+
if (measureStartTicks.has(ev.tick)) continue;
|
|
649
|
+
const m = grid.measures.find((mm) => ev.tick >= mm.startTick && ev.tick < mm.endTick);
|
|
650
|
+
if (!m) continue;
|
|
651
|
+
breakpoints.push({
|
|
652
|
+
midiSec: tickToSec(ev.tick),
|
|
653
|
+
quarterPos: ev.tick / ticksPerQuarterNote,
|
|
654
|
+
measureNumber: m.measureNumber,
|
|
655
|
+
beatInMeasure: (ev.tick - m.startTick) / ticksPerQuarterNote,
|
|
656
|
+
repeatIteration: m.repeatIteration
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
const last = grid.measures[grid.measures.length - 1];
|
|
660
|
+
breakpoints.push({
|
|
661
|
+
midiSec: tickToSec(grid.totalTicks),
|
|
662
|
+
quarterPos: grid.totalTicks / ticksPerQuarterNote,
|
|
663
|
+
measureNumber: last ? last.measureNumber : "0",
|
|
664
|
+
beatInMeasure: last ? (grid.totalTicks - last.startTick) / ticksPerQuarterNote : 0,
|
|
665
|
+
repeatIteration: last ? last.repeatIteration : 0
|
|
666
|
+
});
|
|
667
|
+
breakpoints.sort((a, b) => a.midiSec - b.midiSec || a.quarterPos - b.quarterPos);
|
|
668
|
+
const sidecar = {
|
|
669
|
+
version: "1",
|
|
670
|
+
durationSec: tickToSec(grid.totalTicks),
|
|
671
|
+
ticksPerQuarterNote,
|
|
672
|
+
breakpoints
|
|
673
|
+
};
|
|
674
|
+
if (grid.expressions.length > 0) {
|
|
675
|
+
sidecar.expressions = grid.expressions.map((e) => ({
|
|
676
|
+
type: e.type,
|
|
677
|
+
fromMidiSec: tickToSec(e.startTick),
|
|
678
|
+
toMidiSec: tickToSec(e.endTick)
|
|
679
|
+
})).sort((a, b) => a.fromMidiSec - b.fromMidiSec || a.toMidiSec - b.toMidiSec);
|
|
680
|
+
}
|
|
681
|
+
return sidecar;
|
|
682
|
+
}
|
|
683
|
+
function generatePlaybackTimeline(score, options = {}) {
|
|
684
|
+
const ticksPerQuarterNote = _nullishCoalesce(options.ticksPerQuarterNote, () => ( 480));
|
|
685
|
+
const defaultTempo = _nullishCoalesce(options.defaultTempo, () => ( 120));
|
|
686
|
+
const sequence = generatePlaybackSequence(score);
|
|
687
|
+
const grid = buildGridTimeline(score, ticksPerQuarterNote, sequence, {
|
|
688
|
+
defaultTempo,
|
|
689
|
+
fermataHoldMultiplier: options.fermataHoldMultiplier,
|
|
690
|
+
caesuraSeconds: options.caesuraSeconds,
|
|
691
|
+
breathSeconds: options.breathSeconds,
|
|
692
|
+
tempoRampSteps: options.tempoRampSteps
|
|
693
|
+
});
|
|
694
|
+
return buildTimingSidecar(grid, defaultTempo, ticksPerQuarterNote);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
exports.extractPlaybackControls = extractPlaybackControls; exports.generatePlaybackSequence = generatePlaybackSequence; exports.hasPlaybackControls = hasPlaybackControls; exports.measureEndTick = measureEndTick; exports.buildGridTimeline = buildGridTimeline; exports.bpmToUsPerQuarter = bpmToUsPerQuarter; exports.buildTimingSidecar = buildTimingSidecar; exports.generatePlaybackTimeline = generatePlaybackTimeline;
|