@libraz/libsonare 1.3.2 → 1.4.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.
@@ -367,6 +367,40 @@ export interface NoteStretchOptions {
367
367
  stretchRatio?: number;
368
368
  }
369
369
 
370
+ /** How a `spectralEdit` region op modifies the masked bins. */
371
+ export type SpectralEditMode = 'gain' | 'attenuate' | 'mute' | 'heal';
372
+
373
+ /** Analysis/synthesis window used by `spectralEdit`. */
374
+ export type SpectralEditWindow = 'hann' | 'hamming' | 'blackman' | 'rectangular';
375
+
376
+ /** One time x frequency rectangle edit op for `spectralEdit`. */
377
+ export interface SpectralRegionOp {
378
+ /** Region time start (input samples); clamped to [0, length]. Default 0. */
379
+ startSample?: number;
380
+ /** Region time end, exclusive (input samples); clamped to [0, length]. Default = signal length. */
381
+ endSample?: number;
382
+ /** Region frequency low edge in Hz; clamped to [0, nyquist]. Default 0. */
383
+ lowHz?: number;
384
+ /** Region frequency high edge in Hz; <=0 or >= nyquist means nyquist. Default 0. */
385
+ highHz?: number;
386
+ /** Linear gain in dB for 'gain'/'attenuate'; ignored by 'mute'/'heal'. Default 0. */
387
+ gainDb?: number;
388
+ /** Edit mode. Default 'gain'. */
389
+ mode?: SpectralEditMode;
390
+ }
391
+
392
+ /** STFT + heal parameters for `spectralEdit`. All fields are optional. */
393
+ export interface SpectralEditOptions {
394
+ /** FFT size; must be a power of two (>= 2). Default 2048. */
395
+ nFft?: number;
396
+ /** Hop length; must satisfy 0 < hop <= nFft/2. Default 512. */
397
+ hopLength?: number;
398
+ /** Analysis + synthesis window. Default 'hann'. */
399
+ window?: SpectralEditWindow;
400
+ /** Neighbour frames each side used by 'heal' (>= 1). Default 2. */
401
+ healRadiusFrames?: number;
402
+ }
403
+
370
404
  /**
371
405
  * Detected beat
372
406
  */
@@ -655,6 +689,24 @@ export type MasteringProcessorParams = Record<string, number | boolean>;
655
689
 
656
690
  export type PanMode = 'balance' | 'stereoPan' | 'stereo-pan' | 'dualPan' | 'dual-pan' | number;
657
691
 
692
+ /**
693
+ * Surround pan position for a strip feeding a >2-channel bus. Phase 1 honors
694
+ * `azimuth`/`divergence`/`lfe`; `elevation`/`distance` are reserved. All fields
695
+ * are optional and default to a centered point source.
696
+ */
697
+ export interface SurroundPan {
698
+ /** -180..180 deg, 0 = front-center, positive = right. */
699
+ azimuth?: number;
700
+ /** Reserved (no height beds in phase 1). */
701
+ elevation?: number;
702
+ /** 0 = point source, 1 = spread across the front. */
703
+ divergence?: number;
704
+ /** 0..1 scalar send into the LFE plane. */
705
+ lfe?: number;
706
+ /** Reserved (focus/spread), defaults to 1. */
707
+ distance?: number;
708
+ }
709
+
658
710
  export interface MixOptions {
659
711
  inputTrimDb?: number | number[];
660
712
  faderDb?: number | number[];
@@ -1,5 +1,8 @@
1
+ import { panLawCode, panModeCode, sendTimingCode } from './codes';
2
+ import { ErrorCode, SonareError } from './errors';
1
3
  import { getSonareModule } from './module_state';
2
4
  import type { SynthPatch } from './project';
5
+ import type { EqBand, PanLaw, PanMode, SendTiming } from './public_types';
3
6
  import type {
4
7
  WasmClipPageRequest,
5
8
  WasmEngineAutomationPoint,
@@ -12,10 +15,14 @@ import type {
12
15
  WasmEngineGraphSpec,
13
16
  WasmEngineMarker,
14
17
  WasmEngineMeterTelemetry,
18
+ WasmEngineMeterTelemetryWide,
15
19
  WasmEngineMetronomeConfig,
16
20
  WasmEngineParameterInfo,
17
21
  WasmEngineProcessWithMonitorResult,
22
+ WasmEngineScopeTelemetry,
18
23
  WasmEngineTelemetry,
24
+ WasmEngineTempoSegment,
25
+ WasmEngineTimeSignatureSegment,
19
26
  WasmEngineTransportState,
20
27
  WasmRealtimeEngine,
21
28
  } from './sonare.js';
@@ -34,7 +41,75 @@ export type EngineFreezeOptions = WasmEngineFreezeOptions;
34
41
  export type EngineFreezeResult = WasmEngineFreezeResult;
35
42
  export type EngineTelemetry = WasmEngineTelemetry;
36
43
  export type EngineMeterTelemetry = WasmEngineMeterTelemetry;
44
+ export type EngineMeterTelemetryWide = WasmEngineMeterTelemetryWide;
45
+ export type EngineScopeTelemetry = WasmEngineScopeTelemetry;
37
46
  export type EngineTransportState = WasmEngineTransportState;
47
+ export type EngineTempoSegment = WasmEngineTempoSegment;
48
+ export type EngineTimeSignatureSegment = WasmEngineTimeSignatureSegment;
49
+
50
+ export interface EngineTrackSend {
51
+ busId: number;
52
+ levelDb?: number;
53
+ enabled?: boolean;
54
+ /**
55
+ * Pre/post-fader tap point. Defaults to post-fader when omitted, matching the
56
+ * historical lane-send behavior and the scene-JSON default.
57
+ */
58
+ sendTiming?: SendTiming | number;
59
+ }
60
+
61
+ export interface EngineTrackLane {
62
+ trackId: number;
63
+ sends?: EngineTrackSend[];
64
+ /**
65
+ * Bus the lane's post-fader output sums into instead of the master mix
66
+ * (group/folder routing); 0 or absent keeps the lane on the master mix.
67
+ */
68
+ outputBusId?: number;
69
+ /**
70
+ * Input channel layout of the source feeding this lane (`SonareChannelLayout`:
71
+ * 0 mono, 1 stereo, 2 5.1, 3 7.1). Absent defaults to stereo. Stored but inert
72
+ * until the surround DSP path lands.
73
+ */
74
+ sourceChannelLayout?: number;
75
+ }
76
+
77
+ export interface EngineBus {
78
+ busId: number;
79
+ gainDb?: number;
80
+ /**
81
+ * Channel layout of the bus (`SonareChannelLayout`: 0 mono, 1 stereo, 2 5.1,
82
+ * 3 7.1). A surround layout makes this a surround group bus: lanes routed to
83
+ * it are surround-panned and it sums into the master plane-by-plane. Defaults
84
+ * to stereo.
85
+ */
86
+ channelLayout?: number;
87
+ }
88
+
89
+ export interface EngineMidiEvent {
90
+ renderFrame: number;
91
+ word0?: number;
92
+ word1?: number;
93
+ word2?: number;
94
+ word3?: number;
95
+ wordCount?: number;
96
+ group?: number;
97
+ sysexHandle?: number;
98
+ data0?: number;
99
+ data1?: number;
100
+ }
101
+
102
+ export interface EngineMidiClipSchedule {
103
+ id?: number;
104
+ trackId?: number;
105
+ destinationId?: number;
106
+ startSample?: number;
107
+ startPpq?: number;
108
+ lengthSamples?: number;
109
+ loop?: boolean;
110
+ loopLengthSamples?: number;
111
+ events: EngineMidiEvent[];
112
+ }
38
113
 
39
114
  export const EXPECTED_ENGINE_ABI_VERSION = 3;
40
115
 
@@ -75,91 +150,9 @@ export function engineCapabilities(): EngineCapabilities {
75
150
  };
76
151
  }
77
152
 
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
153
  export class RealtimeEngine {
157
154
  private native: WasmRealtimeEngine;
158
155
 
159
- private nativeExt(): WasmRealtimeEngineExt {
160
- return this.native as unknown as WasmRealtimeEngineExt;
161
- }
162
-
163
156
  constructor(
164
157
  sampleRate = 48000,
165
158
  maxBlockSize = 128,
@@ -200,11 +193,19 @@ export class RealtimeEngine {
200
193
  this.native.setParameterSmoothed(paramId, value, renderFrame);
201
194
  }
202
195
 
196
+ setSoloMute(laneIndex: number, solo: boolean, mute: boolean, renderFrame = -1): void {
197
+ this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
198
+ }
199
+
200
+ setMidiClips(clips: readonly EngineMidiClipSchedule[]): void {
201
+ this.native.setMidiClips(clips);
202
+ }
203
+
203
204
  setBuiltinInstrument(
204
205
  config: { destinationId?: number } & Record<string, unknown> = {},
205
206
  destinationId = config.destinationId ?? 0,
206
207
  ): void {
207
- this.nativeExt().setBuiltinInstrument(destinationId, config);
208
+ this.native.setBuiltinInstrument(destinationId, config);
208
209
  }
209
210
 
210
211
  /**
@@ -220,7 +221,7 @@ export class RealtimeEngine {
220
221
  patch: SynthPatch | string = {},
221
222
  destinationId = (typeof patch === 'object' ? patch.destinationId : undefined) ?? 0,
222
223
  ): void {
223
- this.nativeExt().setSynthInstrument(destinationId, patch);
224
+ this.native.setSynthInstrument(destinationId, patch);
224
225
  }
225
226
 
226
227
  /**
@@ -230,7 +231,7 @@ export class RealtimeEngine {
230
231
  * not referenced afterwards. Replaces any previously loaded SoundFont.
231
232
  */
232
233
  loadSoundFont(data: Uint8Array): void {
233
- this.nativeExt().loadSoundFont(data);
234
+ this.native.loadSoundFont(data);
234
235
  }
235
236
 
236
237
  /**
@@ -246,15 +247,15 @@ export class RealtimeEngine {
246
247
  config: { destinationId?: number; gain?: number; polyphony?: number } = {},
247
248
  destinationId = config.destinationId ?? 0,
248
249
  ): void {
249
- this.nativeExt().setSf2Instrument(destinationId, config);
250
+ this.native.setSf2Instrument(destinationId, config);
250
251
  }
251
252
 
252
253
  clearMidiInstrument(destinationId = 0): void {
253
- this.nativeExt().clearMidiInstrument(destinationId);
254
+ this.native.clearMidiInstrument(destinationId);
254
255
  }
255
256
 
256
257
  midiInstrumentCount(): number {
257
- return this.nativeExt().midiInstrumentCount();
258
+ return this.native.midiInstrumentCount();
258
259
  }
259
260
 
260
261
  /**
@@ -268,7 +269,7 @@ export class RealtimeEngine {
268
269
  paramId: number,
269
270
  options: MidiCcBindOptions = {},
270
271
  ): void {
271
- this.nativeExt().bindMidiCc(
272
+ this.native.bindMidiCc(
272
273
  channel,
273
274
  controller,
274
275
  paramId,
@@ -278,33 +279,33 @@ export class RealtimeEngine {
278
279
  }
279
280
 
280
281
  clearMidiCcBindings(): void {
281
- this.nativeExt().clearMidiCcBindings();
282
+ this.native.clearMidiCcBindings();
282
283
  }
283
284
 
284
285
  midiCcBindingCount(): number {
285
- return this.nativeExt().midiCcBindingCount();
286
+ return this.native.midiCcBindingCount();
286
287
  }
287
288
 
288
289
  /** Install/replace a live non-destructive MIDI-FX insert for one destination. */
289
290
  setMidiFx(destinationId: number, configJson: string): void {
290
- this.nativeExt().setMidiFx(destinationId, configJson);
291
+ this.native.setMidiFx(destinationId, configJson);
291
292
  }
292
293
 
293
294
  clearMidiFx(destinationId = 0): void {
294
- this.nativeExt().clearMidiFx(destinationId);
295
+ this.native.clearMidiFx(destinationId);
295
296
  }
296
297
 
297
298
  /** Enable the engine-owned live MIDI input source for a destination. */
298
299
  setMidiInputSource(destinationId = 0): void {
299
- this.nativeExt().setMidiInputSource(destinationId);
300
+ this.native.setMidiInputSource(destinationId);
300
301
  }
301
302
 
302
303
  clearMidiInputSource(): void {
303
- this.nativeExt().clearMidiInputSource();
304
+ this.native.clearMidiInputSource();
304
305
  }
305
306
 
306
307
  midiInputPendingCount(): number {
307
- return this.nativeExt().midiInputPendingCount();
308
+ return this.native.midiInputPendingCount();
308
309
  }
309
310
 
310
311
  pushMidiInputNoteOn(
@@ -314,7 +315,7 @@ export class RealtimeEngine {
314
315
  velocity: number,
315
316
  portTimeSamples = 0,
316
317
  ): void {
317
- this.nativeExt().pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
318
+ this.native.pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);
318
319
  }
319
320
 
320
321
  pushMidiInputNoteOff(
@@ -324,7 +325,7 @@ export class RealtimeEngine {
324
325
  velocity = 0,
325
326
  portTimeSamples = 0,
326
327
  ): void {
327
- this.nativeExt().pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
328
+ this.native.pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);
328
329
  }
329
330
 
330
331
  pushMidiInputCc(
@@ -334,7 +335,7 @@ export class RealtimeEngine {
334
335
  value: number,
335
336
  portTimeSamples = 0,
336
337
  ): void {
337
- this.nativeExt().pushMidiInputCc(group, channel, controller, value, portTimeSamples);
338
+ this.native.pushMidiInputCc(group, channel, controller, value, portTimeSamples);
338
339
  }
339
340
 
340
341
  pushMidiNoteOn(
@@ -345,7 +346,7 @@ export class RealtimeEngine {
345
346
  velocity: number,
346
347
  renderFrame = -1,
347
348
  ): void {
348
- this.nativeExt().pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
349
+ this.native.pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);
349
350
  }
350
351
 
351
352
  pushMidiNoteOff(
@@ -356,7 +357,7 @@ export class RealtimeEngine {
356
357
  velocity = 0,
357
358
  renderFrame = -1,
358
359
  ): void {
359
- this.nativeExt().pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
360
+ this.native.pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);
360
361
  }
361
362
 
362
363
  /**
@@ -373,7 +374,7 @@ export class RealtimeEngine {
373
374
  value: number,
374
375
  renderFrame = -1,
375
376
  ): void {
376
- this.nativeExt().pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
377
+ this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
377
378
  }
378
379
 
379
380
  /**
@@ -381,7 +382,7 @@ export class RealtimeEngine {
381
382
  * `renderFrame` (-1 = immediate). Mirrors the C-ABI `pushMidiPanic`.
382
383
  */
383
384
  pushMidiPanic(renderFrame = -1): void {
384
- this.nativeExt().pushMidiPanic(renderFrame);
385
+ this.native.pushMidiPanic(renderFrame);
385
386
  }
386
387
 
387
388
  /**
@@ -389,7 +390,7 @@ export class RealtimeEngine {
389
390
  * only; not realtime-safe. Mirrors the C-ABI `clearParameters`.
390
391
  */
391
392
  clearParameters(): void {
392
- this.nativeExt().clearParameters();
393
+ this.native.clearParameters();
393
394
  }
394
395
 
395
396
  /** Read back the current transport state snapshot. */
@@ -409,6 +410,16 @@ export class RealtimeEngine {
409
410
  this.native.seekSample(timelineSample, renderFrame);
410
411
  }
411
412
 
413
+ /**
414
+ * Snaps every in-flight parameter ramp (engine-level smoothed params, mixer
415
+ * lane fader/pan/gate, bus gains) to its target value. Offline renders call
416
+ * this after a priming process() block so the first audible block renders at
417
+ * settled values instead of ramping in from defaults.
418
+ */
419
+ settleParameters(): void {
420
+ this.native.settleParameters();
421
+ }
422
+
412
423
  seekPpq(ppq: number, renderFrame = -1): void {
413
424
  this.native.seekPpq(ppq, renderFrame);
414
425
  }
@@ -417,10 +428,22 @@ export class RealtimeEngine {
417
428
  this.native.setTempo(bpm);
418
429
  }
419
430
 
431
+ setTempoSegments(segments: readonly EngineTempoSegment[]): void {
432
+ this.native.setTempoSegments([...segments]);
433
+ }
434
+
420
435
  setTimeSignature(numerator: number, denominator: number): void {
421
436
  this.native.setTimeSignature(numerator, denominator);
422
437
  }
423
438
 
439
+ setTimeSignatureSegments(segments: readonly EngineTimeSignatureSegment[]): void {
440
+ this.native.setTimeSignatureSegments([...segments]);
441
+ }
442
+
443
+ sampleAtPpq(ppq: number): number {
444
+ return Number(this.native.sampleAtPpq(ppq));
445
+ }
446
+
424
447
  setLoop(startPpq: number, endPpq: number, enabled = true): void {
425
448
  this.native.setLoop(startPpq, endPpq, enabled);
426
449
  }
@@ -513,29 +536,183 @@ export class RealtimeEngine {
513
536
  return this.native.clipCount();
514
537
  }
515
538
 
539
+ setTrackLanes(lanes: Array<number | EngineTrackLane>): void {
540
+ this.native.setTrackLanes(
541
+ lanes.map((lane) => {
542
+ if (typeof lane === 'number') {
543
+ return { trackId: lane };
544
+ }
545
+ if (!lane.sends) {
546
+ return lane;
547
+ }
548
+ // Normalize each send's pre/post tap point to the integer the native
549
+ // layer reads (defaults to post-fader when omitted).
550
+ return {
551
+ ...lane,
552
+ sends: lane.sends.map((send) => ({
553
+ ...send,
554
+ // Post-fader (0) is the default for an omitted sendTiming.
555
+ sendTiming: send.sendTiming === undefined ? 0 : sendTimingCode(send.sendTiming),
556
+ })),
557
+ };
558
+ }),
559
+ );
560
+ }
561
+
562
+ /**
563
+ * Keys one insert of a lane strip from another lane's post-strip audio
564
+ * (ducking/sidechainRouter inserts). sourceTrackId 0 removes the binding.
565
+ */
566
+ setLaneSidechain(trackId: number, insertIndex: number, sourceTrackId: number): void {
567
+ this.native.setLaneSidechain(trackId, insertIndex, sourceTrackId);
568
+ }
569
+
570
+ setTrackBuses(buses: EngineBus[]): void {
571
+ this.native.setTrackBuses(buses);
572
+ }
573
+
574
+ setBusStripJson(busId: number, sceneJson: string): void {
575
+ try {
576
+ JSON.parse(sceneJson);
577
+ } catch (error) {
578
+ const message = error instanceof Error ? error.message : 'invalid bus strip JSON';
579
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
580
+ }
581
+ this.native.setBusStripJson(busId, sceneJson);
582
+ }
583
+
584
+ setTrackStripJson(trackId: number, sceneJson: string): void {
585
+ try {
586
+ JSON.parse(sceneJson);
587
+ } catch (error) {
588
+ const message = error instanceof Error ? error.message : 'invalid track strip JSON';
589
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
590
+ }
591
+ this.native.setTrackStripJson(trackId, sceneJson);
592
+ }
593
+
594
+ setTrackStripEqBand(trackId: number, bandIndex: number, band: EqBand | string): void {
595
+ this.native.setTrackStripEqBandJson(
596
+ trackId,
597
+ bandIndex,
598
+ typeof band === 'string' ? band : JSON.stringify(band),
599
+ );
600
+ }
601
+
602
+ setTrackStripEqBandJson(trackId: number, bandIndex: number, bandJson: string): void {
603
+ this.native.setTrackStripEqBandJson(trackId, bandIndex, bandJson);
604
+ }
605
+
606
+ setTrackStripInsertBypassed(
607
+ trackId: number,
608
+ insertIndex: number,
609
+ bypassed: boolean,
610
+ resetOnBypass = false,
611
+ ): void {
612
+ this.native.setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass);
613
+ }
614
+
615
+ setMasterStripJson(sceneJson: string): void {
616
+ try {
617
+ JSON.parse(sceneJson);
618
+ } catch (error) {
619
+ const message = error instanceof Error ? error.message : 'invalid master strip JSON';
620
+ throw new SonareError(ErrorCode.InvalidFormat, 'InvalidFormat', message);
621
+ }
622
+ this.native.setMasterStripJson(sceneJson);
623
+ }
624
+
625
+ setMasterStripEqBand(bandIndex: number, band: EqBand | string): void {
626
+ this.native.setMasterStripEqBandJson(
627
+ bandIndex,
628
+ typeof band === 'string' ? band : JSON.stringify(band),
629
+ );
630
+ }
631
+
632
+ setMasterStripEqBandJson(bandIndex: number, bandJson: string): void {
633
+ this.native.setMasterStripEqBandJson(bandIndex, bandJson);
634
+ }
635
+
636
+ setMasterStripInsertBypassed(
637
+ insertIndex: number,
638
+ bypassed: boolean,
639
+ resetOnBypass = false,
640
+ ): void {
641
+ this.native.setMasterStripInsertBypassed(insertIndex, bypassed, resetOnBypass);
642
+ }
643
+
644
+ /**
645
+ * Changes one track-strip insert parameter in realtime, addressed by the
646
+ * processor's JSON-key parameter name (see {@link masteringInsertParamInfo}).
647
+ * Applied at the next block head via the engine command queue; safe during
648
+ * playback. Throws if the track, insert, or name is unknown, the param is not
649
+ * realtime-safe, or the command queue is full.
650
+ */
651
+ setTrackStripInsertParamByName(
652
+ trackId: number,
653
+ insertIndex: number,
654
+ paramName: string,
655
+ value: number,
656
+ ): void {
657
+ this.native.setTrackStripInsertParamByName(trackId, insertIndex, paramName, value);
658
+ }
659
+
660
+ /** Master-strip counterpart of {@link setTrackStripInsertParamByName}. */
661
+ setMasterStripInsertParamByName(insertIndex: number, paramName: string, value: number): void {
662
+ this.native.setMasterStripInsertParamByName(insertIndex, paramName, value);
663
+ }
664
+
665
+ /** Sets a track lane strip's pan position in realtime (glitch-free). */
666
+ setTrackStripPan(trackId: number, pan: number): void {
667
+ this.native.setTrackStripPan(trackId, pan);
668
+ }
669
+
670
+ /** Sets a track lane strip's pan law in realtime. */
671
+ setTrackStripPanLaw(trackId: number, panLaw: PanLaw | number): void {
672
+ this.native.setTrackStripPanLaw(trackId, panLawCode(panLaw));
673
+ }
674
+
675
+ /** Sets a track lane strip's pan mode in realtime. */
676
+ setTrackStripPanMode(trackId: number, panMode: PanMode | number): void {
677
+ this.native.setTrackStripPanMode(trackId, panModeCode(panMode));
678
+ }
679
+
680
+ /** Sets a track lane strip's dual-pan left/right positions in realtime. */
681
+ setTrackStripDualPan(trackId: number, leftPan: number, rightPan: number): void {
682
+ this.native.setTrackStripDualPan(trackId, leftPan, rightPan);
683
+ }
684
+
685
+ /**
686
+ * Sets a track lane strip's inter-channel alignment delay (whole samples).
687
+ * Adjusts strip latency, so PDC and reported graph latency are refreshed.
688
+ */
689
+ setTrackStripChannelDelaySamples(trackId: number, delaySamples: number): void {
690
+ this.native.setTrackStripChannelDelaySamples(trackId, delaySamples);
691
+ }
692
+
516
693
  createClipPageProvider(
517
694
  numChannels: number,
518
695
  numSamples: number,
519
696
  pageFrames: number,
520
697
  ): ClipPageProvider {
521
- const id = this.nativeExt().createClipPageProvider(numChannels, numSamples, pageFrames);
698
+ const id = this.native.createClipPageProvider(numChannels, numSamples, pageFrames);
522
699
  return new ClipPageProvider(this, id);
523
700
  }
524
701
 
525
702
  supplyClipPage(providerId: number, pageIndex: number, channels: Float32Array[]): void {
526
- this.nativeExt().supplyClipPage(providerId, pageIndex, channels);
703
+ this.native.supplyClipPage(providerId, pageIndex, channels);
527
704
  }
528
705
 
529
706
  clearClipPage(providerId: number, pageIndex: number): void {
530
- this.nativeExt().clearClipPage(providerId, pageIndex);
707
+ this.native.clearClipPage(providerId, pageIndex);
531
708
  }
532
709
 
533
710
  destroyClipPageProvider(providerId: number): void {
534
- this.nativeExt().destroyClipPageProvider(providerId);
711
+ this.native.destroyClipPageProvider(providerId);
535
712
  }
536
713
 
537
714
  popClipPageRequest(): ClipPageRequest | null {
538
- return this.nativeExt().popClipPageRequest();
715
+ return this.native.popClipPageRequest();
539
716
  }
540
717
 
541
718
  setCaptureBuffer(numChannels: number, capacityFrames: number): void {
@@ -630,6 +807,33 @@ export class RealtimeEngine {
630
807
  return this.native.drainMeterTelemetry(maxRecords);
631
808
  }
632
809
 
810
+ /**
811
+ * Drains pending meter telemetry as per-plane (wide) records for a surround
812
+ * target. Use this for a surround mix target; {@link drainMeterTelemetry}
813
+ * stays the stereo fast path. The two share one queue — call only one per
814
+ * target. The live AudioWorklet path owns the queue via the stereo drain, so
815
+ * this wide drain is for an offline (non-worklet) engine instance; per-plane
816
+ * surround meters are not delivered over the live worklet meter ring.
817
+ */
818
+ drainMeterTelemetryWide(maxRecords = 1024): EngineMeterTelemetryWide[] {
819
+ return this.native.drainMeterTelemetryWide(maxRecords);
820
+ }
821
+
822
+ /**
823
+ * Enables per-target spectrum + vectorscope capture. @param intervalFrames is
824
+ * the minimum render-frame gap between snapshots (0 disables). @param bandCount
825
+ * is the FFT band resolution (1..64); changing it re-prepares the tap. Returns
826
+ * the band count actually applied.
827
+ */
828
+ configureScopeTelemetry(intervalFrames: number, bandCount: number): number {
829
+ return this.native.configureScopeTelemetry(intervalFrames, bandCount);
830
+ }
831
+
832
+ /** Drains pending spectrum + vectorscope snapshots (per mix target). */
833
+ drainScopeTelemetry(maxRecords = 1024): EngineScopeTelemetry[] {
834
+ return this.native.drainScopeTelemetry(maxRecords);
835
+ }
836
+
633
837
  destroy(): void {
634
838
  this.native.delete();
635
839
  }