@libraz/libsonare 1.5.5 → 1.7.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.
Files changed (80) hide show
  1. package/README.md +109 -20
  2. package/dist/analysis.d.ts +5809 -0
  3. package/dist/analysis.js +2574 -0
  4. package/dist/analysis.js.map +1 -0
  5. package/dist/index.d.ts +884 -55
  6. package/dist/index.js +1302 -292
  7. package/dist/index.js.map +1 -1
  8. package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
  9. package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
  10. package/dist/sonare-analysis.js +2 -0
  11. package/dist/sonare-analysis.wasm +0 -0
  12. package/dist/sonare.js +1 -1
  13. package/dist/sonare.wasm +0 -0
  14. package/dist/worker.d.ts +65 -0
  15. package/dist/worker.js +734 -0
  16. package/dist/worker.js.map +1 -0
  17. package/dist/worklet.d.ts +628 -88
  18. package/dist/worklet.js +1816 -266
  19. package/dist/worklet.js.map +1 -1
  20. package/package.json +23 -11
  21. package/src/_chain_config.ts +5 -3
  22. package/src/analysis.ts +137 -0
  23. package/src/analysis_helpers.ts +40 -29
  24. package/src/audio.ts +7 -2
  25. package/src/clip_page_streamer.ts +62 -8
  26. package/src/codes.ts +87 -64
  27. package/src/effects_mastering.ts +1 -0
  28. package/src/effects_transform.ts +179 -10
  29. package/src/errors.ts +1 -0
  30. package/src/feature_core.ts +140 -2
  31. package/src/feature_music.ts +25 -3
  32. package/src/feature_pitch.ts +70 -1
  33. package/src/feature_spectral.ts +295 -7
  34. package/src/feature_spectrogram.ts +203 -3
  35. package/src/features.ts +16 -1
  36. package/src/index.ts +120 -8
  37. package/src/mastering_chain.ts +20 -8
  38. package/src/mastering_core.ts +5 -0
  39. package/src/mastering_dynamics.ts +7 -1
  40. package/src/metering.ts +6 -1
  41. package/src/mixer.ts +3 -3
  42. package/src/opfs_clip_pages.ts +26 -1
  43. package/src/project.ts +13 -0
  44. package/src/project_class.ts +204 -23
  45. package/src/project_internal.ts +38 -34
  46. package/src/project_types.ts +114 -11
  47. package/src/public_types.ts +68 -0
  48. package/src/public_types_mastering.ts +22 -0
  49. package/src/public_types_mixing.ts +27 -2
  50. package/src/public_types_music.ts +9 -0
  51. package/src/public_types_realtime.ts +23 -5
  52. package/src/public_types_spectral.ts +33 -0
  53. package/src/quick_analysis.ts +46 -10
  54. package/src/realtime_engine.ts +167 -6
  55. package/src/realtime_voice_changer.ts +51 -77
  56. package/src/sonare-analysis.js.d.ts +8 -0
  57. package/src/sonare.js.d.ts +340 -6
  58. package/src/stream_analyzer.ts +6 -1
  59. package/src/streaming_processors.ts +15 -0
  60. package/src/web_midi.ts +46 -11
  61. package/src/worker.ts +196 -0
  62. package/src/worker_client.ts +352 -0
  63. package/src/worker_protocol.ts +59 -0
  64. package/src/worklet/engine-automation.ts +2 -1
  65. package/src/worklet/engine-capture-facade.ts +13 -4
  66. package/src/worklet/engine-clips.ts +12 -3
  67. package/src/worklet/engine-node.ts +264 -21
  68. package/src/worklet/engine-offline.ts +7 -2
  69. package/src/worklet/engine-parameter-facade.ts +55 -5
  70. package/src/worklet/engine-processor.ts +435 -62
  71. package/src/worklet/engine-register.ts +9 -1
  72. package/src/worklet/engine-strips.ts +19 -3
  73. package/src/worklet/engine-sync.ts +2 -6
  74. package/src/worklet/engine-tempo-facade.ts +8 -6
  75. package/src/worklet/engine.ts +390 -13
  76. package/src/worklet/guards.ts +121 -8
  77. package/src/worklet/messages.ts +166 -8
  78. package/src/worklet/protocol.ts +305 -17
  79. package/src/worklet/voice-changer-processor.ts +12 -15
  80. package/src/worklet.ts +20 -1
@@ -4,3 +4,71 @@ export * from './public_types_mixing';
4
4
  export * from './public_types_music';
5
5
  export * from './public_types_realtime';
6
6
  export * from './public_types_spectral';
7
+
8
+ /** Runtime capabilities reported by the loaded libsonare build. */
9
+ export interface SonareCapabilities {
10
+ version: string;
11
+ abi: {
12
+ project: number;
13
+ engine: number;
14
+ };
15
+ platform: string;
16
+ features: {
17
+ mastering: boolean;
18
+ mixing: boolean;
19
+ fx: boolean;
20
+ ffmpeg: boolean;
21
+ };
22
+ decode: {
23
+ builtin: string[];
24
+ ffmpeg: string[];
25
+ };
26
+ simd: string;
27
+ hardwareConcurrency: number;
28
+ }
29
+
30
+ /** One parameter descriptor in the cross-surface capability catalog. */
31
+ export interface CapabilityCatalogParameter {
32
+ name: string;
33
+ id: number;
34
+ rtSafe: boolean;
35
+ type: 'boolean' | 'number';
36
+ min: number | null;
37
+ max: number | null;
38
+ default: boolean | number | null;
39
+ unit: string | null;
40
+ }
41
+
42
+ /** One named mastering processor and its host-facing capabilities. */
43
+ export interface CapabilityCatalogProcessor {
44
+ id: string;
45
+ kind: 'realtime' | 'offline' | 'pair';
46
+ realtimeInsertable: boolean;
47
+ stereoOnly: boolean;
48
+ latencySamples: number;
49
+ tailSamples: number;
50
+ /** Coarse realtime work estimate, or null when the processor is not an insert. */
51
+ realtimeCost: 'low' | 'moderate' | 'high' | null;
52
+ channelPolicy: 'multichannel' | 'stereoPairOnly' | 'perChannel' | 'passthrough';
53
+ category: string;
54
+ params: CapabilityCatalogParameter[];
55
+ }
56
+
57
+ /** Built-in preset names grouped by feature family. */
58
+ export interface CapabilityCatalogPresets {
59
+ mastering: string[];
60
+ synth: string[];
61
+ mixingScene: string[];
62
+ voiceChanger: string[];
63
+ }
64
+
65
+ /** Complete runtime catalog exposed by {@link capabilityCatalog}. */
66
+ export interface CapabilityCatalog {
67
+ version: string;
68
+ abi: SonareCapabilities['abi'];
69
+ processors: CapabilityCatalogProcessor[];
70
+ presets: CapabilityCatalogPresets;
71
+ }
72
+
73
+ /** Synchronous progress callback for offline operations. Its return value is ignored. */
74
+ export type ProgressCallback = (progress: number, stage: string) => void;
@@ -394,6 +394,26 @@ export interface StageGainReduction {
394
394
  gainReductionDb: number;
395
395
  }
396
396
 
397
+ /** Existing EBU R128 measurements captured before or after mastering. */
398
+ export interface MasteringLoudnessSummary {
399
+ integratedLufs: number;
400
+ maxMomentaryLufs: number;
401
+ maxShortTermLufs: number;
402
+ truePeakDbtp: number;
403
+ loudnessRange: number;
404
+ }
405
+
406
+ /** Compact explanation of how an offline mastering chain changed a program. */
407
+ export interface MasteringReport {
408
+ before: MasteringLoudnessSummary;
409
+ after: MasteringLoudnessSummary;
410
+ appliedGainDb: number;
411
+ maxGainReductionDb: number;
412
+ loudnessTargetLimited: boolean;
413
+ /** 32 logarithmically-spaced after-minus-before spectral energy deltas (dB). */
414
+ bandEnergyDeltaDb: Float32Array;
415
+ }
416
+
397
417
  export interface MasteringChainResult {
398
418
  /** Latency-compensated offline output; no separate latency field is reported. */
399
419
  samples: Float32Array;
@@ -414,6 +434,7 @@ export interface MasteringChainResult {
414
434
  loudnessTargetLimited: boolean;
415
435
  /** Per-stage gain reductions for the dynamics/maximizer stages (a subset of `stages`). */
416
436
  stageGainReductions: StageGainReduction[];
437
+ report: MasteringReport;
417
438
  }
418
439
 
419
440
  export interface MasteringChainStereoResult {
@@ -429,6 +450,7 @@ export interface MasteringChainStereoResult {
429
450
  outputLra: number;
430
451
  loudnessTargetLimited: boolean;
431
452
  stageGainReductions: StageGainReduction[];
453
+ report: MasteringReport;
432
454
  }
433
455
 
434
456
  /**
@@ -1,4 +1,11 @@
1
- export type PanMode = 'balance' | 'stereoPan' | 'stereo-pan' | 'dualPan' | 'dual-pan' | number;
1
+ export type PanMode =
2
+ | 'balance'
3
+ | 'pan'
4
+ | 'stereoPan'
5
+ | 'stereo-pan'
6
+ | 'dualPan'
7
+ | 'dual-pan'
8
+ | number;
2
9
 
3
10
  /**
4
11
  * Surround pan position for a strip feeding a >2-channel bus. Phase 1 honors
@@ -77,10 +84,28 @@ export type AutomationCurve = 'linear' | 'exponential' | 'hold' | 's-curve';
77
84
 
78
85
  /**
79
86
  * Pan law applied when computing left/right gains from a pan position
80
- * (see {@link Mixer.setPanLaw}). Maps to the underlying integer code.
87
+ * (see {@link Mixer.setPanLaw}). On mono strips it changes the centre gain;
88
+ * on stereo Balance strips it changes only the far-channel taper, while centre
89
+ * remains unity. Maps to the underlying integer code.
81
90
  */
82
91
  export type PanLaw = 'const3dB' | 'const4.5dB' | 'const6dB' | 'linear0dB';
83
92
 
93
+ /** Accepted pan-law name aliases for mixer and realtime-engine inputs. */
94
+ export type PanLawName =
95
+ | PanLaw
96
+ | 'const-3db'
97
+ | '-3db'
98
+ | 'const-4.5db'
99
+ | '-4.5db'
100
+ | 'const-6db'
101
+ | '-6db'
102
+ | 'linear-0db'
103
+ | 'linear'
104
+ | '0db';
105
+
106
+ /** Pan-law name or raw C ABI ordinal. */
107
+ export type PanLawInput = PanLawName | number;
108
+
84
109
  /**
85
110
  * Meter tap point for reading a strip's meter snapshot
86
111
  * (see {@link Mixer.meterTap} and {@link Mixer.stripMeter}).
@@ -315,6 +315,13 @@ export interface TimeSignature {
315
315
  confidence: number;
316
316
  }
317
317
 
318
+ /** Existing tempo-estimator hypothesis retained by unified analysis. */
319
+ export interface BpmHypothesis {
320
+ value: number;
321
+ confidence: number;
322
+ relation: 'primary' | 'half' | 'double' | 'other';
323
+ }
324
+
318
325
  /**
319
326
  * Rhythm features
320
327
  */
@@ -343,8 +350,10 @@ export interface MelodyContour {
343
350
  export interface AnalysisResult {
344
351
  bpm: number;
345
352
  bpmConfidence: number;
353
+ bpmCandidates: BpmHypothesis[];
346
354
  key: Key;
347
355
  timeSignature: TimeSignature;
356
+ timeSignatureCandidates: TimeSignature[];
348
357
  beatTimes: Float32Array;
349
358
  beats: Beat[];
350
359
  chords: Chord[];
@@ -105,15 +105,33 @@ export type VoicePresetId =
105
105
  | 'robot-mascot'
106
106
  | 'dark-villain';
107
107
 
108
- export interface RealtimeVoiceChangerPreset {
108
+ export type VoicePresetCategory =
109
+ | 'monitor'
110
+ | 'bright'
111
+ | 'soft'
112
+ | 'deep'
113
+ | 'robot'
114
+ | 'dark'
115
+ | 'custom';
116
+
117
+ export interface RealtimeVoiceChangerPresetMetadata {
109
118
  schemaVersion: 1;
110
- id?: string;
111
- name?: string;
119
+ id: string;
120
+ name: string;
112
121
  description?: string;
113
- macros?: Record<string, number>;
114
- dsp?: Record<string, unknown>;
122
+ category: VoicePresetCategory;
115
123
  }
116
124
 
125
+ export type RealtimeVoiceChangerPreset =
126
+ | (RealtimeVoiceChangerPresetMetadata & {
127
+ dsp: Record<string, unknown>;
128
+ macros?: never;
129
+ })
130
+ | (RealtimeVoiceChangerPresetMetadata & {
131
+ macros: Record<string, number>;
132
+ dsp?: never;
133
+ });
134
+
117
135
  export type RealtimeVoiceChangerConfigInput = VoicePresetId | RealtimeVoiceChangerPreset;
118
136
 
119
137
  /**
@@ -182,6 +182,39 @@ export interface PitchResult {
182
182
  meanF0: number;
183
183
  }
184
184
 
185
+ /** Per-bin pitch candidates and peak magnitudes from spectral piptrack. */
186
+ export interface PiptrackResult {
187
+ nBins: number;
188
+ nFrames: number;
189
+ pitches: Float32Array;
190
+ magnitudes: Float32Array;
191
+ }
192
+
193
+ /** Magnitude and reassigned time/frequency coordinates for every STFT bin. */
194
+ export interface ReassignedSpectrogramResult {
195
+ nBins: number;
196
+ nFrames: number;
197
+ magnitude: Float32Array;
198
+ times: Float32Array;
199
+ frequencies: Float32Array;
200
+ }
201
+
202
+ /** Row-major matrix returned by librosa.segment-compatible APIs. */
203
+ export interface SegmentMatrix {
204
+ rows: number;
205
+ cols: number;
206
+ values: Float32Array;
207
+ }
208
+
209
+ /** One stable monophonic note region segmented from an F0 track. */
210
+ export interface NoteSegment {
211
+ frameStart: number;
212
+ frameEnd: number;
213
+ startSeconds: number;
214
+ endSeconds: number;
215
+ medianCents: number;
216
+ }
217
+
185
218
  /**
186
219
  * Loudness measurement result (EBU R128 / ITU-R BS.1770)
187
220
  */
@@ -29,7 +29,12 @@ import type {
29
29
  import { Mode, PitchClass } from './public_types';
30
30
  import type { ProgressCallback, WasmAcousticResult } from './sonare.js';
31
31
  import type { ValidateOptions } from './validation';
32
- import { assertNonNegativeInteger, assertSampleRate, assertSamples } from './validation';
32
+ import {
33
+ assertFiniteScalar,
34
+ assertNonNegativeInteger,
35
+ assertSampleRate,
36
+ assertSamples,
37
+ } from './validation';
33
38
 
34
39
  function requireModule() {
35
40
  return getSonareModule();
@@ -43,12 +48,29 @@ export interface SamplesRequest extends GuardedOptions {
43
48
  sampleRate?: number;
44
49
  }
45
50
 
51
+ /** Peak-picking configuration for {@link detectOnsets}. */
52
+ export interface OnsetDetectOptions {
53
+ nFft?: number;
54
+ hopLength?: number;
55
+ threshold?: number;
56
+ preMax?: number;
57
+ postMax?: number;
58
+ preAvg?: number;
59
+ postAvg?: number;
60
+ delta?: number;
61
+ wait?: number;
62
+ backtrack?: boolean;
63
+ backtrackRange?: number;
64
+ }
65
+ export interface DetectOnsetsRequest extends SamplesRequest, OnsetDetectOptions {}
66
+
46
67
  /** Canonical request form for key detection functions. */
47
68
  export interface DetectKeyRequest extends KeyDetectionOptions, SamplesRequest {}
48
69
 
49
70
  /** Canonical request form for analysis with synchronous progress reporting. */
50
71
  export interface AnalyzeWithProgressRequest extends SamplesRequest {
51
- onProgress: ProgressCallback;
72
+ onProgress?: ProgressCallback;
73
+ cancel?: () => boolean;
52
74
  }
53
75
 
54
76
  /** Canonical request form for chord detection. */
@@ -64,6 +86,7 @@ export interface ChordFunctionalAnalysisRequest extends DetectChordsRequest {
64
86
  /** Canonical request form for impulse-response analysis. */
65
87
  export interface AnalyzeImpulseResponseRequest extends SamplesRequest {
66
88
  nOctaveBands?: number;
89
+ minDecayDb?: number;
67
90
  }
68
91
 
69
92
  /** Canonical request form for acoustic analysis. */
@@ -207,20 +230,20 @@ export function detectKeyCandidates(
207
230
  * @param sampleRate - Sample rate in Hz (default: 22050)
208
231
  * @returns Array of onset times in seconds
209
232
  */
210
- export function detectOnsets(request: SamplesRequest): Float32Array;
233
+ export function detectOnsets(request: DetectOnsetsRequest): Float32Array;
211
234
  export function detectOnsets(
212
235
  samples: Float32Array,
213
236
  sampleRate?: number,
214
- options?: GuardedOptions,
237
+ options?: OnsetDetectOptions & GuardedOptions,
215
238
  ): Float32Array;
216
239
  export function detectOnsets(
217
- samples: Float32Array | SamplesRequest,
240
+ samples: Float32Array | DetectOnsetsRequest,
218
241
  sampleRate = 22050,
219
- options: GuardedOptions = {},
242
+ options: OnsetDetectOptions & GuardedOptions = {},
220
243
  ): Float32Array {
221
244
  const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
222
245
  validateAnalysisInput('detectOnsets', request.samples, request.sampleRate ?? 22050, request);
223
- return requireModule().detectOnsets(request.samples, request.sampleRate ?? 22050);
246
+ return requireModule().detectOnsets(request.samples, request.sampleRate ?? 22050, request);
224
247
  }
225
248
 
226
249
  /**
@@ -418,23 +441,35 @@ export function analyzeImpulseResponse(
418
441
  samples: Float32Array,
419
442
  sampleRate?: number,
420
443
  nOctaveBands?: number,
444
+ minDecayDb?: number,
421
445
  ): AcousticResult;
422
446
  export function analyzeImpulseResponse(
423
447
  samples: Float32Array | AnalyzeImpulseResponseRequest,
424
448
  sampleRate = 48000,
425
449
  nOctaveBands = 6,
450
+ minDecayDb?: number,
426
451
  ): AcousticResult {
427
- const request = samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands } : samples;
452
+ const request =
453
+ samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands, minDecayDb } : samples;
454
+ if (request.minDecayDb === null) {
455
+ throw new TypeError('analyzeImpulseResponse: minDecayDb must be a finite number');
456
+ }
457
+ const resolvedMinDecayDb = request.minDecayDb === undefined ? 30.0 : request.minDecayDb;
458
+ assertFiniteScalar('analyzeImpulseResponse', resolvedMinDecayDb, 'minDecayDb');
459
+ if (resolvedMinDecayDb <= 0) {
460
+ throw new RangeError('analyzeImpulseResponse: minDecayDb must be greater than zero');
461
+ }
428
462
  validateAnalysisInput(
429
463
  'analyzeImpulseResponse',
430
464
  request.samples,
431
465
  request.sampleRate ?? 48000,
432
466
  request,
433
467
  );
434
- const result: WasmAcousticResult = requireModule().analyzeImpulseResponse(
468
+ const result: WasmAcousticResult = requireModule().analyzeImpulseResponseEx(
435
469
  request.samples,
436
470
  request.sampleRate ?? 48000,
437
471
  request.nOctaveBands ?? 6,
472
+ resolvedMinDecayDb,
438
473
  );
439
474
  return result;
440
475
  }
@@ -559,7 +594,8 @@ export function analyzeWithProgress(
559
594
  const result = requireModule().analyzeWithProgress(
560
595
  request.samples,
561
596
  request.sampleRate ?? 22050,
562
- request.onProgress,
597
+ request.onProgress ?? (() => {}),
598
+ request.cancel ?? (() => false),
563
599
  );
564
600
  return convertAnalysisResult(result);
565
601
  }
@@ -1,8 +1,8 @@
1
- import { panLawCode, panModeCode, sendTimingCode } from './codes';
1
+ import { panLawCode, panModeCode, sendTimingCode, trackMonitorModeCode } from './codes';
2
2
  import { ErrorCode, SonareError } from './errors';
3
3
  import { getSonareModule } from './module_state';
4
4
  import type { ProjectMidiCcBinding, SynthPatch } from './project';
5
- import type { EqBand, PanLaw, PanMode, SendTiming } from './public_types';
5
+ import type { EqBand, PanLawInput, PanMode, SendTiming } from './public_types';
6
6
  import type {
7
7
  WasmClipPageRequest,
8
8
  WasmEngineAutomationPoint,
@@ -38,6 +38,7 @@ export type EngineMarker = WasmEngineMarker;
38
38
  export type EngineMetronomeConfig = WasmEngineMetronomeConfig;
39
39
  export type EngineGraphSpec = WasmEngineGraphSpec;
40
40
  export type EngineCaptureStatus = WasmEngineCaptureStatus;
41
+ export type EngineCaptureSource = EngineCaptureStatus['source'] | number;
41
42
  export type EngineBounceOptions = WasmEngineBounceOptions;
42
43
  export type EngineBounceResult = WasmEngineBounceResult;
43
44
  export type EngineFreezeOptions = WasmEngineFreezeOptions;
@@ -77,6 +78,12 @@ export interface EngineTrackLane {
77
78
  sourceChannelLayout?: number;
78
79
  }
79
80
 
81
+ /** Per-track cue/monitor tap mode: off, pre-fader listen, or after-fader listen. */
82
+ export type EngineTrackMonitorMode = 'off' | 'pfl' | 'afl' | 0 | 1 | 2;
83
+
84
+ /** Short alias for {@link EngineTrackMonitorMode}. */
85
+ export type TrackMonitorMode = EngineTrackMonitorMode;
86
+
80
87
  export interface EngineBus {
81
88
  busId: number;
82
89
  gainDb?: number;
@@ -161,6 +168,7 @@ export class RealtimeEngine {
161
168
  maxBlockSize = 128,
162
169
  commandCapacity = 1024,
163
170
  telemetryCapacity = 1024,
171
+ maxChannels = 64,
164
172
  ) {
165
173
  const module = getSonareModule();
166
174
  const capabilities = engineCapabilities();
@@ -174,6 +182,7 @@ export class RealtimeEngine {
174
182
  maxBlockSize,
175
183
  commandCapacity,
176
184
  telemetryCapacity,
185
+ maxChannels,
177
186
  );
178
187
  }
179
188
 
@@ -182,8 +191,15 @@ export class RealtimeEngine {
182
191
  maxBlockSize: number,
183
192
  commandCapacity = 1024,
184
193
  telemetryCapacity = 1024,
194
+ maxChannels = 64,
185
195
  ): void {
186
- this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);
196
+ this.native.prepareWithChannels(
197
+ sampleRate,
198
+ maxBlockSize,
199
+ commandCapacity,
200
+ telemetryCapacity,
201
+ maxChannels,
202
+ );
187
203
  }
188
204
 
189
205
  /** Queue a sample-accurate parameter change (engine kSetParam). */
@@ -209,6 +225,11 @@ export class RealtimeEngine {
209
225
  this.native.setSoloMute(laneIndex, solo, mute, renderFrame);
210
226
  }
211
227
 
228
+ /** Queue a per-track PFL/AFL monitor tap mode change. */
229
+ setTrackMonitorMode(laneIndex: number, mode: EngineTrackMonitorMode, renderFrame = -1): void {
230
+ this.native.setTrackMonitorMode(laneIndex, trackMonitorModeCode(mode), renderFrame);
231
+ }
232
+
212
233
  setMidiClips(clips: readonly EngineMidiClipSchedule[]): void {
213
234
  this.native.setMidiClips(clips);
214
235
  }
@@ -256,7 +277,12 @@ export class RealtimeEngine {
256
277
  * synthesizer GM fallback bank (the data-free floor).
257
278
  */
258
279
  setSf2Instrument(
259
- config: { destinationId?: number; gain?: number; polyphony?: number } = {},
280
+ config: {
281
+ destinationId?: number;
282
+ gain?: number;
283
+ polyphony?: number;
284
+ preferModelForModeledFamilies?: boolean;
285
+ } = {},
260
286
  destinationId = config.destinationId ?? 0,
261
287
  ): void {
262
288
  this.native.setSf2Instrument(destinationId, config);
@@ -347,6 +373,10 @@ export class RealtimeEngine {
347
373
  return this.native.externalMidiDroppedCount();
348
374
  }
349
375
 
376
+ externalMidiPendingCount(): number {
377
+ return this.native.externalMidiPendingCount();
378
+ }
379
+
350
380
  /**
351
381
  * Drain queued external-MIDI events, already lowered to MIDI 1.0 byte
352
382
  * messages ready to write to a Web MIDI output port. Call once per audio
@@ -358,6 +388,31 @@ export class RealtimeEngine {
358
388
  return this.native.drainExternalMidi(maxRecords);
359
389
  }
360
390
 
391
+ /** Scalar, allocation-free external-MIDI drain for AudioWorklet SAB output. */
392
+ popExternalMidiToScratch(): boolean {
393
+ return this.native.popExternalMidiToScratch();
394
+ }
395
+
396
+ externalMidiScratchDestinationId(): number {
397
+ return this.native.externalMidiScratchDestinationId();
398
+ }
399
+
400
+ externalMidiScratchRenderFrame(): number {
401
+ return this.native.externalMidiScratchRenderFrame();
402
+ }
403
+
404
+ externalMidiScratchByteWord(): number {
405
+ return this.native.externalMidiScratchByteWord();
406
+ }
407
+
408
+ externalMidiScratchByteCount(): number {
409
+ return this.native.externalMidiScratchByteCount();
410
+ }
411
+
412
+ consumeExternalMidiScratch(): void {
413
+ this.native.consumeExternalMidiScratch();
414
+ }
415
+
361
416
  pushMidiInputNoteOn(
362
417
  group: number,
363
418
  channel: number,
@@ -427,6 +482,11 @@ export class RealtimeEngine {
427
482
  this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
428
483
  }
429
484
 
485
+ /** Queue one immediate MIDI 1.0 channel-voice UMP word for a destination. */
486
+ pushMidiUmp(destinationId: number, word0: number, renderFrame = -1): void {
487
+ this.native.pushMidiUmp(destinationId, word0, renderFrame);
488
+ }
489
+
430
490
  /**
431
491
  * Queue an immediate (live) MIDI SysEx frame to a MIDI destination. `data` is
432
492
  * the full message including the leading 0xF0 and trailing 0xF7 (1..512
@@ -480,6 +540,11 @@ export class RealtimeEngine {
480
540
  this.native.settleParameters();
481
541
  }
482
542
 
543
+ /** Drains queued commands on an offline/control-only engine immediately. */
544
+ flushControlCommands(): void {
545
+ this.native.flushControlCommands();
546
+ }
547
+
483
548
  seekPpq(ppq: number, renderFrame = -1): void {
484
549
  this.native.seekPpq(ppq, renderFrame);
485
550
  }
@@ -777,7 +842,7 @@ export class RealtimeEngine {
777
842
  }
778
843
 
779
844
  /** Sets a track lane strip's pan law in realtime. */
780
- setTrackStripPanLaw(trackId: number, panLaw: PanLaw | number): void {
845
+ setTrackStripPanLaw(trackId: number, panLaw: PanLawInput): void {
781
846
  this.native.setTrackStripPanLaw(trackId, panLawCode(panLaw));
782
847
  }
783
848
 
@@ -824,6 +889,27 @@ export class RealtimeEngine {
824
889
  return this.native.popClipPageRequest();
825
890
  }
826
891
 
892
+ /**
893
+ * Moves one native request into the binding's persistent scalar scratch.
894
+ * This avoids creating an embind JS object in AudioWorklet process().
895
+ */
896
+ popClipPageRequestToScratch(): boolean {
897
+ return this.native.popClipPageRequestToScratch();
898
+ }
899
+
900
+ clipPageRequestScratchClipId(): number {
901
+ return this.native.clipPageRequestScratchClipId();
902
+ }
903
+
904
+ clipPageRequestScratchSample(): number {
905
+ return this.native.clipPageRequestScratchSample();
906
+ }
907
+
908
+ /** Cumulative page misses dropped because the native bounded request queue was full. */
909
+ clipPageRequestOverflowCount(): number {
910
+ return this.native.clipPageRequestOverflowCount();
911
+ }
912
+
827
913
  setCaptureBuffer(numChannels: number, capacityFrames: number): void {
828
914
  this.native.setCaptureBuffer(numChannels, capacityFrames);
829
915
  }
@@ -836,7 +922,7 @@ export class RealtimeEngine {
836
922
  this.native.setCapturePunch(startSample, endSample, enabled);
837
923
  }
838
924
 
839
- setCaptureSource(source: EngineCaptureStatus['source']): void {
925
+ setCaptureSource(source: EngineCaptureSource): void {
840
926
  this.native.setCaptureSource(source);
841
927
  }
842
928
 
@@ -861,6 +947,10 @@ export class RealtimeEngine {
861
947
  return this.native.capturedAudio();
862
948
  }
863
949
 
950
+ /**
951
+ * Renders in place, adding engine output to `channels`. Zero each plane first
952
+ * when it contains no upstream input.
953
+ */
864
954
  process(channels: Float32Array[]): Float32Array[] {
865
955
  return this.native.process(channels);
866
956
  }
@@ -886,6 +976,7 @@ export class RealtimeEngine {
886
976
 
887
977
  /**
888
978
  * Runs the engine in place over the prepared per-channel scratch buffers.
979
+ * Zero each active span first when it contains no upstream input.
889
980
  * Allocation-free: safe to call on the AudioWorklet render thread after
890
981
  * `prepareChannels`.
891
982
  */
@@ -913,6 +1004,51 @@ export class RealtimeEngine {
913
1004
  return this.native.drainTelemetry(maxRecords);
914
1005
  }
915
1006
 
1007
+ popTelemetryToScratch(): boolean {
1008
+ return this.native.popTelemetryToScratch();
1009
+ }
1010
+
1011
+ telemetryScratchType(): number {
1012
+ return this.native.telemetryScratchType();
1013
+ }
1014
+
1015
+ telemetryScratchError(): number {
1016
+ return this.native.telemetryScratchError();
1017
+ }
1018
+
1019
+ telemetryScratchRenderFrame(): number {
1020
+ return Number(this.native.telemetryScratchRenderFrame());
1021
+ }
1022
+
1023
+ telemetryScratchTimelineSample(): number {
1024
+ return Number(this.native.telemetryScratchTimelineSample());
1025
+ }
1026
+
1027
+ telemetryScratchAudibleTimelineSample(): number {
1028
+ return Number(this.native.telemetryScratchAudibleTimelineSample());
1029
+ }
1030
+
1031
+ telemetryScratchGraphLatencySamplesQ8(): number {
1032
+ return this.native.telemetryScratchGraphLatencySamplesQ8();
1033
+ }
1034
+
1035
+ telemetryScratchValue(): number {
1036
+ return this.native.telemetryScratchValue();
1037
+ }
1038
+
1039
+ popMeterTelemetryToScratch(): boolean {
1040
+ return this.native.popMeterTelemetryToScratch();
1041
+ }
1042
+ meterScratchTargetId(): number {
1043
+ return this.native.meterScratchTargetId();
1044
+ }
1045
+ meterScratchRenderFrame(): number {
1046
+ return Number(this.native.meterScratchRenderFrame());
1047
+ }
1048
+ meterScratchValue(field: number): number {
1049
+ return this.native.meterScratchValue(field);
1050
+ }
1051
+
916
1052
  drainMeterTelemetry(maxRecords = 1024): EngineMeterTelemetry[] {
917
1053
  return this.native.drainMeterTelemetry(maxRecords);
918
1054
  }
@@ -944,6 +1080,31 @@ export class RealtimeEngine {
944
1080
  return this.native.drainScopeTelemetry(maxRecords);
945
1081
  }
946
1082
 
1083
+ popScopeTelemetryToScratch(): boolean {
1084
+ return this.native.popScopeTelemetryToScratch();
1085
+ }
1086
+ scopeScratchTargetId(): number {
1087
+ return this.native.scopeScratchTargetId();
1088
+ }
1089
+ scopeScratchRenderFrame(): number {
1090
+ return Number(this.native.scopeScratchRenderFrame());
1091
+ }
1092
+ scopeScratchBandCount(): number {
1093
+ return this.native.scopeScratchBandCount();
1094
+ }
1095
+ scopeScratchBand(index: number): number {
1096
+ return this.native.scopeScratchBand(index);
1097
+ }
1098
+ scopeScratchPointCount(): number {
1099
+ return this.native.scopeScratchPointCount();
1100
+ }
1101
+ scopeScratchPointLeft(index: number): number {
1102
+ return this.native.scopeScratchPointLeft(index);
1103
+ }
1104
+ scopeScratchPointRight(index: number): number {
1105
+ return this.native.scopeScratchPointRight(index);
1106
+ }
1107
+
947
1108
  destroy(): void {
948
1109
  this.native.delete();
949
1110
  }