@opendaw/studio-core-wasm 0.0.12 → 0.0.14
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/engine-modules.d.ts.map +1 -1
- package/dist/engine-modules.js +1 -0
- package/dist/script-bridge.d.ts.map +1 -1
- package/dist/script-bridge.js +12 -4
- package/dist/script-spielwerk.d.ts +2 -1
- package/dist/script-spielwerk.d.ts.map +1 -1
- package/dist/script-spielwerk.js +31 -6
- package/dist/wasm/engine.wasm +0 -0
- package/dist/wasm/plugins/device_apparat.wasm +0 -0
- package/dist/wasm/plugins/device_autotune.wasm +0 -0
- package/dist/wasm/plugins/device_compressor.wasm +0 -0
- package/dist/wasm/plugins/device_crusher.wasm +0 -0
- package/dist/wasm/plugins/device_cubed.wasm +0 -0
- package/dist/wasm/plugins/device_dattorro_reverb.wasm +0 -0
- package/dist/wasm/plugins/device_delay.wasm +0 -0
- package/dist/wasm/plugins/device_fold.wasm +0 -0
- package/dist/wasm/plugins/device_gate.wasm +0 -0
- package/dist/wasm/plugins/device_maximizer.wasm +0 -0
- package/dist/wasm/plugins/device_nano.wasm +0 -0
- package/dist/wasm/plugins/device_neon.wasm +0 -0
- package/dist/wasm/plugins/device_neural_amp.wasm +0 -0
- package/dist/wasm/plugins/device_playfield_sample.wasm +0 -0
- package/dist/wasm/plugins/device_revamp.wasm +0 -0
- package/dist/wasm/plugins/device_reverb.wasm +0 -0
- package/dist/wasm/plugins/device_soundfont.wasm +0 -0
- package/dist/wasm/plugins/device_stereo_tool.wasm +0 -0
- package/dist/wasm/plugins/device_tidal.wasm +0 -0
- package/dist/wasm/plugins/device_vaporisateur.wasm +0 -0
- package/dist/wasm/plugins/device_vocoder.wasm +0 -0
- package/dist/wasm/plugins/device_waveshaper.wasm +0 -0
- package/dist/wasm/plugins/device_werkstatt.wasm +0 -0
- package/dist/wasm/plugins/device_zeitgeist.wasm +0 -0
- package/dist/wasm-offline-worker.js +43 -40
- package/dist/wasm-offline-worker.js.map +4 -4
- package/dist/wasm-processor.js +17 -16
- package/dist/wasm-processor.js.map +4 -4
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-modules.d.ts","sourceRoot":"","sources":["../src/engine-modules.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,aAAa,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAG7E,mBAAmB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAE3E,eAAe,EAAE,MAAM,CAAC;IAGxB,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAG3C,cAAc,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAC9C,CAAA;AASD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,mBAAmB,CAAA;IACzB,WAAW,EAAE,0BAA0B,CAAA;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C,CAAA;AAGD,oBAAY,mBAAmB;IAAE,WAAW,IAAI;IAAE,UAAU,IAAI;CAAC;AAGjE,oBAAY,0BAA0B;IAAE,SAAS,IAAI;IAAE,MAAM,IAAI;IAAE,SAAS,IAAI;CAAC;AAEjF,MAAM,MAAM,aAAa,GAAG;IACxB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAA;IAChC,aAAa,EAAE,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAChD,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,UAAU,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACxC,gBAAgB,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;CACvD,CAAA;AAQD,eAAO,MAAM,kBAAkB,QAAO,WAAW,CAAC,MAAgD,CAAA;AAMlG,eAAO,MAAM,OAAO,EAAE,aAAa,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"engine-modules.d.ts","sourceRoot":"","sources":["../src/engine-modules.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,aAAa,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAG7E,mBAAmB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAE3E,eAAe,EAAE,MAAM,CAAC;IAGxB,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAG3C,cAAc,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAC9C,CAAA;AASD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,mBAAmB,CAAA;IACzB,WAAW,EAAE,0BAA0B,CAAA;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C,CAAA;AAGD,oBAAY,mBAAmB;IAAE,WAAW,IAAI;IAAE,UAAU,IAAI;CAAC;AAGjE,oBAAY,0BAA0B;IAAE,SAAS,IAAI;IAAE,MAAM,IAAI;IAAE,SAAS,IAAI;CAAC;AAEjF,MAAM,MAAM,aAAa,GAAG;IACxB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAA;IAChC,aAAa,EAAE,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAChD,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,UAAU,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACxC,gBAAgB,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;CACvD,CAAA;AAQD,eAAO,MAAM,kBAAkB,QAAO,WAAW,CAAC,MAAgD,CAAA;AAMlG,eAAO,MAAM,OAAO,EAAE,aAAa,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6BnE,CAAA;AAKD,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,aAAa,CAWnD,CAAA;AAMD,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC,mBAAmB,CA0BhE,CAAA;AAED,eAAO,MAAM,iBAAiB,GAAU,OAAM,MAAW,KAAG,OAAO,CAAC,aAAa,CAUhF,CAAA"}
|
package/dist/engine-modules.js
CHANGED
|
@@ -49,6 +49,7 @@ export const DEVICES = [
|
|
|
49
49
|
{ url: "/wasm/plugins/device_reverb.wasm", boxType: "ReverbDeviceBox" }, // audio effect
|
|
50
50
|
{ url: "/wasm/plugins/device_dattorro_reverb.wasm", boxType: "DattorroReverbDeviceBox" }, // audio effect
|
|
51
51
|
{ url: "/wasm/plugins/device_soundfont.wasm", boxType: "SoundfontDeviceBox" }, // instrument (preset sampler)
|
|
52
|
+
{ url: "/wasm/plugins/device_cubed.wasm", boxType: "CubedDeviceBox" }, // instrument (303-style bassline)
|
|
52
53
|
{ url: "/wasm/plugins/device_vocoder.wasm", boxType: "VocoderDeviceBox" }, // audio effect (channel vocoder + sidechain)
|
|
53
54
|
{ url: "/wasm/plugins/device_neural_amp.wasm", boxType: "NeuralAmpDeviceBox" }, // audio effect (NAM, via the nam bridge)
|
|
54
55
|
{ url: "/wasm/plugins/device_autotune.wasm", boxType: "AutotuneDeviceBox" }, // audio effect (pitch correction, PSOLA)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-bridge.d.ts","sourceRoot":"","sources":["../src/script-bridge.ts"],"names":[],"mappings":"AA2CA,MAAM,WAAW,YAAY;IACzB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CACxC;
|
|
1
|
+
{"version":3,"file":"script-bridge.d.ts","sourceRoot":"","sources":["../src/script-bridge.ts"],"names":[],"mappings":"AA2CA,MAAM,WAAW,YAAY;IACzB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CACxC;AAyDD,qBAAa,aAAa;;gBASV,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EACpE,SAAS,GAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAe;IAQzE,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,IAAI,CAAC;IAoN/D,eAAe,IAAI,MAAM;CAG5B"}
|
package/dist/script-bridge.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// views EVERY call, since the SharedArrayBuffer can grow / detach (talc), never caching a typed-array view.
|
|
13
13
|
import { isDefined, UUID, ValueMapping } from "@opendaw/lib-std";
|
|
14
14
|
import { SimpleLimiter } from "@opendaw/lib-dsp";
|
|
15
|
-
import { runSpielwerk, SpielwerkRuntime } from "./script-spielwerk";
|
|
15
|
+
import { copyEvents, runSpielwerk, SpielwerkRuntime } from "./script-spielwerk";
|
|
16
16
|
const RENDER_QUANTUM = 128;
|
|
17
17
|
// ~1 second of render calls (128-frame quanta at 48k) before a scriptless device is reported — long enough to
|
|
18
18
|
// cover a script still compiling / addModule-ing async, short enough to surface a genuine misconfiguration.
|
|
@@ -49,6 +49,9 @@ class Bridge {
|
|
|
49
49
|
sampleHandles = new Map(); // index -> resolved engine sample handle (-1 = no sample; 0 is valid)
|
|
50
50
|
limiter = null;
|
|
51
51
|
spielwerk = null;
|
|
52
|
+
// Spielwerk's `@no-pass` verdict for the CURRENTLY loaded script. `true` by default so a device whose script
|
|
53
|
+
// never loaded (or died) forwards its input instead of muting the chain.
|
|
54
|
+
pass = true;
|
|
52
55
|
// A scriptable device with no registered Processor renders silence. That is legitimate for a block or two
|
|
53
56
|
// while the script loads async, but a device that stays scriptless is an anomaly the host must surface (not
|
|
54
57
|
// swallow). Count the render calls that found no registry entry; warn ONCE past the grace window.
|
|
@@ -143,6 +146,8 @@ export class ScriptBridges {
|
|
|
143
146
|
return null;
|
|
144
147
|
}
|
|
145
148
|
if (registry !== undefined && registry.update !== bridge.currentUpdate) {
|
|
149
|
+
// Outside the try: a script that throws in its constructor still gets ITS declared note routing.
|
|
150
|
+
bridge.pass = registry.pass !== false;
|
|
146
151
|
try {
|
|
147
152
|
const proc = new registry.create();
|
|
148
153
|
bridge.paramMappings = new Map(registry.params.map(declaration => [declaration.index, { label: declaration.label, mapping: resolveMapping(declaration) }]));
|
|
@@ -277,16 +282,19 @@ export class ScriptBridges {
|
|
|
277
282
|
if (bridge === undefined) {
|
|
278
283
|
return 0;
|
|
279
284
|
}
|
|
285
|
+
// No live Processor (never loaded, or silenced): forward the input, so a scriptless / broken Spielwerk
|
|
286
|
+
// is transparent rather than a mute button. A `@no-pass` script keeps its silence, as its author asked.
|
|
287
|
+
const forward = () => bridge.pass ? copyEvents(this.#memory.buffer, inPtr, inCount, outPtr, outMax) : 0;
|
|
280
288
|
const proc = this.#ensureProc(bridge);
|
|
281
289
|
if (proc === null || bridge.spielwerk === null) {
|
|
282
|
-
return
|
|
290
|
+
return forward();
|
|
283
291
|
}
|
|
284
292
|
try {
|
|
285
|
-
return runSpielwerk(bridge.spielwerk, proc, this.#memory.buffer, inPtr, inCount, outPtr, outMax, from, to, bpm, flags, s0, s1);
|
|
293
|
+
return runSpielwerk(bridge.spielwerk, proc, this.#memory.buffer, inPtr, inCount, outPtr, outMax, from, to, bpm, flags, s0, s1, bridge.pass);
|
|
286
294
|
}
|
|
287
295
|
catch (error) {
|
|
288
296
|
this.#silence(bridge, `Runtime error: ${error}`);
|
|
289
|
-
return
|
|
297
|
+
return forward();
|
|
290
298
|
}
|
|
291
299
|
}
|
|
292
300
|
// Resolve a sample handle's resident planar frames and hand the user proc an AudioData view (no copy).
|
|
@@ -19,6 +19,7 @@ export declare class SpielwerkRuntime {
|
|
|
19
19
|
readonly sourceToOutput: Map<number, Set<number>>;
|
|
20
20
|
reset(): void;
|
|
21
21
|
}
|
|
22
|
-
export declare const
|
|
22
|
+
export declare const copyEvents: (memory: ArrayBufferLike, inPtr: number, inCount: number, outPtr: number, outMax: number) => number;
|
|
23
|
+
export declare const runSpielwerk: (runtime: SpielwerkRuntime, proc: any, memory: ArrayBufferLike, inPtr: number, inCount: number, outPtr: number, outMax: number, from: number, to: number, bpm: number, flags: number, s0: number, s1: number, pass: boolean) => number;
|
|
23
24
|
export {};
|
|
24
25
|
//# sourceMappingURL=script-spielwerk.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-spielwerk.d.ts","sourceRoot":"","sources":["../src/script-spielwerk.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"script-spielwerk.d.ts","sourceRoot":"","sources":["../src/script-spielwerk.ts"],"names":[],"mappings":"AAuBA,KAAK,aAAa,GAAG;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAA;AACxG,KAAK,YAAY,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAA;AAKnH,qBAAa,gBAAgB;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAK;IACtC,QAAQ,CAAC,SAAS,EAAE,aAAa,EAAE,CAAK;IACxC,QAAQ,CAAC,cAAc,2BAAiC;IAExD,KAAK,IAAI,IAAI;CAKhB;AAgBD,eAAO,MAAM,UAAU,GAAI,QAAQ,eAAe,EAAE,OAAO,MAAM,EAAE,SAAS,MAAM,EACvD,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG,MAI3D,CAAA;AAiBD,eAAO,MAAM,YAAY,GAAI,SAAS,gBAAgB,EAAE,MAAM,GAAG,EAAE,QAAQ,eAAe,EAC7D,OAAO,MAAM,EAAE,SAAS,MAAM,EAAE,QAAQ,MAAM,EAAE,QAAQ,MAAM,EAC9D,MAAM,MAAM,EAAE,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,EAC5E,MAAM,OAAO,KAAG,MAoG5C,CAAA"}
|
package/dist/script-spielwerk.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Spielwerk's stateful note-transform orchestration (mirrors the TS `SpielwerkDeviceProcessor.processNotes`),
|
|
2
2
|
// run JS-side by the script bridge. It reads the upstream input `EventRecord`s the device pulled (note-on /
|
|
3
|
-
// note-off),
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// note-off), FORWARDS them verbatim unless the script declared `// @no-pass`, builds the `UserEvent` stream,
|
|
4
|
+
// runs the user generator `*process(block, events)`, validates each yielded note, schedules future-block notes,
|
|
5
|
+
// and correlates note-on -> note-off via a retainer so it can emit the stops. Forwarded notes are NOT retained:
|
|
6
|
+
// their note-off arrives from upstream. The persistent state (retainer + scheduler) lives in `SpielwerkRuntime`
|
|
7
|
+
// across blocks.
|
|
6
8
|
//
|
|
7
9
|
// EventRecord (abi, 40 bytes, little-endian): position f64@0, offset u32@8, kind u32@12, id u32@16,
|
|
8
10
|
// pitch u32@20, velocity f32@24, cent f32@28, duration f64@32. kind: 0 = note-on, 1 = note-off. `duration` is
|
|
@@ -12,6 +14,11 @@ const KIND_NOTE_ON = 0;
|
|
|
12
14
|
const KIND_NOTE_OFF = 1;
|
|
13
15
|
const MAX_NOTES_PER_BLOCK = 128;
|
|
14
16
|
const MAX_SCHEDULED_NOTES = 128;
|
|
17
|
+
// Forwarded input records keep their UPSTREAM ids, so the ids this device MINTS must live in a disjoint range:
|
|
18
|
+
// the consumer matches a note-off to its voice by id, and a collision would stop the wrong note.
|
|
19
|
+
const GENERATED_ID_BASE = 0x4000_0000;
|
|
20
|
+
const GENERATED_ID_MASK = 0x3fff_ffff;
|
|
21
|
+
const FLAG_DISCONTINUOUS = 1 << 1; // mirrors abi `BlockFlags::DISCONTINUOUS`: a loop wrap or a seek
|
|
15
22
|
let nextOutputId = 1;
|
|
16
23
|
// One Spielwerk instance's cross-block state.
|
|
17
24
|
export class SpielwerkRuntime {
|
|
@@ -35,6 +42,13 @@ const writeRecord = (view, slot, kind, position, id, pitch, velocity, cent, dura
|
|
|
35
42
|
view.setFloat32(base + 28, cent, true);
|
|
36
43
|
view.setFloat64(base + 32, duration, true);
|
|
37
44
|
};
|
|
45
|
+
// Forward the pulled input records UNCHANGED (id, position, duration, pitch, velocity, cent). Used both as the
|
|
46
|
+
// pass-through prologue of a run and as the fallback when there is no live Processor to run at all.
|
|
47
|
+
export const copyEvents = (memory, inPtr, inCount, outPtr, outMax) => {
|
|
48
|
+
const count = Math.min(inCount, outMax);
|
|
49
|
+
new Uint8Array(memory, outPtr, count * RECORD_SIZE).set(new Uint8Array(memory, inPtr, count * RECORD_SIZE));
|
|
50
|
+
return count;
|
|
51
|
+
};
|
|
38
52
|
const validateNote = (note, from) => {
|
|
39
53
|
if (note === undefined || note === null) {
|
|
40
54
|
return "process yielded undefined";
|
|
@@ -67,16 +81,27 @@ const validateNote = (note, from) => {
|
|
|
67
81
|
};
|
|
68
82
|
// Run one pulled range through the user generator + tracking, writing output records, returning the count.
|
|
69
83
|
// Throws on a script error / validation failure / flood (the bridge catches it and silences).
|
|
70
|
-
export const runSpielwerk = (runtime, proc, memory, inPtr, inCount, outPtr, outMax, from, to, bpm, flags, s0, s1) => {
|
|
84
|
+
export const runSpielwerk = (runtime, proc, memory, inPtr, inCount, outPtr, outMax, from, to, bpm, flags, s0, s1, pass) => {
|
|
71
85
|
const input = new DataView(memory, inPtr, inCount * RECORD_SIZE);
|
|
72
86
|
const out = new DataView(memory, outPtr, outMax * RECORD_SIZE);
|
|
73
|
-
|
|
87
|
+
// Pass-through prologue: the input goes out verbatim (the output is still empty, so one bulk copy), and the
|
|
88
|
+
// generator's yields are ADDED on top. `@no-pass` scripts skip this and own the output alone.
|
|
89
|
+
let outCount = pass ? copyEvents(memory, inPtr, inCount, outPtr, outMax) : 0;
|
|
74
90
|
const emit = (kind, position, id, pitch, velocity, cent, duration) => {
|
|
75
91
|
if (outCount >= outMax) {
|
|
76
92
|
throw new Error(`Note flood: exceeded ${outMax} output notes per range`);
|
|
77
93
|
}
|
|
78
94
|
writeRecord(out, outCount++, kind, position, id, pitch, velocity, cent, duration);
|
|
79
95
|
};
|
|
96
|
+
// A transport JUMP (loop wrap / seek) releases EVERYTHING held, at `from` — the contract the engine's own
|
|
97
|
+
// note source honours (`NoteSequencer::process_notes`). Without it a note whose span reaches past the loop
|
|
98
|
+
// end never sees its end position again and the downstream instrument holds that voice forever.
|
|
99
|
+
if ((flags & FLAG_DISCONTINUOUS) !== 0) {
|
|
100
|
+
for (const note of runtime.retained) {
|
|
101
|
+
emit(KIND_NOTE_OFF, from, note.id, note.pitch, 0, 0, 0);
|
|
102
|
+
}
|
|
103
|
+
runtime.reset();
|
|
104
|
+
}
|
|
80
105
|
// Release retained notes whose span completed within this range, emitting their note-off.
|
|
81
106
|
for (let i = runtime.retained.length - 1; i >= 0; i--) {
|
|
82
107
|
const note = runtime.retained[i];
|
|
@@ -173,7 +198,7 @@ export const runSpielwerk = (runtime, proc, memory, inPtr, inCount, outPtr, outM
|
|
|
173
198
|
return outCount;
|
|
174
199
|
};
|
|
175
200
|
const retain = (runtime, note) => {
|
|
176
|
-
const id = nextOutputId
|
|
201
|
+
const id = GENERATED_ID_BASE + (nextOutputId++ & GENERATED_ID_MASK);
|
|
177
202
|
runtime.retained.push({ id, position: note.position, duration: note.duration, pitch: note.pitch, velocity: note.velocity, cent: note.cent });
|
|
178
203
|
return id;
|
|
179
204
|
};
|
package/dist/wasm/engine.wasm
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|