@viritura/score-engine 0.1.1-next.8 → 0.1.1-next.9
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 +250 -36
- package/dist/index.js.map +4 -4
- package/dist/wasm/viritura_wasm_bg.wasm +0 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -53,6 +53,44 @@ function plainTextContent(content) {
|
|
|
53
53
|
return content.map((chunk) => "text" in chunk ? chunk.text : "").join("");
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// packages/core/src/model/instrumentChange.ts
|
|
57
|
+
init_define_import_meta_env();
|
|
58
|
+
var MEASURE_START = [0, 1];
|
|
59
|
+
function comparePositions(a, b) {
|
|
60
|
+
return a[0] * b[1] - b[0] * a[1];
|
|
61
|
+
}
|
|
62
|
+
function changePosition(change) {
|
|
63
|
+
return change.position?.fraction ?? MEASURE_START;
|
|
64
|
+
}
|
|
65
|
+
function initialInstrumentState(part) {
|
|
66
|
+
let ext = part._x?.viritura, instrumentKey = ext?.initialInstrument, instrument = instrumentKey !== void 0 && ext?.instruments && Object.hasOwn(ext.instruments, instrumentKey) ? ext.instruments[instrumentKey] : void 0, transposition = part.transposition ?? instrument?.transposition;
|
|
67
|
+
return {
|
|
68
|
+
...instrumentKey !== void 0 ? { instrumentKey } : {},
|
|
69
|
+
...instrument ? { instrument } : {},
|
|
70
|
+
...transposition ? { transposition } : {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function applyChange(part, state, change) {
|
|
74
|
+
if (change.instrument === void 0)
|
|
75
|
+
return { ...state, ...change.transposition ? { transposition: change.transposition } : {} };
|
|
76
|
+
let instruments = part._x?.viritura?.instruments, instrument = instruments && Object.hasOwn(instruments, change.instrument) ? instruments[change.instrument] : void 0, transposition = change.transposition ?? instrument?.transposition;
|
|
77
|
+
return {
|
|
78
|
+
instrumentKey: change.instrument,
|
|
79
|
+
...instrument ? { instrument } : {},
|
|
80
|
+
...transposition ? { transposition } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function listInstrumentChanges(part) {
|
|
84
|
+
let located = [], state = initialInstrumentState(part);
|
|
85
|
+
return part.measures.forEach((measure, measureIndex) => {
|
|
86
|
+
let ordered = [...measure.instrumentChanges ?? []].sort(
|
|
87
|
+
(a, b) => comparePositions(changePosition(a), changePosition(b))
|
|
88
|
+
);
|
|
89
|
+
for (let change of ordered)
|
|
90
|
+
state = applyChange(part, state, change), located.push({ measureIndex, position: changePosition(change), change, state });
|
|
91
|
+
}), located;
|
|
92
|
+
}
|
|
93
|
+
|
|
56
94
|
// packages/core/src/model/event.ts
|
|
57
95
|
init_define_import_meta_env();
|
|
58
96
|
function isRest(event) {
|
|
@@ -1870,9 +1908,9 @@ function interpolateBeatAnchors(bounds, beat, endX) {
|
|
|
1870
1908
|
|
|
1871
1909
|
// packages/score-engine/src/buildInfo.ts
|
|
1872
1910
|
init_define_import_meta_env();
|
|
1873
|
-
var PACKAGE_VERSION = "0.1.1-next.
|
|
1911
|
+
var PACKAGE_VERSION = "0.1.1-next.9", BUNDLE_WORKER_FILE = "score-engine.worker.js";
|
|
1874
1912
|
function buildCommit() {
|
|
1875
|
-
return "
|
|
1913
|
+
return "8f2a6533eef0ec77252c76925031cfbc732e3271";
|
|
1876
1914
|
}
|
|
1877
1915
|
function bundledAssetBase() {
|
|
1878
1916
|
let moduleUrl = import.meta.url;
|
|
@@ -2764,26 +2802,36 @@ function parsePart(raw) {
|
|
|
2764
2802
|
name: raw.name ?? "",
|
|
2765
2803
|
measures: parsePartMeasures(raw.measures)
|
|
2766
2804
|
};
|
|
2767
|
-
|
|
2768
|
-
let t = raw.transposition;
|
|
2769
|
-
part.transposition = {
|
|
2770
|
-
interval: {
|
|
2771
|
-
halfSteps: t.interval.halfSteps,
|
|
2772
|
-
staffDistance: t.interval.staffDistance
|
|
2773
|
-
}
|
|
2774
|
-
}, t.keyFifthsFlipAt !== void 0 && (part.transposition.keyFifthsFlipAt = t.keyFifthsFlipAt), t.prefersWrittenPitches !== void 0 && (part.transposition.prefersWrittenPitches = t.prefersWrittenPitches);
|
|
2775
|
-
}
|
|
2776
|
-
raw.kit && (part.kit = parseKit(raw.kit));
|
|
2805
|
+
raw.id && (part.id = raw.id), raw.shortName !== void 0 && (part.shortName = raw.shortName), raw.staves !== void 0 && (part.staves = raw.staves), raw.transposition && (part.transposition = parseTransposition(raw.transposition)), raw.kit && (part.kit = parseKit(raw.kit));
|
|
2777
2806
|
let viritura = raw._x?.viritura;
|
|
2778
2807
|
if (viritura) {
|
|
2779
2808
|
viritura.chordSymbolVisibility !== void 0 && (part.chordSymbolVisibility = viritura.chordSymbolVisibility);
|
|
2780
2809
|
let ext = {};
|
|
2781
2810
|
typeof viritura.instrumentId == "string" && (ext.instrumentId = viritura.instrumentId), typeof viritura.midiProgram == "number" && (ext.midiProgram = viritura.midiProgram), typeof viritura.family == "string" && (ext.family = viritura.family);
|
|
2782
2811
|
let sp = viritura.spatial;
|
|
2783
|
-
sp && typeof sp.x == "number" && typeof sp.y == "number" && (ext.spatial = { x: sp.x, y: sp.y }),
|
|
2812
|
+
sp && typeof sp.x == "number" && typeof sp.y == "number" && (ext.spatial = { x: sp.x, y: sp.y }), viritura.instruments && (ext.instruments = Object.fromEntries(
|
|
2813
|
+
Object.entries(viritura.instruments).map(([key, instrument]) => [key, parseInstrumentDefinition(instrument)])
|
|
2814
|
+
)), viritura.initialInstrument !== void 0 && (ext.initialInstrument = viritura.initialInstrument), Object.keys(ext).length > 0 && (part._x = { viritura: ext });
|
|
2784
2815
|
}
|
|
2785
2816
|
return part;
|
|
2786
2817
|
}
|
|
2818
|
+
function parseTransposition(t) {
|
|
2819
|
+
let transposition = {
|
|
2820
|
+
interval: {
|
|
2821
|
+
halfSteps: t.interval.halfSteps,
|
|
2822
|
+
staffDistance: t.interval.staffDistance
|
|
2823
|
+
}
|
|
2824
|
+
};
|
|
2825
|
+
return t.keyFifthsFlipAt !== void 0 && (transposition.keyFifthsFlipAt = t.keyFifthsFlipAt), t.prefersWrittenPitches !== void 0 && (transposition.prefersWrittenPitches = t.prefersWrittenPitches), transposition;
|
|
2826
|
+
}
|
|
2827
|
+
function parseInstrumentDefinition(raw) {
|
|
2828
|
+
let instrument = { instrumentId: raw.instrumentId };
|
|
2829
|
+
return raw.name !== void 0 && (instrument.name = raw.name), raw.shortName !== void 0 && (instrument.shortName = raw.shortName), raw.transposition && (instrument.transposition = parseTransposition(raw.transposition)), raw.midiProgram !== void 0 && (instrument.midiProgram = raw.midiProgram), instrument;
|
|
2830
|
+
}
|
|
2831
|
+
function parseInstrumentChange(raw) {
|
|
2832
|
+
let change = {};
|
|
2833
|
+
return raw.position && (change.position = parseRhythmicPosition(raw.position)), raw.instrument !== void 0 && (change.instrument = raw.instrument), raw.transposition && (change.transposition = parseTransposition(raw.transposition)), raw.instruction && (change.instruction = {}, raw.instruction.text !== void 0 && (change.instruction.text = raw.instruction.text), raw.instruction.hidden !== void 0 && (change.instruction.hidden = raw.instruction.hidden)), raw.reminder && (change.reminder = {}, raw.reminder.text !== void 0 && (change.reminder.text = raw.reminder.text), raw.reminder.hidden !== void 0 && (change.reminder.hidden = raw.reminder.hidden)), change;
|
|
2834
|
+
}
|
|
2787
2835
|
function parseKit(raw) {
|
|
2788
2836
|
let result = {};
|
|
2789
2837
|
for (let [id, value] of Object.entries(raw))
|
|
@@ -2812,7 +2860,7 @@ function parsePartMeasure(raw) {
|
|
|
2812
2860
|
return viritura && (viritura.pedals && (pm.pedals = viritura.pedals.map(parsePedal)), viritura.expressions && (pm.expressions = viritura.expressions.map(parseTextExpression)), viritura.condensingOverride && (pm.condensingOverride = viritura.condensingOverride), viritura.groupingDisplayOverrides && viritura.groupingDisplayOverrides.length > 0 && (pm.groupingDisplayOverrides = viritura.groupingDisplayOverrides.map((override) => ({
|
|
2813
2861
|
staff: override.staff,
|
|
2814
2862
|
groupingDisplay: override.groupingDisplay
|
|
2815
|
-
}))), viritura.staffMeters && viritura.staffMeters.length > 0 && (pm.staffMeters = viritura.staffMeters.map(parseStaffMeterChange))), pm;
|
|
2863
|
+
}))), viritura.staffMeters && viritura.staffMeters.length > 0 && (pm.staffMeters = viritura.staffMeters.map(parseStaffMeterChange)), viritura.instrumentChanges && viritura.instrumentChanges.length > 0 && (pm.instrumentChanges = viritura.instrumentChanges.map(parseInstrumentChange))), pm;
|
|
2816
2864
|
}
|
|
2817
2865
|
function parseStaffMeterChange(raw) {
|
|
2818
2866
|
if ("useGlobal" in raw)
|
|
@@ -2876,6 +2924,17 @@ function parsePositionedStaffConfig(raw) {
|
|
|
2876
2924
|
return raw.id !== void 0 && (positioned.id = raw.id), raw._c !== void 0 && (positioned._c = raw._c), raw._x !== void 0 && (positioned._x = raw._x), raw.position && (positioned.position = parseRhythmicPosition(raw.position)), raw.staff !== void 0 && (positioned.staff = raw.staff), positioned;
|
|
2877
2925
|
}
|
|
2878
2926
|
|
|
2927
|
+
// packages/format/src/mnx/parseInstrumentChangeStyle.ts
|
|
2928
|
+
init_define_import_meta_env();
|
|
2929
|
+
function parseRootInstrumentChangeStyle(rootX) {
|
|
2930
|
+
let raw = rootX?.viritura?.instrumentChangeStyle;
|
|
2931
|
+
if (raw)
|
|
2932
|
+
return {
|
|
2933
|
+
...typeof raw.showChangeLabel == "boolean" ? { showChangeLabel: raw.showChangeLabel } : {},
|
|
2934
|
+
...typeof raw.showAdvanceReminder == "boolean" ? { showAdvanceReminder: raw.showAdvanceReminder } : {}
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2879
2938
|
// packages/format/src/mnx/parseLayout.ts
|
|
2880
2939
|
init_define_import_meta_env();
|
|
2881
2940
|
function parseLayoutDefinition(raw) {
|
|
@@ -3184,6 +3243,8 @@ function applyRootExtensions(score, rootX) {
|
|
|
3184
3243
|
timeSignatures && (score.timeSignatures = timeSignatures);
|
|
3185
3244
|
let chordSymbolStyle = parseRootChordSymbolStyle(rootX);
|
|
3186
3245
|
chordSymbolStyle && (score.chordSymbolStyle = chordSymbolStyle);
|
|
3246
|
+
let instrumentChangeStyle = parseRootInstrumentChangeStyle(rootX);
|
|
3247
|
+
instrumentChangeStyle && (score.instrumentChangeStyle = instrumentChangeStyle);
|
|
3187
3248
|
let soundProfile = parseRootSoundProfile(rootX);
|
|
3188
3249
|
soundProfile && (score.soundProfile = soundProfile);
|
|
3189
3250
|
let videoSync = parseRootVideoSync(rootX);
|
|
@@ -4250,11 +4311,13 @@ function applyMeasureTechniques(expressions, ctx, state, caps, ratioForStaff = (
|
|
|
4250
4311
|
let marks = expressions.map((e) => ({
|
|
4251
4312
|
action: classifyTechniqueText(plainTextContent(e.text)),
|
|
4252
4313
|
beat: fractionToBeats(e.position.fraction) * ratioForStaff(e.staff)
|
|
4253
|
-
})).filter((m) => m.action !== null).sort((a, b) => a.beat - b.beat), { program, muted } = state;
|
|
4314
|
+
})).filter((m) => m.action !== null).sort((a, b) => a.beat - b.beat), { program, muted } = state, instrumentKey = state.instrumentKey ?? ctx.instrumentAtBeatOffset?.(0).key;
|
|
4254
4315
|
for (let mark of marks) {
|
|
4255
|
-
let
|
|
4256
|
-
|
|
4257
|
-
|
|
4316
|
+
let instrument = ctx.instrumentAtBeatOffset?.(mark.beat), baselineProgram = instrument?.program ?? ctx.gmProgram;
|
|
4317
|
+
instrument && instrument.key !== instrumentKey && (program = baselineProgram, muted = !1, instrumentKey = instrument.key);
|
|
4318
|
+
let bowCapable = instrument ? ARCO_CAPABLE_PROGRAMS.has(baselineProgram) : caps.bow, muteFamily = instrument ? muteFamilyForProgram(baselineProgram) : caps.mute, time = ctx.model.timeAtBeat(ctx.measureStartBeat + mark.beat);
|
|
4319
|
+
if (mark.action.kind === "bow" && bowCapable) {
|
|
4320
|
+
let target = mark.action.pizz ? GM_PIZZICATO_STRINGS : baselineProgram;
|
|
4258
4321
|
if (target === program) continue;
|
|
4259
4322
|
program = target, ctx.out.push({
|
|
4260
4323
|
type: "programChange",
|
|
@@ -4265,14 +4328,136 @@ function applyMeasureTechniques(expressions, ctx, state, caps, ratioForStaff = (
|
|
|
4265
4328
|
channel: ctx.channel,
|
|
4266
4329
|
program: target
|
|
4267
4330
|
});
|
|
4268
|
-
} else if (mark.action.kind === "mute" &&
|
|
4331
|
+
} else if (mark.action.kind === "mute" && muteFamily) {
|
|
4269
4332
|
if (mark.action.muted === muted) continue;
|
|
4270
4333
|
muted = mark.action.muted;
|
|
4271
|
-
let curve = muted ? MUTE_CURVES[
|
|
4334
|
+
let curve = muted ? MUTE_CURVES[muteFamily] : OPEN_CURVE;
|
|
4272
4335
|
emitCc(ctx, time, CC_BRIGHTNESS, curve.brightness), emitCc(ctx, time, CC_RESONANCE, curve.resonance);
|
|
4273
4336
|
}
|
|
4274
4337
|
}
|
|
4275
|
-
return { program, muted };
|
|
4338
|
+
return { program, muted, ...instrumentKey === void 0 ? {} : { instrumentKey } };
|
|
4339
|
+
}
|
|
4340
|
+
|
|
4341
|
+
// packages/midi/src/instrumentPlayback/index.ts
|
|
4342
|
+
init_define_import_meta_env();
|
|
4343
|
+
function instrumentPart(part, instrument) {
|
|
4344
|
+
return instrument ? {
|
|
4345
|
+
...part,
|
|
4346
|
+
name: instrument.name ?? part.name,
|
|
4347
|
+
_x: {
|
|
4348
|
+
...part._x,
|
|
4349
|
+
viritura: {
|
|
4350
|
+
...part._x?.viritura,
|
|
4351
|
+
instrumentId: instrument.instrumentId,
|
|
4352
|
+
midiProgram: instrument.midiProgram,
|
|
4353
|
+
initialInstrument: void 0,
|
|
4354
|
+
instruments: void 0
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
} : part;
|
|
4358
|
+
}
|
|
4359
|
+
function playbackInstruments(part) {
|
|
4360
|
+
let initial = initialInstrumentState(part);
|
|
4361
|
+
if (initial.instrumentKey !== void 0 && !initial.instrument)
|
|
4362
|
+
throw new Error(`Unknown initial instrument "${initial.instrumentKey}" in "${part.name}".`);
|
|
4363
|
+
let result = [{ key: "initial", part: instrumentPart(part, initial.instrument) }], seen = /* @__PURE__ */ new Set();
|
|
4364
|
+
for (let located of listInstrumentChanges(part)) {
|
|
4365
|
+
let [numerator, denominator] = located.position;
|
|
4366
|
+
if (denominator <= 0 || numerator < 0) throw new Error(`Invalid instrument-change position in "${part.name}".`);
|
|
4367
|
+
let key = located.change.instrument;
|
|
4368
|
+
if (!(key === void 0 || seen.has(key))) {
|
|
4369
|
+
if (!located.state.instrument) throw new Error(`Unknown instrument "${key}" in "${part.name}".`);
|
|
4370
|
+
seen.add(key), result.push({ key: `instrument:${key}`, part: instrumentPart(part, located.state.instrument) });
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
return result;
|
|
4374
|
+
}
|
|
4375
|
+
function playbackInstrumentKeyAt(part, measureIndex, beatOffset = 0) {
|
|
4376
|
+
let key = "initial";
|
|
4377
|
+
for (let located of listInstrumentChanges(part)) {
|
|
4378
|
+
if (located.measureIndex > measureIndex || located.measureIndex === measureIndex && located.position[0] / located.position[1] * 4 > beatOffset) break;
|
|
4379
|
+
located.change.instrument !== void 0 && (key = `instrument:${located.change.instrument}`);
|
|
4380
|
+
}
|
|
4381
|
+
return key;
|
|
4382
|
+
}
|
|
4383
|
+
function boundariesForPart(part, timing, programs, initialProgram) {
|
|
4384
|
+
let instruments = playbackInstruments(part), definitions = new Map(instruments.map((instrument) => [instrument.key, instrument.part])), changes = listInstrumentChanges(part).filter((located) => located.change.instrument !== void 0), boundaries = [], add = (beat, key) => {
|
|
4385
|
+
let program = programs?.get(key) ?? (key === "initial" ? initialProgram : void 0) ?? definitions.get(key)?._x?.viritura?.midiProgram;
|
|
4386
|
+
boundaries.push({
|
|
4387
|
+
beat,
|
|
4388
|
+
time: timing.model.timeAtBeat(beat),
|
|
4389
|
+
key,
|
|
4390
|
+
program: typeof program == "number" && Number.isInteger(program) && program >= 0 && program <= 127 ? program : void 0
|
|
4391
|
+
});
|
|
4392
|
+
};
|
|
4393
|
+
return timing.measureOrder.forEach((measureIndex, expandedIndex) => {
|
|
4394
|
+
let start = timing.measureStartBeats[expandedIndex];
|
|
4395
|
+
add(start, playbackInstrumentKeyAt(part, measureIndex));
|
|
4396
|
+
for (let located of changes) {
|
|
4397
|
+
if (located.measureIndex !== measureIndex || located.position[0] === 0) continue;
|
|
4398
|
+
let [numerator, denominator] = located.position;
|
|
4399
|
+
if (denominator <= 0 || numerator < 0) throw new Error(`Invalid instrument-change position in "${part.name}".`);
|
|
4400
|
+
add(start + numerator / denominator * 4, `instrument:${located.change.instrument}`);
|
|
4401
|
+
}
|
|
4402
|
+
}), boundaries;
|
|
4403
|
+
}
|
|
4404
|
+
function applyInstrumentPlayback(score, events, timing, programs, initialPrograms) {
|
|
4405
|
+
let diagnostics = [];
|
|
4406
|
+
return score.parts.forEach((part, partIndex) => {
|
|
4407
|
+
if (playbackInstruments(part).length === 1) return;
|
|
4408
|
+
let boundaries = boundariesForPart(part, timing, programs?.get(partIndex), initialPrograms?.[partIndex]), partEvents = events.filter((event) => event.partIndex === partIndex), lanes = new Set(partEvents.flatMap((event) => event.playbackLaneId ? [event.playbackLaneId] : []));
|
|
4409
|
+
lanes.size === 0 && lanes.add(`part:${partIndex}`);
|
|
4410
|
+
let missingPrograms = [
|
|
4411
|
+
...new Set(boundaries.filter((boundary) => boundary.program === void 0).map((boundary) => boundary.key))
|
|
4412
|
+
];
|
|
4413
|
+
missingPrograms.length > 0 && diagnostics.push({
|
|
4414
|
+
code: "instrument-program-unavailable",
|
|
4415
|
+
message: `Physical MIDI cannot select ${missingPrograms.join(", ")} in "${part.name}"; provide resolved instrumentPrograms or a MIDI program fallback.`,
|
|
4416
|
+
playbackLaneIds: [...lanes]
|
|
4417
|
+
});
|
|
4418
|
+
for (let event of [...partEvents]) {
|
|
4419
|
+
if (event.playbackLaneId) continue;
|
|
4420
|
+
let [first, ...others] = lanes;
|
|
4421
|
+
event.playbackLaneId = first;
|
|
4422
|
+
for (let lane of others) {
|
|
4423
|
+
let scoped = { ...event, playbackLaneId: lane };
|
|
4424
|
+
events.push(scoped), partEvents.push(scoped);
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
let attackDelays = /* @__PURE__ */ new Map(), attackKey = (event) => JSON.stringify([event.playbackLaneId, event.channel, event.midiNote, event.scoreBeat, event.scoreEventId]);
|
|
4428
|
+
for (let event of partEvents) {
|
|
4429
|
+
if (event.type === "controlChange" && event.cc !== 74 && event.cc !== 71) continue;
|
|
4430
|
+
let beat = event.scoreBeat ?? timing.model.beatAtTime(event.time), instrument = "initial", instrumentStart = 0;
|
|
4431
|
+
for (let boundary of boundaries) {
|
|
4432
|
+
if (boundary.beat > beat + 1e-9) break;
|
|
4433
|
+
instrument = boundary.key, instrumentStart = boundary.time;
|
|
4434
|
+
}
|
|
4435
|
+
if (event.type === "noteOn") {
|
|
4436
|
+
let delay = Math.max(0, instrumentStart - event.time);
|
|
4437
|
+
attackDelays.set(attackKey(event), delay), event.time = Math.max(event.time, instrumentStart);
|
|
4438
|
+
}
|
|
4439
|
+
event.playbackInstrumentKey = instrument;
|
|
4440
|
+
}
|
|
4441
|
+
for (let event of partEvents)
|
|
4442
|
+
event.type === "noteOff" && (event.time += attackDelays.get(attackKey(event)) ?? 0);
|
|
4443
|
+
for (let lane of lanes) {
|
|
4444
|
+
let previous;
|
|
4445
|
+
for (let boundary of boundaries)
|
|
4446
|
+
boundary.key !== previous && (previous = boundary.key, events.push({
|
|
4447
|
+
type: "programChange",
|
|
4448
|
+
time: boundary.time,
|
|
4449
|
+
scoreBeat: boundary.beat,
|
|
4450
|
+
midiNote: 0,
|
|
4451
|
+
velocity: 0,
|
|
4452
|
+
partIndex,
|
|
4453
|
+
playbackLaneId: lane,
|
|
4454
|
+
channel: partEvents.find((event) => event.playbackLaneId === lane)?.channel ?? 0,
|
|
4455
|
+
playbackInstrumentKey: boundary.key,
|
|
4456
|
+
instrumentChange: !0,
|
|
4457
|
+
program: boundary.program
|
|
4458
|
+
}));
|
|
4459
|
+
}
|
|
4460
|
+
}), diagnostics;
|
|
4276
4461
|
}
|
|
4277
4462
|
|
|
4278
4463
|
// packages/midi/src/tempoMap.ts
|
|
@@ -5058,12 +5243,20 @@ function generateTimeline(inputScore, options) {
|
|
|
5058
5243
|
);
|
|
5059
5244
|
if (chords)
|
|
5060
5245
|
for (let event of chordMidiEvents(chords, measureOrder, tempoBuild)) allEvents.push(event);
|
|
5061
|
-
let
|
|
5062
|
-
allEvents.sort((a, b) => {
|
|
5246
|
+
let EVENT_TYPE_ORDER = { noteOff: 0, programChange: 1, controlChange: 1, noteOn: 2 }, compareEvents = (a, b) => {
|
|
5063
5247
|
if (Math.abs(a.time - b.time) > 1e-9) return a.time - b.time;
|
|
5064
5248
|
let aOrder = EVENT_TYPE_ORDER[a.type] ?? 1, bOrder = EVENT_TYPE_ORDER[b.type] ?? 1;
|
|
5065
|
-
return aOrder !== bOrder ? aOrder - bOrder : a.midiNote - b.midiNote;
|
|
5066
|
-
}
|
|
5249
|
+
return aOrder !== bOrder ? aOrder - bOrder : a.instrumentChange !== b.instrumentChange ? a.instrumentChange ? -1 : 1 : a.midiNote - b.midiNote;
|
|
5250
|
+
};
|
|
5251
|
+
allEvents.sort(compareEvents);
|
|
5252
|
+
let instrumentDiagnostics = applyInstrumentPlayback(
|
|
5253
|
+
score,
|
|
5254
|
+
allEvents,
|
|
5255
|
+
{ model, measureStartBeats, measureOrder },
|
|
5256
|
+
options?.instrumentPrograms,
|
|
5257
|
+
options?.partPrograms
|
|
5258
|
+
), diagnostics = [...assignPhysicalMidiChannels(allEvents, score), ...instrumentDiagnostics];
|
|
5259
|
+
allEvents.sort(compareEvents);
|
|
5067
5260
|
let duration = model.timeAtBeat(tempoBuild.totalBeats), measureTimeSignatures = measureOrder.map((origIdx) => {
|
|
5068
5261
|
let ts = resolveActiveTime(globalMeasures, origIdx);
|
|
5069
5262
|
return { count: ts.count, unit: ts.unit };
|
|
@@ -5179,11 +5372,13 @@ function processPart(score, partIdx, options, partChannels, timing, measureOrder
|
|
|
5179
5372
|
);
|
|
5180
5373
|
for (let lane of dynamicProgram.lanes.values())
|
|
5181
5374
|
emitDynamicsCc11(lane.envelope, partIdx, channel, allEvents, lane.id);
|
|
5182
|
-
let
|
|
5375
|
+
let techniqueState = { program: gmProgram, muted: !1, instrumentKey: "initial" }, instruments = new Map(playbackInstruments(part).map(({ key, part: instrumentPart2 }) => [key, instrumentPart2])), programForInstrument = (key) => key === "initial" ? gmProgram : options?.instrumentPrograms?.get(partIdx)?.get(key) ?? instruments.get(key)?._x?.viritura?.midiProgram ?? -1;
|
|
5183
5376
|
for (let expandedIdx = 0; expandedIdx < measureOrder.length; expandedIdx++) {
|
|
5184
5377
|
let origMeasureIdx = measureOrder[expandedIdx], partMeasure = part.measures[origMeasureIdx];
|
|
5185
5378
|
if (!partMeasure) continue;
|
|
5186
|
-
let measureStartTime = measureStartTimes[expandedIdx], measureStartBeat = measureStartBeats[expandedIdx],
|
|
5379
|
+
let measureStartTime = measureStartTimes[expandedIdx], measureStartBeat = measureStartBeats[expandedIdx], instrumentKey = playbackInstrumentKeyAt(part, origMeasureIdx), activeProgram = programForInstrument(instrumentKey), bowCapable = ARCO_CAPABLE_PROGRAMS.has(activeProgram), muteFamily = muteFamilyForProgram(activeProgram), techniqueEnabled = bowCapable || muteFamily !== null;
|
|
5380
|
+
instrumentKey !== techniqueState.instrumentKey && (techniqueState = { program: activeProgram, muted: !1, instrumentKey });
|
|
5381
|
+
let activeTime = resolveActiveTime(globalMeasures, origMeasureIdx), activeKeyFifths = resolveActiveKeyFifths(globalMeasures, origMeasureIdx), measureHolds = holdSchedule[expandedIdx] ?? [], measureSpq = model.spbAtBeat(measureStartBeat), fermataGroups = measureHolds.filter((h) => h.kind === "fermata").map((h) => ({
|
|
5187
5382
|
startBeat: h.startBeat ?? h.atBeat,
|
|
5188
5383
|
spanEndBeat: h.spanEndBeat ?? h.atBeat,
|
|
5189
5384
|
insertBeat: h.atBeat
|
|
@@ -5199,7 +5394,11 @@ function processPart(score, partIdx, options, partChannels, timing, measureOrder
|
|
|
5199
5394
|
pendingLegato,
|
|
5200
5395
|
voiceKey: "",
|
|
5201
5396
|
out: allEvents,
|
|
5202
|
-
gmProgram,
|
|
5397
|
+
gmProgram: activeProgram,
|
|
5398
|
+
instrumentAtBeatOffset: instruments.size > 1 ? (offset) => {
|
|
5399
|
+
let key = playbackInstrumentKeyAt(part, origMeasureIdx, offset);
|
|
5400
|
+
return { key, program: programForInstrument(key) };
|
|
5401
|
+
} : void 0,
|
|
5203
5402
|
timeSig: activeTime,
|
|
5204
5403
|
staffMeterRatio: 1,
|
|
5205
5404
|
keyFifths: activeKeyFifths,
|
|
@@ -5213,7 +5412,7 @@ function processPart(score, partIdx, options, partChannels, timing, measureOrder
|
|
|
5213
5412
|
measureSpq,
|
|
5214
5413
|
fermataGroups
|
|
5215
5414
|
};
|
|
5216
|
-
techniqueEnabled && (techniqueState = applyMeasureTechniques(
|
|
5415
|
+
(techniqueEnabled || instruments.size > 1) && (techniqueState = applyMeasureTechniques(
|
|
5217
5416
|
partMeasure.expressions,
|
|
5218
5417
|
ctx,
|
|
5219
5418
|
techniqueState,
|
|
@@ -5313,6 +5512,7 @@ function tryProcessSpecialEvent(ctx, event, measureStartTime, beatOffset, baseVe
|
|
|
5313
5512
|
return tremoloMarks && tremoloMarks >= 1 && tremoloMarks <= 3 ? processSingleNoteTremolo(ctx, event, measureStartTime, beatOffset, baseVelocity, beats, tremoloMarks) : event.markings?.trill && tryProcessTrill(ctx, event, measureStartTime, beatOffset, baseVelocity, beats) ? beats : null;
|
|
5314
5513
|
}
|
|
5315
5514
|
function processNoteEvent(ctx, event, measureStartTime, beatOffset, baseVelocity, tupletRatio) {
|
|
5515
|
+
ctx.gmProgram = ctx.instrumentAtBeatOffset?.(beatOffset * ctx.staffMeterRatio).program ?? ctx.gmProgram;
|
|
5316
5516
|
let { partIndex, channel, tieTargets, out, timeSig, kitMidiMap, kitAltProgramMap } = ctx, beats = durationBeats3(event.duration) * tupletRatio;
|
|
5317
5517
|
if (isRest(event))
|
|
5318
5518
|
return flushVoiceLegato(ctx.pendingLegato, ctx.voiceKey, ctx.out), beats;
|
|
@@ -5386,7 +5586,8 @@ function processNoteEvent(ctx, event, measureStartTime, beatOffset, baseVelocity
|
|
|
5386
5586
|
velocity: 0,
|
|
5387
5587
|
partIndex,
|
|
5388
5588
|
channel,
|
|
5389
|
-
drumKitProgram
|
|
5589
|
+
drumKitProgram,
|
|
5590
|
+
...ctx.instrumentAtBeatOffset ? { scoreBeat: ctx.measureStartBeat + beatOffset * ctx.staffMeterRatio } : {}
|
|
5390
5591
|
});
|
|
5391
5592
|
}
|
|
5392
5593
|
}
|
|
@@ -5421,7 +5622,8 @@ function emitFreshNote(ctx, midiNote, startTime, timingOffset, durationSec, dura
|
|
|
5421
5622
|
midiNote,
|
|
5422
5623
|
velocity: 0,
|
|
5423
5624
|
partIndex,
|
|
5424
|
-
channel
|
|
5625
|
+
channel,
|
|
5626
|
+
...ctx.instrumentAtBeatOffset ? metadata : {}
|
|
5425
5627
|
});
|
|
5426
5628
|
} else {
|
|
5427
5629
|
let off = {
|
|
@@ -5430,7 +5632,8 @@ function emitFreshNote(ctx, midiNote, startTime, timingOffset, durationSec, dura
|
|
|
5430
5632
|
midiNote,
|
|
5431
5633
|
velocity: 0,
|
|
5432
5634
|
partIndex,
|
|
5433
|
-
channel
|
|
5635
|
+
channel,
|
|
5636
|
+
...ctx.instrumentAtBeatOffset ? metadata : {}
|
|
5434
5637
|
};
|
|
5435
5638
|
legatoOut ? pushVoiceLegato(ctx.pendingLegato, ctx.voiceKey, off) : out.push(off);
|
|
5436
5639
|
}
|
|
@@ -5478,7 +5681,8 @@ function processKitRoll(ctx, event, measureStartTime, beatOffset, baseVelocity,
|
|
|
5478
5681
|
velocity: 0,
|
|
5479
5682
|
partIndex,
|
|
5480
5683
|
channel,
|
|
5481
|
-
drumKitProgram
|
|
5684
|
+
drumKitProgram,
|
|
5685
|
+
...ctx.instrumentAtBeatOffset ? { scoreBeat: ctx.measureStartBeat + beatOffset * ctx.staffMeterRatio } : {}
|
|
5482
5686
|
});
|
|
5483
5687
|
}
|
|
5484
5688
|
return beats;
|
|
@@ -5515,7 +5719,15 @@ function processSingleNoteTremolo(ctx, event, measureStartTime, beatOffset, base
|
|
|
5515
5719
|
scoreBeat: ctx.measureStartBeat + beatOffset * ctx.staffMeterRatio,
|
|
5516
5720
|
scoreDurationBeats: totalBeats * ctx.staffMeterRatio,
|
|
5517
5721
|
scoreEventId: event.id
|
|
5518
|
-
}), out.push({
|
|
5722
|
+
}), out.push({
|
|
5723
|
+
type: "noteOff",
|
|
5724
|
+
time: jitteredTremStart + durationSec,
|
|
5725
|
+
midiNote,
|
|
5726
|
+
velocity: 0,
|
|
5727
|
+
partIndex,
|
|
5728
|
+
channel,
|
|
5729
|
+
...ctx.instrumentAtBeatOffset ? { scoreBeat: ctx.measureStartBeat + beatOffset * ctx.staffMeterRatio } : {}
|
|
5730
|
+
}));
|
|
5519
5731
|
}
|
|
5520
5732
|
return out.push({
|
|
5521
5733
|
type: "programChange",
|
|
@@ -5548,7 +5760,8 @@ function processSingleNoteTremolo(ctx, event, measureStartTime, beatOffset, base
|
|
|
5548
5760
|
midiNote,
|
|
5549
5761
|
velocity: 0,
|
|
5550
5762
|
partIndex,
|
|
5551
|
-
channel
|
|
5763
|
+
channel,
|
|
5764
|
+
...ctx.instrumentAtBeatOffset ? { scoreBeat: ctx.measureStartBeat + subBeatOffset * ctx.staffMeterRatio } : {}
|
|
5552
5765
|
}));
|
|
5553
5766
|
}
|
|
5554
5767
|
}
|
|
@@ -5599,7 +5812,8 @@ function tryProcessTrill(ctx, event, measureStartTime, beatOffset, baseVelocity,
|
|
|
5599
5812
|
midiNote,
|
|
5600
5813
|
velocity: 0,
|
|
5601
5814
|
partIndex,
|
|
5602
|
-
channel
|
|
5815
|
+
channel,
|
|
5816
|
+
...ctx.instrumentAtBeatOffset ? { scoreBeat: ctx.measureStartBeat + subBeatOffset * ctx.staffMeterRatio } : {}
|
|
5603
5817
|
});
|
|
5604
5818
|
}
|
|
5605
5819
|
}
|