@viritura/score-engine 0.2.1-next.45 → 0.2.1-next.47
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 +182 -21
- package/dist/index.js.map +4 -4
- package/dist/wasm/viritura_wasm_bg.wasm +0 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2265,9 +2265,9 @@ function interpolateBeatAnchors(bounds, beat, endX) {
|
|
|
2265
2265
|
|
|
2266
2266
|
// packages/score-engine/src/buildInfo.ts
|
|
2267
2267
|
init_define_import_meta_env();
|
|
2268
|
-
var PACKAGE_VERSION = "0.2.1-next.
|
|
2268
|
+
var PACKAGE_VERSION = "0.2.1-next.47", BUNDLE_WORKER_FILE = "score-engine.worker.js";
|
|
2269
2269
|
function buildCommit() {
|
|
2270
|
-
return "
|
|
2270
|
+
return "78ae5d80a2cd938fb7b991f4bdebebbc72b18896";
|
|
2271
2271
|
}
|
|
2272
2272
|
function bundledAssetBase() {
|
|
2273
2273
|
let moduleUrl = import.meta.url;
|
|
@@ -13058,7 +13058,7 @@ function validate460(data, { instancePath = "", parentData, parentDataProperty,
|
|
|
13058
13058
|
return validate460.errors = vErrors, errors === 0;
|
|
13059
13059
|
}
|
|
13060
13060
|
validate460.evaluated = { props: !0, dynamicProps: !1, dynamicItems: !1 };
|
|
13061
|
-
var schema455 = { type: "string", enum: ["glissando", "portamento"], description: "Semantic kind of a glissando-family line." }, schema456 = { type: "string", enum: ["straight", "wavy"], description: "Glissando/portamento line style." };
|
|
13061
|
+
var schema454 = { type: "object", description: "A glissando or portamento connecting this event to a target event.", required: ["target"], properties: { target: { type: "string", description: "ID of the target event." }, kind: { $ref: "#/$defs/glissando-kind", description: "Semantic kind. Default: 'glissando'." }, style: { $ref: "#/$defs/glissando-style", description: "Line style. Default: 'straight'." }, playbackType: { type: "string", enum: ["chromatic", "diatonic", "pitch-bend"], description: "Playback interpolation. Omission means diatonic for glissando and pitch-bend for portamento." }, text: { type: "string", description: "Optional text label (e.g. 'gliss.', 'port.')." }, showText: { type: "boolean", description: "Whether to display the text label. Default: true." } }, additionalProperties: !1 }, schema455 = { type: "string", enum: ["glissando", "portamento"], description: "Semantic kind of a glissando-family line." }, schema456 = { type: "string", enum: ["straight", "wavy"], description: "Glissando/portamento line style." };
|
|
13062
13062
|
function validate462(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
|
|
13063
13063
|
let vErrors = null, errors = 0, evaluated0 = validate462.evaluated;
|
|
13064
13064
|
if (evaluated0.dynamicProps && (evaluated0.props = void 0), evaluated0.dynamicItems && (evaluated0.items = void 0), data && typeof data == "object" && !Array.isArray(data)) {
|
|
@@ -13067,7 +13067,7 @@ function validate462(data, { instancePath = "", parentData, parentDataProperty,
|
|
|
13067
13067
|
vErrors === null ? vErrors = [err0] : vErrors.push(err0), errors++;
|
|
13068
13068
|
}
|
|
13069
13069
|
for (let key0 in data)
|
|
13070
|
-
if (!(key0 === "target" || key0 === "kind" || key0 === "style" || key0 === "text" || key0 === "showText")) {
|
|
13070
|
+
if (!(key0 === "target" || key0 === "kind" || key0 === "style" || key0 === "playbackType" || key0 === "text" || key0 === "showText")) {
|
|
13071
13071
|
let err1 = { instancePath, schemaPath: "#/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key0 }, message: "must NOT have additional properties" };
|
|
13072
13072
|
vErrors === null ? vErrors = [err1] : vErrors.push(err1), errors++;
|
|
13073
13073
|
}
|
|
@@ -13097,17 +13097,28 @@ function validate462(data, { instancePath = "", parentData, parentDataProperty,
|
|
|
13097
13097
|
vErrors === null ? vErrors = [err6] : vErrors.push(err6), errors++;
|
|
13098
13098
|
}
|
|
13099
13099
|
}
|
|
13100
|
+
if (data.playbackType !== void 0) {
|
|
13101
|
+
let data3 = data.playbackType;
|
|
13102
|
+
if (typeof data3 != "string") {
|
|
13103
|
+
let err7 = { instancePath: instancePath + "/playbackType", schemaPath: "#/properties/playbackType/type", keyword: "type", params: { type: "string" }, message: "must be string" };
|
|
13104
|
+
vErrors === null ? vErrors = [err7] : vErrors.push(err7), errors++;
|
|
13105
|
+
}
|
|
13106
|
+
if (!(data3 === "chromatic" || data3 === "diatonic" || data3 === "pitch-bend")) {
|
|
13107
|
+
let err8 = { instancePath: instancePath + "/playbackType", schemaPath: "#/properties/playbackType/enum", keyword: "enum", params: { allowedValues: schema454.properties.playbackType.enum }, message: "must be equal to one of the allowed values" };
|
|
13108
|
+
vErrors === null ? vErrors = [err8] : vErrors.push(err8), errors++;
|
|
13109
|
+
}
|
|
13110
|
+
}
|
|
13100
13111
|
if (data.text !== void 0 && typeof data.text != "string") {
|
|
13101
|
-
let
|
|
13102
|
-
vErrors === null ? vErrors = [
|
|
13112
|
+
let err9 = { instancePath: instancePath + "/text", schemaPath: "#/properties/text/type", keyword: "type", params: { type: "string" }, message: "must be string" };
|
|
13113
|
+
vErrors === null ? vErrors = [err9] : vErrors.push(err9), errors++;
|
|
13103
13114
|
}
|
|
13104
13115
|
if (data.showText !== void 0 && typeof data.showText != "boolean") {
|
|
13105
|
-
let
|
|
13106
|
-
vErrors === null ? vErrors = [
|
|
13116
|
+
let err10 = { instancePath: instancePath + "/showText", schemaPath: "#/properties/showText/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" };
|
|
13117
|
+
vErrors === null ? vErrors = [err10] : vErrors.push(err10), errors++;
|
|
13107
13118
|
}
|
|
13108
13119
|
} else {
|
|
13109
|
-
let
|
|
13110
|
-
vErrors === null ? vErrors = [
|
|
13120
|
+
let err11 = { instancePath, schemaPath: "#/type", keyword: "type", params: { type: "object" }, message: "must be object" };
|
|
13121
|
+
vErrors === null ? vErrors = [err11] : vErrors.push(err11), errors++;
|
|
13111
13122
|
}
|
|
13112
13123
|
return validate462.errors = vErrors, errors === 0;
|
|
13113
13124
|
}
|
|
@@ -16266,7 +16277,7 @@ function parseSlur(raw) {
|
|
|
16266
16277
|
}
|
|
16267
16278
|
function parseGlissando(raw) {
|
|
16268
16279
|
let gliss = { target: raw.target };
|
|
16269
|
-
return raw.kind && (gliss.kind = raw.kind), raw.style && (gliss.style = raw.style), raw.text && (gliss.text = raw.text), raw.showText !== void 0 && (gliss.showText = raw.showText), gliss;
|
|
16280
|
+
return raw.kind && (gliss.kind = raw.kind), raw.style && (gliss.style = raw.style), raw.playbackType && (gliss.playbackType = raw.playbackType), raw.text && (gliss.text = raw.text), raw.showText !== void 0 && (gliss.showText = raw.showText), gliss;
|
|
16270
16281
|
}
|
|
16271
16282
|
function parseNotes(arr) {
|
|
16272
16283
|
return arr.map(parseNote);
|
|
@@ -17765,7 +17776,7 @@ function realizeNavigation(timeline, spans, crossJumpContinuations = /* @__PURE_
|
|
|
17765
17776
|
events
|
|
17766
17777
|
), outputBeat += span.endBeat - span.startBeat;
|
|
17767
17778
|
}
|
|
17768
|
-
let rank = { noteOff: 0, programChange: 1, controlChange: 2, noteOn: 3 };
|
|
17779
|
+
let rank = { noteOff: 0, programChange: 1, controlChange: 2, pitchBend: 2, noteOn: 3 };
|
|
17769
17780
|
return events.sort((a, b) => a.time - b.time || rank[a.type] - rank[b.type]), {
|
|
17770
17781
|
...timeline,
|
|
17771
17782
|
events,
|
|
@@ -17906,6 +17917,159 @@ function expandScoreMeasureRepeats(score) {
|
|
|
17906
17917
|
return parts.every((part, index) => part === score.parts[index]) ? score : { ...score, parts };
|
|
17907
17918
|
}
|
|
17908
17919
|
|
|
17920
|
+
// packages/midi/src/glissandoPlayback/index.ts
|
|
17921
|
+
init_define_import_meta_env();
|
|
17922
|
+
function linesForPart(score, partIndex) {
|
|
17923
|
+
let lines = [];
|
|
17924
|
+
for (let measure of score.parts[partIndex]?.measures ?? [])
|
|
17925
|
+
for (let sequence of measure.sequences)
|
|
17926
|
+
for (let { event } of walkSequenceEvents(sequence.content))
|
|
17927
|
+
if (event.glissandos?.length) {
|
|
17928
|
+
if (!event.id) throw new Error("Glissando source event requires an ID.");
|
|
17929
|
+
for (let glissando of event.glissandos) lines.push({ source: event.id, glissando });
|
|
17930
|
+
}
|
|
17931
|
+
return lines;
|
|
17932
|
+
}
|
|
17933
|
+
function noteKey(event) {
|
|
17934
|
+
return JSON.stringify([event.partIndex, event.playbackLaneId, event.playbackInstrumentKey, event.midiNote]);
|
|
17935
|
+
}
|
|
17936
|
+
function releasesFor(events) {
|
|
17937
|
+
let held = /* @__PURE__ */ new Map(), releases = /* @__PURE__ */ new Map();
|
|
17938
|
+
for (let event of events) {
|
|
17939
|
+
let key = noteKey(event);
|
|
17940
|
+
if (event.type === "noteOn") {
|
|
17941
|
+
let queue = held.get(key) ?? [];
|
|
17942
|
+
queue.push(event), held.set(key, queue);
|
|
17943
|
+
} else if (event.type === "noteOff") {
|
|
17944
|
+
let onset = held.get(key)?.shift();
|
|
17945
|
+
onset && releases.set(onset, event);
|
|
17946
|
+
}
|
|
17947
|
+
}
|
|
17948
|
+
return releases;
|
|
17949
|
+
}
|
|
17950
|
+
function measureAt(timeline, beat) {
|
|
17951
|
+
let index = 0;
|
|
17952
|
+
for (; index + 1 < timeline.measureStartBeats.length && timeline.measureStartBeats[index + 1] <= beat + 1e-9; )
|
|
17953
|
+
index++;
|
|
17954
|
+
return index;
|
|
17955
|
+
}
|
|
17956
|
+
function continuousTo(timeline, start, end) {
|
|
17957
|
+
let startBeat = start.scoreBeat ?? timeline.model.beatAtTime(start.time), endBeat = end.scoreBeat ?? timeline.model.beatAtTime(end.time);
|
|
17958
|
+
for (let index = measureAt(timeline, startBeat); index < measureAt(timeline, endBeat); index++)
|
|
17959
|
+
if (timeline.playedSpans) {
|
|
17960
|
+
if (!playedSpansContinue(timeline.playedSpans[index], timeline.playedSpans[index + 1])) return !1;
|
|
17961
|
+
} else if (timeline.expandedMeasureToOriginal[index + 1] !== timeline.expandedMeasureToOriginal[index] + 1)
|
|
17962
|
+
return !1;
|
|
17963
|
+
return !0;
|
|
17964
|
+
}
|
|
17965
|
+
function keyPitches(score, timeline, onset) {
|
|
17966
|
+
let beat = onset.scoreBeat ?? timeline.model.beatAtTime(onset.time), measureIndex = timeline.expandedMeasureToOriginal[measureAt(timeline, beat)], fifths = 0;
|
|
17967
|
+
for (let index = 0; index <= measureIndex; index++)
|
|
17968
|
+
fifths = score.global.measures[index]?.key?.fifths ?? fifths;
|
|
17969
|
+
let steps = ["C", "D", "E", "F", "G", "A", "B"], pitches = [0, 2, 4, 5, 7, 9, 11], order = fifths >= 0 ? SHARP_ORDER : FLAT_ORDER;
|
|
17970
|
+
for (let index = 0; index < Math.abs(fifths); index++) {
|
|
17971
|
+
let position = steps.indexOf(order[index % order.length]);
|
|
17972
|
+
pitches[position] = pitches[position] + Math.sign(fifths);
|
|
17973
|
+
}
|
|
17974
|
+
return new Set(pitches.map((pitch) => (pitch % 12 + 12) % 12));
|
|
17975
|
+
}
|
|
17976
|
+
function steppedPitches(start, end, key) {
|
|
17977
|
+
let direction = Math.sign(end - start), pitches = [start];
|
|
17978
|
+
for (let pitch = start + direction; direction && direction * (end - pitch) > 0; pitch += direction)
|
|
17979
|
+
(!key || key.has(pitch % 12)) && pitches.push(pitch);
|
|
17980
|
+
return pitches;
|
|
17981
|
+
}
|
|
17982
|
+
function renderSteps(on, off, target, pitches, timeline) {
|
|
17983
|
+
let events = [], duration2 = target.time - on.time;
|
|
17984
|
+
return pitches.forEach((midiNote, index) => {
|
|
17985
|
+
let time = on.time + duration2 * index / pitches.length, end = on.time + duration2 * (index + 1) / pitches.length;
|
|
17986
|
+
events.push({
|
|
17987
|
+
...on,
|
|
17988
|
+
midiNote,
|
|
17989
|
+
time,
|
|
17990
|
+
scoreBeat: timeline.model.beatAtTime(time),
|
|
17991
|
+
scoreDurationBeats: timeline.model.beatAtTime(end) - timeline.model.beatAtTime(time)
|
|
17992
|
+
}), events.push({ ...off, midiNote, time: end });
|
|
17993
|
+
}), events;
|
|
17994
|
+
}
|
|
17995
|
+
function renderBend(on, off, target, lane) {
|
|
17996
|
+
let base = { ...on, playbackLaneId: lane }, end = target.time, range = Math.max(2, Math.abs(target.midiNote - on.midiNote)), control = (cc, value) => ({
|
|
17997
|
+
...base,
|
|
17998
|
+
type: "controlChange",
|
|
17999
|
+
time: on.time,
|
|
18000
|
+
cc,
|
|
18001
|
+
value
|
|
18002
|
+
}), events = [
|
|
18003
|
+
control(101, 0),
|
|
18004
|
+
control(100, 0),
|
|
18005
|
+
control(6, range),
|
|
18006
|
+
control(38, 0),
|
|
18007
|
+
control(101, 127),
|
|
18008
|
+
control(100, 127),
|
|
18009
|
+
base,
|
|
18010
|
+
{ ...off, time: end, playbackLaneId: lane }
|
|
18011
|
+
], samples = Math.max(1, Math.ceil((end - on.time) * 200));
|
|
18012
|
+
for (let index = 0; index < samples; index++) {
|
|
18013
|
+
let fraction = index / samples, delta = (target.midiNote - on.midiNote) * fraction;
|
|
18014
|
+
events.push({
|
|
18015
|
+
...base,
|
|
18016
|
+
type: "pitchBend",
|
|
18017
|
+
time: on.time + (end - on.time) * fraction,
|
|
18018
|
+
bendRange: range,
|
|
18019
|
+
value: Math.max(0, Math.min(16383, Math.round(8192 + delta * 8192 / range)))
|
|
18020
|
+
});
|
|
18021
|
+
}
|
|
18022
|
+
return events.push({ ...base, type: "controlChange", time: end, cc: 120, value: 0 }), events.push({ ...base, type: "pitchBend", time: end, value: 8192, bendRange: range }), events;
|
|
18023
|
+
}
|
|
18024
|
+
function applyGlissandoPlayback(score, timeline) {
|
|
18025
|
+
let partLines = score.parts.map((_part, partIndex) => linesForPart(score, partIndex));
|
|
18026
|
+
if (partLines.every((lines) => lines.length === 0)) return [];
|
|
18027
|
+
let diagnostics = [], original = [...timeline.events], releases = releasesFor(original), removed = /* @__PURE__ */ new Set(), added = [], mirroredLanes = /* @__PURE__ */ new Set();
|
|
18028
|
+
return partLines.forEach((lines, partIndex) => {
|
|
18029
|
+
for (let { source, glissando } of lines) {
|
|
18030
|
+
let onsets = original.filter((event) => event.partIndex === partIndex && event.type === "noteOn"), sources = onsets.filter((event) => event.scoreEventId === source), exactTargets = onsets.filter((event) => event.scoreEventId === glissando.target), targets = exactTargets.length > 0 ? exactTargets : onsets.filter((event) => event.scoreEventId?.replaceAll("/", "_") === glissando.target), realized = !1;
|
|
18031
|
+
for (let onset of sources) {
|
|
18032
|
+
if (removed.has(onset)) continue;
|
|
18033
|
+
let time = targets.find(
|
|
18034
|
+
(target2) => target2.time > onset.time + 1e-9 && continuousTo(timeline, onset, target2)
|
|
18035
|
+
)?.time;
|
|
18036
|
+
if (time === void 0) continue;
|
|
18037
|
+
let sourceChord = sources.filter((event) => Math.abs(event.time - onset.time) < 1e-9).sort((a, b) => a.midiNote - b.midiNote), targetChord = targets.filter((event) => Math.abs(event.time - time) < 1e-9).sort((a, b) => a.midiNote - b.midiNote), target = targetChord[Math.min(sourceChord.indexOf(onset), targetChord.length - 1)], off = releases.get(onset);
|
|
18038
|
+
if (!off || onset.channel === 9) continue;
|
|
18039
|
+
realized = !0;
|
|
18040
|
+
let type = glissando.playbackType ?? (glissando.kind === "portamento" ? "pitch-bend" : "diatonic");
|
|
18041
|
+
removed.add(onset), removed.add(off);
|
|
18042
|
+
let lane = `${onset.playbackLaneId ?? `part:${partIndex}`}:gliss:${source}:${sourceChord.indexOf(onset)}`;
|
|
18043
|
+
if (type === "pitch-bend") {
|
|
18044
|
+
let bendEvents = renderBend(onset, off, target, lane), bendOnset = bendEvents.find((event) => event.type === "noteOn");
|
|
18045
|
+
bendOnset.scoreDurationBeats = timeline.model.beatAtTime(target.time) - (onset.scoreBeat ?? timeline.model.beatAtTime(onset.time)), added.push(...bendEvents);
|
|
18046
|
+
} else {
|
|
18047
|
+
let key = type === "diatonic" ? keyPitches(score, timeline, onset) : void 0;
|
|
18048
|
+
added.push(
|
|
18049
|
+
...renderSteps(
|
|
18050
|
+
{ ...onset, playbackLaneId: lane },
|
|
18051
|
+
{ ...off, playbackLaneId: lane },
|
|
18052
|
+
target,
|
|
18053
|
+
steppedPitches(onset.midiNote, target.midiNote, key),
|
|
18054
|
+
timeline
|
|
18055
|
+
)
|
|
18056
|
+
);
|
|
18057
|
+
}
|
|
18058
|
+
if (!mirroredLanes.has(lane)) {
|
|
18059
|
+
mirroredLanes.add(lane);
|
|
18060
|
+
for (let event of original)
|
|
18061
|
+
event.partIndex === partIndex && event.playbackLaneId === onset.playbackLaneId && (event.type === "programChange" || event.type === "controlChange") && added.push({ ...event, playbackLaneId: lane });
|
|
18062
|
+
}
|
|
18063
|
+
}
|
|
18064
|
+
realized || diagnostics.push({
|
|
18065
|
+
code: "glissando-playback-unsupported",
|
|
18066
|
+
message: `Glissando from "${source}" to "${glissando.target}" has no forward pitched endpoints on a continuous playback route.`,
|
|
18067
|
+
playbackLaneIds: sources.flatMap((event) => event.playbackLaneId ? [event.playbackLaneId] : [])
|
|
18068
|
+
});
|
|
18069
|
+
}
|
|
18070
|
+
}), timeline.events = original.filter((event) => !removed.has(event)).concat(added), diagnostics;
|
|
18071
|
+
}
|
|
18072
|
+
|
|
17909
18073
|
// packages/midi/src/chordPlayback/index.ts
|
|
17910
18074
|
init_define_import_meta_env();
|
|
17911
18075
|
function getChordPlaybackPart(score) {
|
|
@@ -19260,18 +19424,13 @@ function generateTimeline(inputScore, options) {
|
|
|
19260
19424
|
return aOrder !== bOrder ? aOrder - bOrder : a.instrumentChange !== b.instrumentChange ? a.instrumentChange ? -1 : 1 : a.midiNote - b.midiNote;
|
|
19261
19425
|
};
|
|
19262
19426
|
allEvents.sort(compareEvents);
|
|
19263
|
-
let
|
|
19427
|
+
let diagnostics = [...applyInstrumentPlayback(
|
|
19264
19428
|
score,
|
|
19265
19429
|
allEvents,
|
|
19266
19430
|
{ model, measureStartBeats, measureOrder },
|
|
19267
19431
|
options?.instrumentPrograms,
|
|
19268
19432
|
options?.partPrograms
|
|
19269
|
-
), diagnostics
|
|
19270
|
-
...assignPhysicalMidiChannels(allEvents, score),
|
|
19271
|
-
...instrumentDiagnostics,
|
|
19272
|
-
...rhythm.diagnostics
|
|
19273
|
-
];
|
|
19274
|
-
for (let diagnostic of diagnostics) options?.onDiagnostic?.(diagnostic);
|
|
19433
|
+
), ...rhythm.diagnostics];
|
|
19275
19434
|
allEvents.sort(compareEvents);
|
|
19276
19435
|
let duration2 = model.timeAtBeat(tempoBuild.totalBeats), measureTimeSignatures = measureOrder.map((origIdx) => {
|
|
19277
19436
|
let ts = resolveActiveTime(globalMeasures, origIdx);
|
|
@@ -19287,8 +19446,10 @@ function generateTimeline(inputScore, options) {
|
|
|
19287
19446
|
expandedMeasureToOriginal: measureOrder,
|
|
19288
19447
|
measureTimeSignatures,
|
|
19289
19448
|
diagnostics
|
|
19290
|
-
};
|
|
19291
|
-
|
|
19449
|
+
}, realized = spans && (globalMeasures.some((measure) => measure.jump !== void 0) || spans.some((span) => span.startBeat > 0 || span.endBeat < span.measureBeats)) ? realizeNavigation(result, spans, crossJumpContinuations) : { ...result, playedSpans: spans };
|
|
19450
|
+
diagnostics.push(...applyGlissandoPlayback(score, realized), ...assignPhysicalMidiChannels(realized.events, score));
|
|
19451
|
+
for (let diagnostic of diagnostics) options?.onDiagnostic?.(diagnostic);
|
|
19452
|
+
return realized.events.sort(compareEvents), realized;
|
|
19292
19453
|
}
|
|
19293
19454
|
function assignPhysicalMidiChannels(events, score) {
|
|
19294
19455
|
let melodicChannels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15], lanesByPart = /* @__PURE__ */ new Map();
|