@libraz/libsonare 1.2.3 → 1.3.0
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 +38 -1
- package/dist/index.d.ts +1 -2842
- package/dist/index.js +3602 -1934
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +1 -1
- package/dist/sonare-rt.js +1 -1
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +4816 -483
- package/dist/worklet.js +747 -440
- package/dist/worklet.js.map +1 -1
- package/package.json +2 -1
- package/src/analysis_helpers.ts +152 -0
- package/src/audio.ts +493 -0
- package/src/codes.ts +56 -0
- package/src/effects_mastering.ts +964 -0
- package/src/feature_core.ts +248 -0
- package/src/feature_music.ts +419 -0
- package/src/feature_pitch.ts +80 -0
- package/src/feature_resample.ts +21 -0
- package/src/feature_spectral.ts +330 -0
- package/src/feature_spectrogram.ts +454 -0
- package/src/features.ts +84 -0
- package/src/index.ts +341 -4963
- package/src/live_audio.ts +45 -0
- package/src/metering.ts +380 -0
- package/src/mixer.ts +523 -0
- package/src/module_state.ts +14 -0
- package/src/opfs_clip_pages.ts +188 -0
- package/src/project.ts +1614 -0
- package/src/public_types.ts +177 -2
- package/src/quick_analysis.ts +508 -0
- package/src/realtime_engine.ts +667 -0
- package/src/realtime_voice_changer.ts +275 -0
- package/src/scale.ts +42 -0
- package/src/sonare.js.d.ts +302 -4
- package/src/stream_analyzer.ts +275 -0
- package/src/stream_types.ts +26 -1
- package/src/streaming_mixing.ts +18 -0
- package/src/streaming_processors.ts +335 -0
- package/src/validation.ts +82 -0
- package/src/web_midi.ts +367 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { getSonareModule } from './module_state';
|
|
2
|
+
import type { SynthPatch } from './project';
|
|
3
|
+
import type {
|
|
4
|
+
WasmClipPageRequest,
|
|
5
|
+
WasmEngineAutomationPoint,
|
|
6
|
+
WasmEngineBounceOptions,
|
|
7
|
+
WasmEngineBounceResult,
|
|
8
|
+
WasmEngineCaptureStatus,
|
|
9
|
+
WasmEngineClip,
|
|
10
|
+
WasmEngineFreezeOptions,
|
|
11
|
+
WasmEngineFreezeResult,
|
|
12
|
+
WasmEngineGraphSpec,
|
|
13
|
+
WasmEngineMarker,
|
|
14
|
+
WasmEngineMeterTelemetry,
|
|
15
|
+
WasmEngineMetronomeConfig,
|
|
16
|
+
WasmEngineParameterInfo,
|
|
17
|
+
WasmEngineProcessWithMonitorResult,
|
|
18
|
+
WasmEngineTelemetry,
|
|
19
|
+
WasmEngineTransportState,
|
|
20
|
+
WasmRealtimeEngine,
|
|
21
|
+
} from './sonare.js';
|
|
22
|
+
|
|
23
|
+
export type EngineClip = WasmEngineClip;
|
|
24
|
+
export type ClipPageRequest = WasmClipPageRequest;
|
|
25
|
+
export type EngineParameterInfo = WasmEngineParameterInfo;
|
|
26
|
+
export type EngineAutomationPoint = WasmEngineAutomationPoint;
|
|
27
|
+
export type EngineMarker = WasmEngineMarker;
|
|
28
|
+
export type EngineMetronomeConfig = WasmEngineMetronomeConfig;
|
|
29
|
+
export type EngineGraphSpec = WasmEngineGraphSpec;
|
|
30
|
+
export type EngineCaptureStatus = WasmEngineCaptureStatus;
|
|
31
|
+
export type EngineBounceOptions = WasmEngineBounceOptions;
|
|
32
|
+
export type EngineBounceResult = WasmEngineBounceResult;
|
|
33
|
+
export type EngineFreezeOptions = WasmEngineFreezeOptions;
|
|
34
|
+
export type EngineFreezeResult = WasmEngineFreezeResult;
|
|
35
|
+
export type EngineTelemetry = WasmEngineTelemetry;
|
|
36
|
+
export type EngineMeterTelemetry = WasmEngineMeterTelemetry;
|
|
37
|
+
export type EngineTransportState = WasmEngineTransportState;
|
|
38
|
+
|
|
39
|
+
export const EXPECTED_ENGINE_ABI_VERSION = 3;
|
|
40
|
+
|
|
41
|
+
/** Options for {@link RealtimeEngine.bindMidiCc}. All fields are optional. */
|
|
42
|
+
export interface MidiCcBindOptions {
|
|
43
|
+
/** Lower end of the mapped parameter range. Default `0`. */
|
|
44
|
+
minValue?: number;
|
|
45
|
+
/** Upper end of the mapped parameter range. Default `1`. */
|
|
46
|
+
maxValue?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface EngineCapabilities {
|
|
50
|
+
engineAbiVersion: number;
|
|
51
|
+
expectedEngineAbiVersion: number;
|
|
52
|
+
abiCompatible: boolean;
|
|
53
|
+
sharedArrayBuffer: boolean;
|
|
54
|
+
atomics: boolean;
|
|
55
|
+
audioWorklet: boolean;
|
|
56
|
+
mode: 'sab' | 'postMessage';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function engineCapabilities(): EngineCapabilities {
|
|
60
|
+
const abiVersion = getSonareModule().engineAbiVersion();
|
|
61
|
+
const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';
|
|
62
|
+
const atomics = typeof globalThis.Atomics === 'object';
|
|
63
|
+
const audioWorklet =
|
|
64
|
+
typeof AudioWorkletNode !== 'undefined' ||
|
|
65
|
+
typeof (globalThis as typeof globalThis & { AudioWorkletProcessor?: unknown })
|
|
66
|
+
.AudioWorkletProcessor !== 'undefined';
|
|
67
|
+
return {
|
|
68
|
+
engineAbiVersion: abiVersion,
|
|
69
|
+
expectedEngineAbiVersion: EXPECTED_ENGINE_ABI_VERSION,
|
|
70
|
+
abiCompatible: abiVersion === EXPECTED_ENGINE_ABI_VERSION,
|
|
71
|
+
sharedArrayBuffer,
|
|
72
|
+
atomics,
|
|
73
|
+
audioWorklet,
|
|
74
|
+
mode: sharedArrayBuffer && atomics ? 'sab' : 'postMessage',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Methods added to the embind RealtimeEngine that the generated `sonare.js`
|
|
79
|
+
// declarations only gain after a WASM rebuild. The native handle is cast to this
|
|
80
|
+
// shape so the wrapper can reach them without a stale type error.
|
|
81
|
+
interface WasmRealtimeEngineExt {
|
|
82
|
+
setBuiltinInstrument: (destinationId: number, config: object) => void;
|
|
83
|
+
setSynthInstrument: (destinationId: number, patch: object | string) => void;
|
|
84
|
+
loadSoundFont: (data: Uint8Array) => void;
|
|
85
|
+
setSf2Instrument: (destinationId: number, config: object) => void;
|
|
86
|
+
clearMidiInstrument: (destinationId: number) => void;
|
|
87
|
+
midiInstrumentCount: () => number;
|
|
88
|
+
bindMidiCc: (
|
|
89
|
+
channel: number,
|
|
90
|
+
controller: number,
|
|
91
|
+
paramId: number,
|
|
92
|
+
minValue: number,
|
|
93
|
+
maxValue: number,
|
|
94
|
+
) => void;
|
|
95
|
+
clearMidiCcBindings: () => void;
|
|
96
|
+
midiCcBindingCount: () => number;
|
|
97
|
+
setMidiFx: (destinationId: number, configJson: string) => void;
|
|
98
|
+
clearMidiFx: (destinationId: number) => void;
|
|
99
|
+
setMidiInputSource: (destinationId: number) => void;
|
|
100
|
+
clearMidiInputSource: () => void;
|
|
101
|
+
midiInputPendingCount: () => number;
|
|
102
|
+
createClipPageProvider: (numChannels: number, numSamples: number, pageFrames: number) => number;
|
|
103
|
+
supplyClipPage: (providerId: number, pageIndex: number, channels: Float32Array[]) => void;
|
|
104
|
+
clearClipPage: (providerId: number, pageIndex: number) => void;
|
|
105
|
+
destroyClipPageProvider: (providerId: number) => void;
|
|
106
|
+
popClipPageRequest: () => ClipPageRequest | null;
|
|
107
|
+
pushMidiInputNoteOn: (
|
|
108
|
+
group: number,
|
|
109
|
+
channel: number,
|
|
110
|
+
note: number,
|
|
111
|
+
velocity: number,
|
|
112
|
+
portTimeSamples: number,
|
|
113
|
+
) => void;
|
|
114
|
+
pushMidiInputNoteOff: (
|
|
115
|
+
group: number,
|
|
116
|
+
channel: number,
|
|
117
|
+
note: number,
|
|
118
|
+
velocity: number,
|
|
119
|
+
portTimeSamples: number,
|
|
120
|
+
) => void;
|
|
121
|
+
pushMidiInputCc: (
|
|
122
|
+
group: number,
|
|
123
|
+
channel: number,
|
|
124
|
+
controller: number,
|
|
125
|
+
value: number,
|
|
126
|
+
portTimeSamples: number,
|
|
127
|
+
) => void;
|
|
128
|
+
pushMidiNoteOn: (
|
|
129
|
+
destinationId: number,
|
|
130
|
+
group: number,
|
|
131
|
+
channel: number,
|
|
132
|
+
note: number,
|
|
133
|
+
velocity: number,
|
|
134
|
+
renderFrame: number,
|
|
135
|
+
) => void;
|
|
136
|
+
pushMidiNoteOff: (
|
|
137
|
+
destinationId: number,
|
|
138
|
+
group: number,
|
|
139
|
+
channel: number,
|
|
140
|
+
note: number,
|
|
141
|
+
velocity: number,
|
|
142
|
+
renderFrame: number,
|
|
143
|
+
) => void;
|
|
144
|
+
pushMidiCc: (
|
|
145
|
+
destinationId: number,
|
|
146
|
+
group: number,
|
|
147
|
+
channel: number,
|
|
148
|
+
controller: number,
|
|
149
|
+
value: number,
|
|
150
|
+
renderFrame: number,
|
|
151
|
+
) => void;
|
|
152
|
+
pushMidiPanic: (renderFrame: number) => void;
|
|
153
|
+
clearParameters: () => void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export class RealtimeEngine {
|
|
157
|
+
private native: WasmRealtimeEngine;
|
|
158
|
+
|
|
159
|
+
private nativeExt(): WasmRealtimeEngineExt {
|
|
160
|
+
return this.native as unknown as WasmRealtimeEngineExt;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
constructor(
|
|
164
|
+
sampleRate = 48000,
|
|
165
|
+
maxBlockSize = 128,
|
|
166
|
+
commandCapacity = 1024,
|
|
167
|
+
telemetryCapacity = 1024,
|
|
168
|
+
) {
|
|
169
|
+
const module = getSonareModule();
|
|
170
|
+
const capabilities = engineCapabilities();
|
|
171
|
+
if (!capabilities.abiCompatible) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`Engine ABI mismatch: wasm=${capabilities.engineAbiVersion}, expected=${capabilities.expectedEngineAbiVersion}`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
this.native = new module.RealtimeEngine(
|
|
177
|
+
sampleRate,
|
|
178
|
+
maxBlockSize,
|
|
179
|
+
commandCapacity,
|
|
180
|
+
telemetryCapacity,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
prepare(
|
|
185
|
+
sampleRate: number,
|
|
186
|
+
maxBlockSize: number,
|
|
187
|
+
commandCapacity = 1024,
|
|
188
|
+
telemetryCapacity = 1024,
|
|
189
|
+
): void {
|
|
190
|
+
this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Queue a sample-accurate parameter change (engine kSetParam). */
|
|
194
|
+
setParameter(paramId: number, value: number, renderFrame = -1): void {
|
|
195
|
+
this.native.setParameter(paramId, value, renderFrame);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Queue a smoothed parameter change (engine kSetParamSmoothed). */
|
|
199
|
+
setParameterSmoothed(paramId: number, value: number, renderFrame = -1): void {
|
|
200
|
+
this.native.setParameterSmoothed(paramId, value, renderFrame);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setBuiltinInstrument(
|
|
204
|
+
config: { destinationId?: number } & Record<string, unknown> = {},
|
|
205
|
+
destinationId = config.destinationId ?? 0,
|
|
206
|
+
): void {
|
|
207
|
+
this.nativeExt().setBuiltinInstrument(destinationId, config);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Bind the patch-driven NativeSynth to a realtime MIDI destination. `patch`
|
|
212
|
+
* is a {@link SynthPatch} or a preset-name string (`'saw-lead'` /
|
|
213
|
+
* `'va:saw-lead'`; see {@link synthPresetNames}), resolving exactly like
|
|
214
|
+
* {@link Project.bounceWithSynthInstrument}. Live note/CC commands and
|
|
215
|
+
* scheduled MIDI clips routed to that destination render through the synth.
|
|
216
|
+
* Unknown preset names throw. An object patch's `destinationId` is a JS
|
|
217
|
+
* binding convenience, not part of the NativeSynth patch itself.
|
|
218
|
+
*/
|
|
219
|
+
setSynthInstrument(
|
|
220
|
+
patch: SynthPatch | string = {},
|
|
221
|
+
destinationId = (typeof patch === 'object' ? patch.destinationId : undefined) ?? 0,
|
|
222
|
+
): void {
|
|
223
|
+
this.nativeExt().setSynthInstrument(destinationId, patch);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Load (parse) SoundFont 2 bytes into the engine so SF2 instruments can be
|
|
228
|
+
* bound with {@link setSf2Instrument}. The host fetches the `.sf2` and
|
|
229
|
+
* passes the raw bytes; they are copied into linear memory for the call and
|
|
230
|
+
* not referenced afterwards. Replaces any previously loaded SoundFont.
|
|
231
|
+
*/
|
|
232
|
+
loadSoundFont(data: Uint8Array): void {
|
|
233
|
+
this.nativeExt().loadSoundFont(data);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Bind a GS-compatible SoundFont player to a realtime MIDI destination, fed
|
|
238
|
+
* by the engine's loaded SoundFont ({@link loadSoundFont}). Live note/CC
|
|
239
|
+
* commands and scheduled MIDI clips routed to that destination render
|
|
240
|
+
* through the player (16 MIDI channels, channel 10 drums, GS NRPN part
|
|
241
|
+
* edits, GS/GM SysEx resets). Without a loaded SoundFont — or for programs
|
|
242
|
+
* the SoundFont does not cover — notes play through the built-in
|
|
243
|
+
* synthesizer GM fallback bank (the data-free floor).
|
|
244
|
+
*/
|
|
245
|
+
setSf2Instrument(
|
|
246
|
+
config: { destinationId?: number; gain?: number; polyphony?: number } = {},
|
|
247
|
+
destinationId = config.destinationId ?? 0,
|
|
248
|
+
): void {
|
|
249
|
+
this.nativeExt().setSf2Instrument(destinationId, config);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
clearMidiInstrument(destinationId = 0): void {
|
|
253
|
+
this.nativeExt().clearMidiInstrument(destinationId);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
midiInstrumentCount(): number {
|
|
257
|
+
return this.nativeExt().midiInstrumentCount();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Bind a live MIDI CC to an engine automation parameter. The MIDI event still
|
|
262
|
+
* reaches the destination instrument; when bound, its 7-bit value is also
|
|
263
|
+
* mapped into [minValue, maxValue] for `paramId`.
|
|
264
|
+
*/
|
|
265
|
+
bindMidiCc(
|
|
266
|
+
channel: number,
|
|
267
|
+
controller: number,
|
|
268
|
+
paramId: number,
|
|
269
|
+
options: MidiCcBindOptions = {},
|
|
270
|
+
): void {
|
|
271
|
+
this.nativeExt().bindMidiCc(
|
|
272
|
+
channel,
|
|
273
|
+
controller,
|
|
274
|
+
paramId,
|
|
275
|
+
options.minValue ?? 0,
|
|
276
|
+
options.maxValue ?? 1,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
clearMidiCcBindings(): void {
|
|
281
|
+
this.nativeExt().clearMidiCcBindings();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
midiCcBindingCount(): number {
|
|
285
|
+
return this.nativeExt().midiCcBindingCount();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Install/replace a live non-destructive MIDI-FX insert for one destination. */
|
|
289
|
+
setMidiFx(destinationId: number, configJson: string): void {
|
|
290
|
+
this.nativeExt().setMidiFx(destinationId, configJson);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
clearMidiFx(destinationId = 0): void {
|
|
294
|
+
this.nativeExt().clearMidiFx(destinationId);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Enable the engine-owned live MIDI input source for a destination. */
|
|
298
|
+
setMidiInputSource(destinationId = 0): void {
|
|
299
|
+
this.nativeExt().setMidiInputSource(destinationId);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
clearMidiInputSource(): void {
|
|
303
|
+
this.nativeExt().clearMidiInputSource();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
midiInputPendingCount(): number {
|
|
307
|
+
return this.nativeExt().midiInputPendingCount();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
pushMidiInputNoteOn(
|
|
311
|
+
group: number,
|
|
312
|
+
channel: number,
|
|
313
|
+
note: number,
|
|
314
|
+
velocity: number,
|
|
315
|
+
portTimeSamples = 0,
|
|
316
|
+
): void {
|
|
317
|
+
this.nativeExt().pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
pushMidiInputNoteOff(
|
|
321
|
+
group: number,
|
|
322
|
+
channel: number,
|
|
323
|
+
note: number,
|
|
324
|
+
velocity = 0,
|
|
325
|
+
portTimeSamples = 0,
|
|
326
|
+
): void {
|
|
327
|
+
this.nativeExt().pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
pushMidiInputCc(
|
|
331
|
+
group: number,
|
|
332
|
+
channel: number,
|
|
333
|
+
controller: number,
|
|
334
|
+
value: number,
|
|
335
|
+
portTimeSamples = 0,
|
|
336
|
+
): void {
|
|
337
|
+
this.nativeExt().pushMidiInputCc(group, channel, controller, value, portTimeSamples);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
pushMidiNoteOn(
|
|
341
|
+
destinationId: number,
|
|
342
|
+
group: number,
|
|
343
|
+
channel: number,
|
|
344
|
+
note: number,
|
|
345
|
+
velocity: number,
|
|
346
|
+
renderFrame = -1,
|
|
347
|
+
): void {
|
|
348
|
+
this.nativeExt().pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
pushMidiNoteOff(
|
|
352
|
+
destinationId: number,
|
|
353
|
+
group: number,
|
|
354
|
+
channel: number,
|
|
355
|
+
note: number,
|
|
356
|
+
velocity = 0,
|
|
357
|
+
renderFrame = -1,
|
|
358
|
+
): void {
|
|
359
|
+
this.nativeExt().pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Queue an immediate (live) MIDI control change to a MIDI destination
|
|
364
|
+
* (engine kMidiCcImmediate). `group`/`channel` are 0..15; `controller`/`value`
|
|
365
|
+
* are 7-bit (0..127). `renderFrame` is the frame to fire at, or -1 for
|
|
366
|
+
* immediate. Mirrors the Node/Python/C-ABI `pushMidiCc`.
|
|
367
|
+
*/
|
|
368
|
+
pushMidiCc(
|
|
369
|
+
destinationId: number,
|
|
370
|
+
group: number,
|
|
371
|
+
channel: number,
|
|
372
|
+
controller: number,
|
|
373
|
+
value: number,
|
|
374
|
+
renderFrame = -1,
|
|
375
|
+
): void {
|
|
376
|
+
this.nativeExt().pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Queue a MIDI panic (all-notes-off) releasing every sounding note at
|
|
381
|
+
* `renderFrame` (-1 = immediate). Mirrors the C-ABI `pushMidiPanic`.
|
|
382
|
+
*/
|
|
383
|
+
pushMidiPanic(renderFrame = -1): void {
|
|
384
|
+
this.nativeExt().pushMidiPanic(renderFrame);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Remove all registered parameters (and their automation lanes). Control-thread
|
|
389
|
+
* only; not realtime-safe. Mirrors the C-ABI `clearParameters`.
|
|
390
|
+
*/
|
|
391
|
+
clearParameters(): void {
|
|
392
|
+
this.nativeExt().clearParameters();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Read back the current transport state snapshot. */
|
|
396
|
+
getTransportState(): EngineTransportState {
|
|
397
|
+
return this.native.getTransportState();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
play(renderFrame = -1): void {
|
|
401
|
+
this.native.play(renderFrame);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
stop(renderFrame = -1): void {
|
|
405
|
+
this.native.stop(renderFrame);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
seekSample(timelineSample: number, renderFrame = -1): void {
|
|
409
|
+
this.native.seekSample(timelineSample, renderFrame);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
seekPpq(ppq: number, renderFrame = -1): void {
|
|
413
|
+
this.native.seekPpq(ppq, renderFrame);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
setTempo(bpm: number): void {
|
|
417
|
+
this.native.setTempo(bpm);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
setTimeSignature(numerator: number, denominator: number): void {
|
|
421
|
+
this.native.setTimeSignature(numerator, denominator);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
setLoop(startPpq: number, endPpq: number, enabled = true): void {
|
|
425
|
+
this.native.setLoop(startPpq, endPpq, enabled);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
addParameter(info: EngineParameterInfo): void {
|
|
429
|
+
this.native.addParameter(info);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
parameterCount(): number {
|
|
433
|
+
return this.native.parameterCount();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
parameterInfoByIndex(index: number): EngineParameterInfo {
|
|
437
|
+
return this.native.parameterInfoByIndex(index);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
parameterInfo(id: number): EngineParameterInfo {
|
|
441
|
+
return this.native.parameterInfo(id);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
setAutomationLane(paramId: number, points: EngineAutomationPoint[]): void {
|
|
445
|
+
this.native.setAutomationLane(paramId, points);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
automationLaneCount(): number {
|
|
449
|
+
return this.native.automationLaneCount();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
setMarkers(markers: EngineMarker[]): void {
|
|
453
|
+
this.native.setMarkers(markers);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
markerCount(): number {
|
|
457
|
+
return this.native.markerCount();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
markerByIndex(index: number): EngineMarker {
|
|
461
|
+
return this.native.markerByIndex(index);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
marker(id: number): EngineMarker {
|
|
465
|
+
return this.native.marker(id);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
seekMarker(markerId: number, renderFrame = -1): void {
|
|
469
|
+
this.native.seekMarker(markerId, renderFrame);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
setLoopFromMarkers(startMarkerId: number, endMarkerId: number): void {
|
|
473
|
+
this.native.setLoopFromMarkers(startMarkerId, endMarkerId);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
setMetronome(config: EngineMetronomeConfig): void {
|
|
477
|
+
this.native.setMetronome(config);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
metronome(): Required<EngineMetronomeConfig> {
|
|
481
|
+
return this.native.metronome();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
countInEndSample(startSample: number, bars: number): number {
|
|
485
|
+
return Number(this.native.countInEndSample(startSample, bars));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
setGraph(spec: EngineGraphSpec): void {
|
|
489
|
+
this.native.setGraph(spec);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
graphNodeCount(): number {
|
|
493
|
+
return this.native.graphNodeCount();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
graphConnectionCount(): number {
|
|
497
|
+
return this.native.graphConnectionCount();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
setClips(clips: EngineClip[]): void {
|
|
501
|
+
this.native.setClips(
|
|
502
|
+
clips.map((clip) => ({
|
|
503
|
+
...clip,
|
|
504
|
+
pageProvider:
|
|
505
|
+
typeof clip.pageProvider === 'object' && clip.pageProvider !== null
|
|
506
|
+
? clip.pageProvider.id
|
|
507
|
+
: clip.pageProvider,
|
|
508
|
+
})),
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
clipCount(): number {
|
|
513
|
+
return this.native.clipCount();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
createClipPageProvider(
|
|
517
|
+
numChannels: number,
|
|
518
|
+
numSamples: number,
|
|
519
|
+
pageFrames: number,
|
|
520
|
+
): ClipPageProvider {
|
|
521
|
+
const id = this.nativeExt().createClipPageProvider(numChannels, numSamples, pageFrames);
|
|
522
|
+
return new ClipPageProvider(this, id);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
supplyClipPage(providerId: number, pageIndex: number, channels: Float32Array[]): void {
|
|
526
|
+
this.nativeExt().supplyClipPage(providerId, pageIndex, channels);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
clearClipPage(providerId: number, pageIndex: number): void {
|
|
530
|
+
this.nativeExt().clearClipPage(providerId, pageIndex);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
destroyClipPageProvider(providerId: number): void {
|
|
534
|
+
this.nativeExt().destroyClipPageProvider(providerId);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
popClipPageRequest(): ClipPageRequest | null {
|
|
538
|
+
return this.nativeExt().popClipPageRequest();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
setCaptureBuffer(numChannels: number, capacityFrames: number): void {
|
|
542
|
+
this.native.setCaptureBuffer(numChannels, capacityFrames);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
armCapture(armed = true): void {
|
|
546
|
+
this.native.armCapture(armed);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
setCapturePunch(startSample: number, endSample: number, enabled = true): void {
|
|
550
|
+
this.native.setCapturePunch(startSample, endSample, enabled);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
setCaptureSource(source: EngineCaptureStatus['source']): void {
|
|
554
|
+
this.native.setCaptureSource(source);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
setRecordOffsetSamples(offsetSamples: number): void {
|
|
558
|
+
this.native.setRecordOffsetSamples(offsetSamples);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
setInputMonitor(enabled: boolean, gain = 1): void {
|
|
562
|
+
this.native.setInputMonitor(enabled, gain);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
resetCapture(): void {
|
|
566
|
+
this.native.resetCapture();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
captureStatus(): EngineCaptureStatus {
|
|
570
|
+
return this.native.captureStatus();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
capturedAudio(): Float32Array[] {
|
|
574
|
+
return this.native.capturedAudio();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
process(channels: Float32Array[]): Float32Array[] {
|
|
578
|
+
return this.native.process(channels);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Allocates persistent per-channel WASM-heap scratch for the zero-copy
|
|
583
|
+
* `getChannelBuffer` / `processPrepared` realtime path. Call once (off the
|
|
584
|
+
* audio thread) before driving `processPrepared` from an AudioWorklet so the
|
|
585
|
+
* render callback never allocates on the C++/JS heap.
|
|
586
|
+
*/
|
|
587
|
+
prepareChannels(numChannels: number, maxFrames: number): void {
|
|
588
|
+
this.native.prepareChannels(numChannels, maxFrames);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Returns a Float32Array view onto the persistent WASM-heap scratch for one
|
|
593
|
+
* channel (valid for up to `numFrames`). Fill it, call `processPrepared`, then
|
|
594
|
+
* read the same view back. Re-acquire after WASM memory growth.
|
|
595
|
+
*/
|
|
596
|
+
getChannelBuffer(channel: number, numFrames: number): Float32Array {
|
|
597
|
+
return this.native.getChannelBuffer(channel, numFrames);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Runs the engine in place over the prepared per-channel scratch buffers.
|
|
602
|
+
* Allocation-free: safe to call on the AudioWorklet render thread after
|
|
603
|
+
* `prepareChannels`.
|
|
604
|
+
*/
|
|
605
|
+
processPrepared(numFrames: number): void {
|
|
606
|
+
this.native.processPrepared(numFrames);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
processWithMonitor(channels: Float32Array[]): WasmEngineProcessWithMonitorResult {
|
|
610
|
+
return this.native.processWithMonitor(channels);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
renderOffline(channels: Float32Array[], blockSize = 128): Float32Array[] {
|
|
614
|
+
return this.native.renderOffline(channels, blockSize);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
bounceOffline(options: EngineBounceOptions): EngineBounceResult {
|
|
618
|
+
return this.native.bounceOffline(options);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
freezeOffline(options: EngineFreezeOptions): EngineFreezeResult {
|
|
622
|
+
return this.native.freezeOffline(options);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
drainTelemetry(maxRecords = 1024): EngineTelemetry[] {
|
|
626
|
+
return this.native.drainTelemetry(maxRecords);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
drainMeterTelemetry(maxRecords = 1024): EngineMeterTelemetry[] {
|
|
630
|
+
return this.native.drainMeterTelemetry(maxRecords);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
destroy(): void {
|
|
634
|
+
this.native.delete();
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export class ClipPageProvider {
|
|
639
|
+
private disposed = false;
|
|
640
|
+
|
|
641
|
+
constructor(
|
|
642
|
+
private readonly engine: RealtimeEngine,
|
|
643
|
+
readonly id: number,
|
|
644
|
+
) {}
|
|
645
|
+
|
|
646
|
+
supply(pageIndex: number, channels: Float32Array[]): void {
|
|
647
|
+
if (this.disposed) {
|
|
648
|
+
throw new Error('ClipPageProvider is destroyed');
|
|
649
|
+
}
|
|
650
|
+
this.engine.supplyClipPage(this.id, pageIndex, channels);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
clear(pageIndex: number): void {
|
|
654
|
+
if (this.disposed) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
this.engine.clearClipPage(this.id, pageIndex);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
destroy(): void {
|
|
661
|
+
if (this.disposed) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
this.disposed = true;
|
|
665
|
+
this.engine.destroyClipPageProvider(this.id);
|
|
666
|
+
}
|
|
667
|
+
}
|