@libraz/libsonare 1.3.1 → 1.3.3

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.
@@ -1,5 +1,7 @@
1
+ import { ErrorCode, SonareError } from './errors';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type { SynthPatch } from './project';
4
+ import type { EqBand } from './public_types';
3
5
  import type {
4
6
  WasmClipPageRequest,
5
7
  WasmEngineAutomationPoint,
@@ -16,6 +18,8 @@ import type {
16
18
  WasmEngineParameterInfo,
17
19
  WasmEngineProcessWithMonitorResult,
18
20
  WasmEngineTelemetry,
21
+ WasmEngineTempoSegment,
22
+ WasmEngineTimeSignatureSegment,
19
23
  WasmEngineTransportState,
20
24
  WasmRealtimeEngine,
21
25
  } from './sonare.js';
@@ -35,6 +39,49 @@ export type EngineFreezeResult = WasmEngineFreezeResult;
35
39
  export type EngineTelemetry = WasmEngineTelemetry;
36
40
  export type EngineMeterTelemetry = WasmEngineMeterTelemetry;
37
41
  export type EngineTransportState = WasmEngineTransportState;
42
+ export type EngineTempoSegment = WasmEngineTempoSegment;
43
+ export type EngineTimeSignatureSegment = WasmEngineTimeSignatureSegment;
44
+
45
+ export interface EngineTrackSend {
46
+ busId: number;
47
+ levelDb?: number;
48
+ enabled?: boolean;
49
+ }
50
+
51
+ export interface EngineTrackLane {
52
+ trackId: number;
53
+ sends?: EngineTrackSend[];
54
+ }
55
+
56
+ export interface EngineBus {
57
+ busId: number;
58
+ gainDb?: number;
59
+ }
60
+
61
+ export interface EngineMidiEvent {
62
+ renderFrame: number;
63
+ word0?: number;
64
+ word1?: number;
65
+ word2?: number;
66
+ word3?: number;
67
+ wordCount?: number;
68
+ group?: number;
69
+ sysexHandle?: number;
70
+ data0?: number;
71
+ data1?: number;
72
+ }
73
+
74
+ export interface EngineMidiClipSchedule {
75
+ id?: number;
76
+ trackId?: number;
77
+ destinationId?: number;
78
+ startSample?: number;
79
+ startPpq?: number;
80
+ lengthSamples?: number;
81
+ loop?: boolean;
82
+ loopLengthSamples?: number;
83
+ events: EngineMidiEvent[];
84
+ }
38
85
 
39
86
  export const EXPECTED_ENGINE_ABI_VERSION = 3;
40
87
 
@@ -75,91 +122,9 @@ export function engineCapabilities(): EngineCapabilities {
75
122
  };
76
123
  }
77
124
 
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
125
  export class RealtimeEngine {
157
126
  private native: WasmRealtimeEngine;
158
127
 
159
- private nativeExt(): WasmRealtimeEngineExt {
160
- return this.native as unknown as WasmRealtimeEngineExt;
161
- }
162
-
163
128
  constructor(
164
129
  sampleRate = 48000,
165
130
  maxBlockSize = 128,
@@ -200,11 +165,19 @@ export class RealtimeEngine {
200
165
  this.native.setParameterSmoothed(paramId, value, renderFrame);
201
166
  }
202
167
 
168
+ setSoloMute(laneIndex: number, solo: boolean, mute: boolean, renderFrame = -1): void {
169
+ this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
170
+ }
171
+
172
+ setMidiClips(clips: readonly EngineMidiClipSchedule[]): void {
173
+ this.native.setMidiClips(clips);
174
+ }
175
+
203
176
  setBuiltinInstrument(
204
177
  config: { destinationId?: number } & Record<string, unknown> = {},
205
178
  destinationId = config.destinationId ?? 0,
206
179
  ): void {
207
- this.nativeExt().setBuiltinInstrument(destinationId, config);
180
+ this.native.setBuiltinInstrument(destinationId, config);
208
181
  }
209
182
 
210
183
  /**
@@ -220,7 +193,7 @@ export class RealtimeEngine {
220
193
  patch: SynthPatch | string = {},
221
194
  destinationId = (typeof patch === 'object' ? patch.destinationId : undefined) ?? 0,
222
195
  ): void {
223
- this.nativeExt().setSynthInstrument(destinationId, patch);
196
+ this.native.setSynthInstrument(destinationId, patch);
224
197
  }
225
198
 
226
199
  /**
@@ -230,7 +203,7 @@ export class RealtimeEngine {
230
203
  * not referenced afterwards. Replaces any previously loaded SoundFont.
231
204
  */
232
205
  loadSoundFont(data: Uint8Array): void {
233
- this.nativeExt().loadSoundFont(data);
206
+ this.native.loadSoundFont(data);
234
207
  }
235
208
 
236
209
  /**
@@ -246,15 +219,15 @@ export class RealtimeEngine {
246
219
  config: { destinationId?: number; gain?: number; polyphony?: number } = {},
247
220
  destinationId = config.destinationId ?? 0,
248
221
  ): void {
249
- this.nativeExt().setSf2Instrument(destinationId, config);
222
+ this.native.setSf2Instrument(destinationId, config);
250
223
  }
251
224
 
252
225
  clearMidiInstrument(destinationId = 0): void {
253
- this.nativeExt().clearMidiInstrument(destinationId);
226
+ this.native.clearMidiInstrument(destinationId);
254
227
  }
255
228
 
256
229
  midiInstrumentCount(): number {
257
- return this.nativeExt().midiInstrumentCount();
230
+ return this.native.midiInstrumentCount();
258
231
  }
259
232
 
260
233
  /**
@@ -268,7 +241,7 @@ export class RealtimeEngine {
268
241
  paramId: number,
269
242
  options: MidiCcBindOptions = {},
270
243
  ): void {
271
- this.nativeExt().bindMidiCc(
244
+ this.native.bindMidiCc(
272
245
  channel,
273
246
  controller,
274
247
  paramId,
@@ -278,33 +251,33 @@ export class RealtimeEngine {
278
251
  }
279
252
 
280
253
  clearMidiCcBindings(): void {
281
- this.nativeExt().clearMidiCcBindings();
254
+ this.native.clearMidiCcBindings();
282
255
  }
283
256
 
284
257
  midiCcBindingCount(): number {
285
- return this.nativeExt().midiCcBindingCount();
258
+ return this.native.midiCcBindingCount();
286
259
  }
287
260
 
288
261
  /** Install/replace a live non-destructive MIDI-FX insert for one destination. */
289
262
  setMidiFx(destinationId: number, configJson: string): void {
290
- this.nativeExt().setMidiFx(destinationId, configJson);
263
+ this.native.setMidiFx(destinationId, configJson);
291
264
  }
292
265
 
293
266
  clearMidiFx(destinationId = 0): void {
294
- this.nativeExt().clearMidiFx(destinationId);
267
+ this.native.clearMidiFx(destinationId);
295
268
  }
296
269
 
297
270
  /** Enable the engine-owned live MIDI input source for a destination. */
298
271
  setMidiInputSource(destinationId = 0): void {
299
- this.nativeExt().setMidiInputSource(destinationId);
272
+ this.native.setMidiInputSource(destinationId);
300
273
  }
301
274
 
302
275
  clearMidiInputSource(): void {
303
- this.nativeExt().clearMidiInputSource();
276
+ this.native.clearMidiInputSource();
304
277
  }
305
278
 
306
279
  midiInputPendingCount(): number {
307
- return this.nativeExt().midiInputPendingCount();
280
+ return this.native.midiInputPendingCount();
308
281
  }
309
282
 
310
283
  pushMidiInputNoteOn(
@@ -314,7 +287,7 @@ export class RealtimeEngine {
314
287
  velocity: number,
315
288
  portTimeSamples = 0,
316
289
  ): void {
317
- this.nativeExt().pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
290
+ this.native.pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
318
291
  }
319
292
 
320
293
  pushMidiInputNoteOff(
@@ -324,7 +297,7 @@ export class RealtimeEngine {
324
297
  velocity = 0,
325
298
  portTimeSamples = 0,
326
299
  ): void {
327
- this.nativeExt().pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
300
+ this.native.pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
328
301
  }
329
302
 
330
303
  pushMidiInputCc(
@@ -334,7 +307,7 @@ export class RealtimeEngine {
334
307
  value: number,
335
308
  portTimeSamples = 0,
336
309
  ): void {
337
- this.nativeExt().pushMidiInputCc(group, channel, controller, value, portTimeSamples);
310
+ this.native.pushMidiInputCc(group, channel, controller, value, portTimeSamples);
338
311
  }
339
312
 
340
313
  pushMidiNoteOn(
@@ -345,7 +318,7 @@ export class RealtimeEngine {
345
318
  velocity: number,
346
319
  renderFrame = -1,
347
320
  ): void {
348
- this.nativeExt().pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
321
+ this.native.pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
349
322
  }
350
323
 
351
324
  pushMidiNoteOff(
@@ -356,7 +329,7 @@ export class RealtimeEngine {
356
329
  velocity = 0,
357
330
  renderFrame = -1,
358
331
  ): void {
359
- this.nativeExt().pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
332
+ this.native.pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
360
333
  }
361
334
 
362
335
  /**
@@ -373,7 +346,7 @@ export class RealtimeEngine {
373
346
  value: number,
374
347
  renderFrame = -1,
375
348
  ): void {
376
- this.nativeExt().pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
349
+ this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
377
350
  }
378
351
 
379
352
  /**
@@ -381,7 +354,7 @@ export class RealtimeEngine {
381
354
  * `renderFrame` (-1 = immediate). Mirrors the C-ABI `pushMidiPanic`.
382
355
  */
383
356
  pushMidiPanic(renderFrame = -1): void {
384
- this.nativeExt().pushMidiPanic(renderFrame);
357
+ this.native.pushMidiPanic(renderFrame);
385
358
  }
386
359
 
387
360
  /**
@@ -389,7 +362,7 @@ export class RealtimeEngine {
389
362
  * only; not realtime-safe. Mirrors the C-ABI `clearParameters`.
390
363
  */
391
364
  clearParameters(): void {
392
- this.nativeExt().clearParameters();
365
+ this.native.clearParameters();
393
366
  }
394
367
 
395
368
  /** Read back the current transport state snapshot. */
@@ -417,10 +390,22 @@ export class RealtimeEngine {
417
390
  this.native.setTempo(bpm);
418
391
  }
419
392
 
393
+ setTempoSegments(segments: readonly EngineTempoSegment[]): void {
394
+ this.native.setTempoSegments([...segments]);
395
+ }
396
+
420
397
  setTimeSignature(numerator: number, denominator: number): void {
421
398
  this.native.setTimeSignature(numerator, denominator);
422
399
  }
423
400
 
401
+ setTimeSignatureSegments(segments: readonly EngineTimeSignatureSegment[]): void {
402
+ this.native.setTimeSignatureSegments([...segments]);
403
+ }
404
+
405
+ sampleAtPpq(ppq: number): number {
406
+ return Number(this.native.sampleAtPpq(ppq));
407
+ }
408
+
424
409
  setLoop(startPpq: number, endPpq: number, enabled = true): void {
425
410
  this.native.setLoop(startPpq, endPpq, enabled);
426
411
  }
@@ -513,29 +498,109 @@ export class RealtimeEngine {
513
498
  return this.native.clipCount();
514
499
  }
515
500
 
501
+ setTrackLanes(lanes: Array<number | EngineTrackLane>): void {
502
+ this.native.setTrackLanes(
503
+ lanes.map((lane) => (typeof lane === 'number' ? { trackId: lane } : lane)),
504
+ );
505
+ }
506
+
507
+ setTrackBuses(buses: EngineBus[]): void {
508
+ this.native.setTrackBuses(buses);
509
+ }
510
+
511
+ setBusStripJson(busId: number, sceneJson: string): void {
512
+ try {
513
+ JSON.parse(sceneJson);
514
+ } catch (error) {
515
+ const message = error instanceof Error ? error.message : 'invalid bus strip JSON';
516
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
517
+ }
518
+ this.native.setBusStripJson(busId, sceneJson);
519
+ }
520
+
521
+ setTrackStripJson(trackId: number, sceneJson: string): void {
522
+ try {
523
+ JSON.parse(sceneJson);
524
+ } catch (error) {
525
+ const message = error instanceof Error ? error.message : 'invalid track strip JSON';
526
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
527
+ }
528
+ this.native.setTrackStripJson(trackId, sceneJson);
529
+ }
530
+
531
+ setTrackStripEqBand(trackId: number, bandIndex: number, band: EqBand | string): void {
532
+ this.native.setTrackStripEqBandJson(
533
+ trackId,
534
+ bandIndex,
535
+ typeof band === 'string' ? band : JSON.stringify(band),
536
+ );
537
+ }
538
+
539
+ setTrackStripEqBandJson(trackId: number, bandIndex: number, bandJson: string): void {
540
+ this.native.setTrackStripEqBandJson(trackId, bandIndex, bandJson);
541
+ }
542
+
543
+ setTrackStripInsertBypassed(
544
+ trackId: number,
545
+ insertIndex: number,
546
+ bypassed: boolean,
547
+ resetOnBypass = false,
548
+ ): void {
549
+ this.native.setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass);
550
+ }
551
+
552
+ setMasterStripJson(sceneJson: string): void {
553
+ try {
554
+ JSON.parse(sceneJson);
555
+ } catch (error) {
556
+ const message = error instanceof Error ? error.message : 'invalid master strip JSON';
557
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
558
+ }
559
+ this.native.setMasterStripJson(sceneJson);
560
+ }
561
+
562
+ setMasterStripEqBand(bandIndex: number, band: EqBand | string): void {
563
+ this.native.setMasterStripEqBandJson(
564
+ bandIndex,
565
+ typeof band === 'string' ? band : JSON.stringify(band),
566
+ );
567
+ }
568
+
569
+ setMasterStripEqBandJson(bandIndex: number, bandJson: string): void {
570
+ this.native.setMasterStripEqBandJson(bandIndex, bandJson);
571
+ }
572
+
573
+ setMasterStripInsertBypassed(
574
+ insertIndex: number,
575
+ bypassed: boolean,
576
+ resetOnBypass = false,
577
+ ): void {
578
+ this.native.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
579
+ }
580
+
516
581
  createClipPageProvider(
517
582
  numChannels: number,
518
583
  numSamples: number,
519
584
  pageFrames: number,
520
585
  ): ClipPageProvider {
521
- const id = this.nativeExt().createClipPageProvider(numChannels, numSamples, pageFrames);
586
+ const id = this.native.createClipPageProvider(numChannels, numSamples, pageFrames);
522
587
  return new ClipPageProvider(this, id);
523
588
  }
524
589
 
525
590
  supplyClipPage(providerId: number, pageIndex: number, channels: Float32Array[]): void {
526
- this.nativeExt().supplyClipPage(providerId, pageIndex, channels);
591
+ this.native.supplyClipPage(providerId, pageIndex, channels);
527
592
  }
528
593
 
529
594
  clearClipPage(providerId: number, pageIndex: number): void {
530
- this.nativeExt().clearClipPage(providerId, pageIndex);
595
+ this.native.clearClipPage(providerId, pageIndex);
531
596
  }
532
597
 
533
598
  destroyClipPageProvider(providerId: number): void {
534
- this.nativeExt().destroyClipPageProvider(providerId);
599
+ this.native.destroyClipPageProvider(providerId);
535
600
  }
536
601
 
537
602
  popClipPageRequest(): ClipPageRequest | null {
538
- return this.nativeExt().popClipPageRequest();
603
+ return this.native.popClipPageRequest();
539
604
  }
540
605
 
541
606
  setCaptureBuffer(numChannels: number, capacityFrames: number): void {
@@ -4,6 +4,7 @@
4
4
 
5
5
  export interface SonareModuleOptions {
6
6
  locateFile?: (path: string, prefix: string) => string;
7
+ wasmBinary?: ArrayBuffer | Uint8Array;
7
8
  onRuntimeInitialized?: () => void;
8
9
  print?: (text: string) => void;
9
10
  printErr?: (text: string) => void;
@@ -518,6 +519,7 @@ export interface WasmMixResult {
518
519
 
519
520
  export interface WasmEngineClip {
520
521
  id?: number;
522
+ trackId?: number;
521
523
  channels?: Float32Array[];
522
524
  startPpq: number;
523
525
  lengthSamples?: number;
@@ -531,6 +533,22 @@ export interface WasmEngineClip {
531
533
  pageProvider?: number | { readonly id: number };
532
534
  }
533
535
 
536
+ export interface WasmEngineTrackSend {
537
+ busId: number;
538
+ levelDb?: number;
539
+ enabled?: boolean;
540
+ }
541
+
542
+ export interface WasmEngineTrackLane {
543
+ trackId: number;
544
+ sends?: WasmEngineTrackSend[];
545
+ }
546
+
547
+ export interface WasmEngineBus {
548
+ busId: number;
549
+ gainDb?: number;
550
+ }
551
+
534
552
  export interface WasmClipPageRequest {
535
553
  clipId: number;
536
554
  channel: number;
@@ -696,6 +714,18 @@ export interface WasmEngineProcessWithMonitorResult {
696
714
  monitor: Float32Array[];
697
715
  }
698
716
 
717
+ export interface WasmEngineTempoSegment {
718
+ startPpq: number;
719
+ bpm: number;
720
+ endBpm?: number;
721
+ }
722
+
723
+ export interface WasmEngineTimeSignatureSegment {
724
+ startPpq: number;
725
+ numerator: number;
726
+ denominator: number;
727
+ }
728
+
699
729
  export interface WasmRealtimeEngine {
700
730
  prepare: (
701
731
  sampleRate: number,
@@ -705,13 +735,17 @@ export interface WasmRealtimeEngine {
705
735
  ) => void;
706
736
  setParameter: (paramId: number, value: number, renderFrame: number) => void;
707
737
  setParameterSmoothed: (paramId: number, value: number, renderFrame: number) => void;
738
+ setSoloMute: (laneIndex: number, solo: boolean, mute: boolean, renderFrame: number) => void;
708
739
  getTransportState: () => WasmEngineTransportState;
709
740
  play: (renderFrame: number) => void;
710
741
  stop: (renderFrame: number) => void;
711
742
  seekSample: (timelineSample: number, renderFrame: number) => void;
712
743
  seekPpq: (ppq: number, renderFrame: number) => void;
713
744
  setTempo: (bpm: number) => void;
745
+ setTempoSegments: (segments: WasmEngineTempoSegment[]) => void;
714
746
  setTimeSignature: (numerator: number, denominator: number) => void;
747
+ setTimeSignatureSegments: (segments: WasmEngineTimeSignatureSegment[]) => void;
748
+ sampleAtPpq: (ppq: number) => number;
715
749
  setLoop: (startPpq: number, endPpq: number, enabled: boolean) => void;
716
750
  addParameter: (info: WasmEngineParameterInfo) => void;
717
751
  parameterCount: () => number;
@@ -733,6 +767,24 @@ export interface WasmRealtimeEngine {
733
767
  graphConnectionCount: () => number;
734
768
  setClips: (clips: WasmEngineClip[]) => void;
735
769
  clipCount: () => number;
770
+ setTrackLanes: (lanes: Array<number | WasmEngineTrackLane>) => void;
771
+ setTrackBuses: (buses: WasmEngineBus[]) => void;
772
+ setBusStripJson: (busId: number, sceneJson: string) => void;
773
+ setTrackStripJson: (trackId: number, sceneJson: string) => void;
774
+ setTrackStripEqBandJson: (trackId: number, bandIndex: number, bandJson: string) => void;
775
+ setTrackStripInsertBypassed: (
776
+ trackId: number,
777
+ insertIndex: number,
778
+ bypassed: boolean,
779
+ resetOnBypass: boolean,
780
+ ) => void;
781
+ setMasterStripJson: (sceneJson: string) => void;
782
+ setMasterStripEqBandJson: (bandIndex: number, bandJson: string) => void;
783
+ setMasterStripInsertBypassed: (
784
+ insertIndex: number,
785
+ bypassed: boolean,
786
+ resetOnBypass: boolean,
787
+ ) => void;
736
788
  createClipPageProvider: (numChannels: number, numSamples: number, pageFrames: number) => number;
737
789
  supplyClipPage: (providerId: number, pageIndex: number, channels: Float32Array[]) => void;
738
790
  clearClipPage: (providerId: number, pageIndex: number) => void;
@@ -747,7 +799,11 @@ export interface WasmRealtimeEngine {
747
799
  resetCapture: () => void;
748
800
  captureStatus: () => WasmEngineCaptureStatus;
749
801
  capturedAudio: () => Float32Array[];
802
+ setMidiClips: (clips: readonly object[]) => void;
750
803
  setBuiltinInstrument: (destinationId: number, config: object) => void;
804
+ setSynthInstrument: (destinationId: number, patch: object | string) => void;
805
+ loadSoundFont: (data: Uint8Array) => void;
806
+ setSf2Instrument: (destinationId: number, config: object) => void;
751
807
  clearMidiInstrument: (destinationId: number) => void;
752
808
  midiInstrumentCount: () => number;
753
809
  bindMidiCc: (
@@ -759,6 +815,8 @@ export interface WasmRealtimeEngine {
759
815
  ) => void;
760
816
  clearMidiCcBindings: () => void;
761
817
  midiCcBindingCount: () => number;
818
+ setMidiFx: (destinationId: number, configJson: string) => void;
819
+ clearMidiFx: (destinationId: number) => void;
762
820
  setMidiInputSource: (destinationId: number) => void;
763
821
  clearMidiInputSource: () => void;
764
822
  midiInputPendingCount: () => number;
@@ -808,6 +866,7 @@ export interface WasmRealtimeEngine {
808
866
  renderFrame: number,
809
867
  ) => void;
810
868
  pushMidiPanic: (renderFrame: number) => void;
869
+ clearParameters: () => void;
811
870
  process: (channels: Float32Array[]) => Float32Array[];
812
871
  prepareChannels: (numChannels: number, maxFrames: number) => void;
813
872
  getChannelBuffer: (channel: number, numFrames: number) => Float32Array;
@@ -1845,6 +1904,11 @@ export interface SonareModule {
1845
1904
 
1846
1905
  // Mixing - scene-based Mixer
1847
1906
  createMixerFromSceneJson: (json: string, sampleRate: number, blockSize: number) => WasmMixer;
1907
+
1908
+ // Decodes a thrown native exception-object pointer (emscripten classic EH
1909
+ // surfaces a C++ throw as the raw pointer number) into a structured error.
1910
+ // Consumed by the module-error wrapper in module_state.ts.
1911
+ sonareExceptionInfo: (ptr: number) => { code: number; codeName: string; message: string };
1848
1912
  }
1849
1913
 
1850
1914
  export interface WasmStreamingMasteringChain {
@@ -1947,6 +2011,7 @@ export interface WasmMixer {
1947
2011
  outputRightView: () => Float32Array;
1948
2012
  processPreparedStereo: (numSamples: number) => void;
1949
2013
  stripCount: () => number;
2014
+ sceneWarnings: () => string[];
1950
2015
  scheduleInsertAutomation: (
1951
2016
  stripIndex: number,
1952
2017
  insertIndex: number,
package/src/web_midi.ts CHANGED
@@ -93,6 +93,7 @@ export interface WebMidiBinding {
93
93
  type BoundInput = {
94
94
  input: MidiInputLike;
95
95
  listener: (event: MidiMessageEventLike) => void;
96
+ runningStatus: number;
96
97
  };
97
98
 
98
99
  export function isWebMidiAvailable(): boolean {
@@ -128,7 +129,6 @@ export async function bindWebMidi(
128
129
 
129
130
  const bound = new Map<string, BoundInput>();
130
131
  let closed = false;
131
- let runningStatus = 0;
132
132
 
133
133
  const shouldBind = (input: MidiInputLike) =>
134
134
  input.state !== 'disconnected' && (selectedIds.size === 0 || selectedIds.has(input.id));
@@ -147,22 +147,26 @@ export async function bindWebMidi(
147
147
  if (bound.has(input.id) || !shouldBind(input)) {
148
148
  return;
149
149
  }
150
- const listener = (event: MidiMessageEventLike) => {
151
- const status = dispatchMidiMessage(
152
- engine,
153
- event,
154
- group,
155
- runningStatus,
156
- options.timestampToSamples,
157
- );
158
- runningStatus = status;
150
+ const entry: BoundInput = {
151
+ input,
152
+ listener: (event: MidiMessageEventLike) => {
153
+ entry.runningStatus = dispatchMidiMessage(
154
+ engine,
155
+ event,
156
+ group,
157
+ entry.runningStatus,
158
+ options.timestampToSamples,
159
+ );
160
+ },
161
+ runningStatus: 0,
159
162
  };
163
+ const listener = entry.listener;
160
164
  if (input.addEventListener) {
161
165
  input.addEventListener('midimessage', listener);
162
166
  } else {
163
167
  input.onmidimessage = listener;
164
168
  }
165
- bound.set(input.id, { input, listener });
169
+ bound.set(input.id, entry);
166
170
  };
167
171
 
168
172
  const unbindInput = (input: MidiInputLike) => {