@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
@@ -1,6 +1,8 @@
1
+ import type { EngineCaptureStatus } from '../index';
1
2
  import type {
2
3
  SonareEngineCaptureRequestMessage,
3
- SonareEngineCaptureResponseMessage,
4
+ SonareEngineCaptureResponseMessageInternal,
5
+ SonareEngineClipPageRequestMessage,
4
6
  SonareEngineSyncMessage,
5
7
  SonareEngineTransportRequestMessage,
6
8
  SonareEngineTransportResponseMessage,
@@ -39,7 +41,9 @@ export function isEngineSyncMessage(value: unknown): value is SonareEngineSyncMe
39
41
  value.type === 'syncClipsDelta' ||
40
42
  value.type === 'syncClipPageProvider' ||
41
43
  value.type === 'syncClipPage' ||
44
+ value.type === 'syncClipPageClear' ||
42
45
  value.type === 'syncClipPageCommit' ||
46
+ value.type === 'syncClipPageDestroy' ||
43
47
  value.type === 'syncMidiClips' ||
44
48
  value.type === 'syncMarkers' ||
45
49
  value.type === 'syncMetronome' ||
@@ -68,10 +72,12 @@ export function isEngineSyncMessage(value: unknown): value is SonareEngineSyncMe
68
72
  value.type === 'syncMidiNoteOn' ||
69
73
  value.type === 'syncMidiNoteOff' ||
70
74
  value.type === 'syncMidiCc' ||
75
+ value.type === 'syncMidiUmp' ||
71
76
  value.type === 'syncMidiSysex' ||
72
77
  value.type === 'syncMidiPanic' ||
73
78
  value.type === 'syncMidiDestinationExternal' ||
74
- value.type === 'syncExternalMidiClock'
79
+ value.type === 'syncExternalMidiClock' ||
80
+ value.type === 'destroy'
75
81
  );
76
82
  }
77
83
 
@@ -86,17 +92,108 @@ export function isEngineCaptureRequestMessage(
86
92
  );
87
93
  }
88
94
 
89
- export function isEngineCaptureResponseMessage(
90
- value: unknown,
91
- ): value is SonareEngineCaptureResponseMessage {
95
+ function isCaptureStatus(value: unknown): value is EngineCaptureStatus {
92
96
  return (
93
97
  isRecord(value) &&
94
- value.type === 'captureResponse' &&
95
- typeof value.requestId === 'number' &&
96
- typeof value.ok === 'boolean'
98
+ typeof value.capturedFrames === 'number' &&
99
+ Number.isSafeInteger(value.capturedFrames) &&
100
+ value.capturedFrames >= 0 &&
101
+ typeof value.overflowCount === 'number' &&
102
+ Number.isSafeInteger(value.overflowCount) &&
103
+ value.overflowCount >= 0 &&
104
+ typeof value.armed === 'boolean' &&
105
+ typeof value.punchEnabled === 'boolean' &&
106
+ (value.source === 'input' || value.source === 'output') &&
107
+ typeof value.recordOffsetSamples === 'number' &&
108
+ Number.isSafeInteger(value.recordOffsetSamples)
97
109
  );
98
110
  }
99
111
 
112
+ function isCaptureChannel(value: unknown): value is Float32Array {
113
+ // Capture responses are transferred out of the worklet. A typed view onto
114
+ // the WASM heap or a SharedArrayBuffer must not cross that boundary: the
115
+ // former can be detached by memory growth and the latter cannot be listed
116
+ // in a transfer list. `instanceof` also rejects boxed number arrays.
117
+ return (
118
+ value instanceof Float32Array &&
119
+ typeof ArrayBuffer !== 'undefined' &&
120
+ value.buffer instanceof ArrayBuffer
121
+ );
122
+ }
123
+
124
+ function isCaptureChannels(value: unknown): value is Float32Array[] {
125
+ return Array.isArray(value) && value.every((channel) => isCaptureChannel(channel));
126
+ }
127
+
128
+ function hasOwn(value: Record<string, unknown>, key: string): boolean {
129
+ // biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn is newer than the ES2020 target.
130
+ return Object.prototype.hasOwnProperty.call(value, key);
131
+ }
132
+
133
+ /** Return a request ID even when the response payload itself is malformed. */
134
+ export function engineCaptureResponseRequestId(value: unknown): number | undefined {
135
+ if (
136
+ !isRecord(value) ||
137
+ value.type !== 'captureResponse' ||
138
+ typeof value.requestId !== 'number' ||
139
+ !Number.isSafeInteger(value.requestId)
140
+ ) {
141
+ return undefined;
142
+ }
143
+ return value.requestId;
144
+ }
145
+
146
+ export function isEngineCaptureResponseMessage(
147
+ value: unknown,
148
+ ): value is SonareEngineCaptureResponseMessageInternal {
149
+ const requestId = engineCaptureResponseRequestId(value);
150
+ if (requestId === undefined || !isRecord(value)) {
151
+ return false;
152
+ }
153
+ if (value.ok === false) {
154
+ return (
155
+ typeof value.error === 'string' && !hasOwn(value, 'status') && !hasOwn(value, 'channels')
156
+ );
157
+ }
158
+ if (value.ok !== true || hasOwn(value, 'error')) {
159
+ return false;
160
+ }
161
+ const hasStatus = hasOwn(value, 'status');
162
+ const hasChannels = hasOwn(value, 'channels');
163
+ if (hasStatus && hasChannels) {
164
+ return false;
165
+ }
166
+ if (hasStatus) {
167
+ return isCaptureStatus(value.status);
168
+ }
169
+ if (hasChannels) {
170
+ return isCaptureChannels(value.channels);
171
+ }
172
+ return true;
173
+ }
174
+
175
+ /**
176
+ * Check that a strict success response belongs to the requested operation.
177
+ * Failure responses are operation-independent and are therefore valid for
178
+ * every pending request (the node rejects them after this check).
179
+ */
180
+ export function isEngineCaptureResponseForOperation(
181
+ response: SonareEngineCaptureResponseMessageInternal,
182
+ op: SonareEngineCaptureRequestMessage['op'],
183
+ ): boolean {
184
+ if (!response.ok) {
185
+ return true;
186
+ }
187
+ switch (op) {
188
+ case 'status':
189
+ return 'status' in response;
190
+ case 'read':
191
+ return 'channels' in response;
192
+ case 'reset':
193
+ return !('status' in response) && !('channels' in response);
194
+ }
195
+ }
196
+
100
197
  export function isEngineTransportRequestMessage(
101
198
  value: unknown,
102
199
  ): value is SonareEngineTransportRequestMessage {
@@ -147,6 +244,22 @@ export function isExternalMidiBatchMessage(
147
244
  return isRecord(value) && value.type === 'externalMidi' && Array.isArray(value.events);
148
245
  }
149
246
 
247
+ export function isClipPageRequestMessage(
248
+ value: unknown,
249
+ ): value is SonareEngineClipPageRequestMessage {
250
+ return (
251
+ isRecord(value) &&
252
+ value.type === 'clipPageRequest' &&
253
+ Array.isArray(value.requests) &&
254
+ value.requests.every(
255
+ (request) =>
256
+ isRecord(request) &&
257
+ typeof request.clipId === 'number' &&
258
+ typeof request.pageIndex === 'number',
259
+ )
260
+ );
261
+ }
262
+
150
263
  export function isMeterSnapshot(value: unknown): value is SonareWorkletMeterSnapshot {
151
264
  return (
152
265
  isRecord(value) &&
@@ -6,11 +6,13 @@ import type {
6
6
  EngineMarker,
7
7
  EngineMetronomeConfig,
8
8
  EngineMidiClipSchedule,
9
+ EngineParameterInfo,
9
10
  EngineTempoSegment,
10
11
  EngineTimeSignatureSegment,
11
12
  EngineTrackLane,
12
13
  EngineTransportState,
13
14
  RealtimeVoiceChangerConfigInput,
15
+ RealtimeVoiceChangerPodConfig,
14
16
  } from '../index';
15
17
  import type { AutomationCurve } from '../public_types';
16
18
  import type {
@@ -55,6 +57,16 @@ export interface SonareRealtimeEngineWorkletProcessorOptions {
55
57
  scopeBands?: number;
56
58
  scopeSharedBuffer?: SharedArrayBuffer;
57
59
  scopeRingCapacity?: number;
60
+ /**
61
+ * Lock-free SPSC queue for clip-page cache misses. Supplying this is required
62
+ * for realtime-safe OPFS streaming: it avoids both embind object creation and
63
+ * postMessage structured cloning from AudioWorklet process().
64
+ */
65
+ clipPageRequestSharedBuffer?: SharedArrayBuffer;
66
+ clipPageRequestRingCapacity?: number;
67
+ /** Lock-free worklet-to-main-thread MIDI-1 output ring. */
68
+ externalMidiSharedBuffer?: SharedArrayBuffer;
69
+ externalMidiRingCapacity?: number;
58
70
  }
59
71
 
60
72
  export interface SonareRealtimeVoiceChangerWorkletProcessorOptions {
@@ -66,7 +78,8 @@ export interface SonareRealtimeVoiceChangerWorkletProcessorOptions {
66
78
 
67
79
  export interface SonareRealtimeVoiceChangerSetConfigMessage {
68
80
  type: 'setConfig';
69
- preset: RealtimeVoiceChangerConfigInput;
81
+ /** Pre-normalized by the main thread; never JSON parsed in the worklet. */
82
+ config: RealtimeVoiceChangerPodConfig;
70
83
  }
71
84
 
72
85
  export interface SonareRealtimeVoiceChangerResetMessage {
@@ -88,6 +101,10 @@ export interface SonareRealtimeEngineNodeCapabilities {
88
101
  sharedArrayBuffer: boolean;
89
102
  atomics: boolean;
90
103
  audioWorklet: boolean;
104
+ /** True only when clip-page misses use the bounded SAB ring. */
105
+ clipPageRequestsRealtimeSafe: boolean;
106
+ /** True when external MIDI uses the SAB output ring rather than postMessage. */
107
+ externalMidiRealtimeSafe: boolean;
91
108
  engineAbiVersion?: number;
92
109
  expectedEngineAbiVersion?: number;
93
110
  abiCompatible?: boolean;
@@ -161,18 +178,27 @@ export interface SonareWorkletExternalMidiMessage {
161
178
  events: SonareWorkletExternalMidiEvent[];
162
179
  }
163
180
 
181
+ /** A control-plane sync message that the worklet rejected without crashing. */
182
+ export interface SonareEngineSyncErrorMessage {
183
+ type: 'syncError';
184
+ syncType: SonareEngineSyncMessage['type'];
185
+ message: string;
186
+ }
187
+
164
188
  export type SonareWorkletTransportMessage =
165
189
  | SonareWorkletMeterSnapshot
166
190
  | SonareWorkletSpectrumSnapshot
167
191
  | SonareWorkletExternalMidiMessage
192
+ | SonareEngineClipPageRequestMessage
168
193
  | SonareEngineTelemetryRecord;
169
194
 
170
195
  export interface WorkletTransport {
171
196
  postMessage?: (
172
197
  message:
173
198
  | SonareWorkletTransportMessage
174
- | SonareEngineCaptureResponseMessage
175
- | SonareEngineTransportResponseMessage,
199
+ | SonareEngineCaptureResponseMessageInternal
200
+ | SonareEngineTransportResponseMessage
201
+ | SonareEngineSyncErrorMessage,
176
202
  transfer?: Transferable[],
177
203
  ) => void;
178
204
  onMeter?: (meter: SonareWorkletMeterSnapshot) => void;
@@ -183,6 +209,7 @@ export interface ResolvedMetronomeConfig {
183
209
  beatGain: number;
184
210
  accentGain: number;
185
211
  clickSamples: number;
212
+ clickSeconds: number;
186
213
  }
187
214
 
188
215
  // Fallback metronome gains/click length used by the worklet consumer until the
@@ -192,6 +219,7 @@ export const DEFAULT_METRONOME_CONFIG: ResolvedMetronomeConfig = {
192
219
  beatGain: 0.35,
193
220
  accentGain: 0.7,
194
221
  clickSamples: 0,
222
+ clickSeconds: 0,
195
223
  };
196
224
 
197
225
  export function resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedMetronomeConfig {
@@ -199,6 +227,7 @@ export function resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedM
199
227
  beatGain: config.beatGain ?? DEFAULT_METRONOME_CONFIG.beatGain,
200
228
  accentGain: config.accentGain ?? DEFAULT_METRONOME_CONFIG.accentGain,
201
229
  clickSamples: config.clickSamples ?? DEFAULT_METRONOME_CONFIG.clickSamples,
230
+ clickSeconds: config.clickSeconds ?? DEFAULT_METRONOME_CONFIG.clickSeconds,
202
231
  };
203
232
  if (
204
233
  !Number.isFinite(resolved.beatGain) ||
@@ -207,6 +236,8 @@ export function resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedM
207
236
  resolved.accentGain < 0 ||
208
237
  !Number.isInteger(resolved.clickSamples) ||
209
238
  resolved.clickSamples < 0 ||
239
+ !Number.isFinite(resolved.clickSeconds) ||
240
+ resolved.clickSeconds < 0 ||
210
241
  resolved.clickSamples > 384000 ||
211
242
  (config.clickSeconds !== undefined &&
212
243
  (!Number.isFinite(config.clickSeconds) || config.clickSeconds < 0 || config.clickSeconds > 1))
@@ -220,8 +251,9 @@ export function resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedM
220
251
  // to the worklet engine processor over node.port. Unlike SonareEngineCommandRecord
221
252
  // (a small POD POSTed/ringed every block) these carry bulk/structured payloads
222
253
  // (clip audio buffers, marker lists, metronome config) that cannot fit the
223
- // fixed-size SAB command record, so they are applied OUTSIDE process() — the
224
- // audio-thread equivalent of the engine's control-thread RtPublisher setters.
254
+ // fixed-size SAB command record. Their port handlers still execute on the
255
+ // AudioWorklet rendering thread, so consumers must validate them without
256
+ // allocation and publish bounded snapshots for process() to consume.
225
257
  export interface SonareEngineSyncClipsMessage {
226
258
  type: 'syncClips';
227
259
  clips: EngineClip[];
@@ -237,7 +269,8 @@ export interface SonareEngineSyncClipsDeltaMessage {
237
269
  export interface SonareEngineSyncClipPageProviderMessage {
238
270
  type: 'syncClipPageProvider';
239
271
  clipId: number;
240
- clip: EngineClip;
272
+ /** Omitted when an OPFS stream is primed before its clip is scheduled. */
273
+ clip?: EngineClip;
241
274
  numChannels: number;
242
275
  numSamples: number;
243
276
  pageFrames: number;
@@ -251,10 +284,41 @@ export interface SonareEngineSyncClipPageMessage {
251
284
  channels: Float32Array[];
252
285
  }
253
286
 
287
+ /** Evicts one page after the main-thread sliding window advances. */
288
+ export interface SonareEngineSyncClipPageClearMessage {
289
+ type: 'syncClipPageClear';
290
+ clipId: number;
291
+ pageIndex: number;
292
+ }
293
+
294
+ /** Releases a provider whose clip was removed or whose stream was closed. */
295
+ export interface SonareEngineSyncClipPageDestroyMessage {
296
+ type: 'syncClipPageDestroy';
297
+ clipId: number;
298
+ }
299
+
254
300
  /** Makes a fully supplied paged clip visible to the audio engine. */
255
301
  export interface SonareEngineSyncClipPageCommitMessage {
256
302
  type: 'syncClipPageCommit';
257
303
  clipId: number;
304
+ /**
305
+ * Clip schedule to publish with a provider that was primed before its
306
+ * schedule was known. The original pre-baked push path supplies it on the
307
+ * provider message instead.
308
+ */
309
+ clip?: EngineClip;
310
+ }
311
+
312
+ /**
313
+ * A bounded batch of page misses emitted by the AudioWorklet. The main thread
314
+ * resolves these from OPFS and responds with `syncClipPage`; a missing page is
315
+ * silent until that response arrives.
316
+ */
317
+ export interface SonareEngineClipPageRequestMessage {
318
+ type: 'clipPageRequest';
319
+ requests: Array<{ clipId: number; pageIndex: number }>;
320
+ /** Number of misses dropped because a bounded native or SAB request queue was full. */
321
+ dropped?: number;
258
322
  }
259
323
 
260
324
  export interface SonareEngineSyncMidiClipsMessage {
@@ -278,6 +342,12 @@ export interface SonareEngineSyncAutomationMessage {
278
342
  points: EngineAutomationPoint[];
279
343
  }
280
344
 
345
+ /** Replaces the live engine's registered custom-parameter set. */
346
+ export interface SonareEngineSyncParametersMessage {
347
+ type: 'syncParameters';
348
+ parameters: EngineParameterInfo[];
349
+ }
350
+
281
351
  export interface SonareEngineSyncTempoMessage {
282
352
  type: 'syncTempo';
283
353
  bpm: number;
@@ -403,7 +473,12 @@ export interface SonareEngineSyncSynthInstrumentMessage {
403
473
  export interface SonareEngineSyncSf2InstrumentMessage {
404
474
  type: 'syncSf2Instrument';
405
475
  destinationId: number;
406
- config: { destinationId?: number; gain?: number; polyphony?: number };
476
+ config: {
477
+ destinationId?: number;
478
+ gain?: number;
479
+ polyphony?: number;
480
+ preferModelForModeledFamilies?: boolean;
481
+ };
407
482
  }
408
483
 
409
484
  export interface SonareEngineSyncLoadSoundFontMessage {
@@ -438,6 +513,13 @@ export interface SonareEngineSyncMidiCcMessage {
438
513
  renderFrame: number;
439
514
  }
440
515
 
516
+ export interface SonareEngineSyncMidiUmpMessage {
517
+ type: 'syncMidiUmp';
518
+ destinationId: number;
519
+ word0: number;
520
+ renderFrame: number;
521
+ }
522
+
441
523
  export interface SonareEngineSyncMidiSysexMessage {
442
524
  type: 'syncMidiSysex';
443
525
  destinationId: number;
@@ -461,6 +543,34 @@ export interface SonareEngineSyncExternalMidiClockMessage {
461
543
  enabled: boolean;
462
544
  }
463
545
 
546
+ export interface SonareEngineSyncMidiInputSourceMessage {
547
+ type: 'syncMidiInputSource' | 'syncClearMidiInputSource';
548
+ destinationId?: number;
549
+ }
550
+
551
+ export interface SonareEngineSyncMidiCcBindingMessage {
552
+ type: 'syncMidiCcBinding';
553
+ channel: number;
554
+ controller: number;
555
+ paramId: number;
556
+ minValue: number;
557
+ maxValue: number;
558
+ }
559
+
560
+ export interface SonareEngineSyncMidiInputEventMessage {
561
+ type: 'syncMidiInputNoteOn' | 'syncMidiInputNoteOff' | 'syncMidiInputCc';
562
+ group: number;
563
+ channel: number;
564
+ data0: number;
565
+ data1: number;
566
+ portTimeSamples: number;
567
+ }
568
+
569
+ /** Releases the realtime engine and all worklet-owned clip buffers. */
570
+ export interface SonareEngineDestroyMessage {
571
+ type: 'destroy';
572
+ }
573
+
464
574
  export type SonareEngineInstrumentSyncMessage =
465
575
  | SonareEngineSyncBuiltinInstrumentMessage
466
576
  | SonareEngineSyncSynthInstrumentMessage
@@ -473,11 +583,14 @@ export type SonareEngineSyncMessage =
473
583
  | SonareEngineSyncClipsDeltaMessage
474
584
  | SonareEngineSyncClipPageProviderMessage
475
585
  | SonareEngineSyncClipPageMessage
586
+ | SonareEngineSyncClipPageClearMessage
476
587
  | SonareEngineSyncClipPageCommitMessage
588
+ | SonareEngineSyncClipPageDestroyMessage
477
589
  | SonareEngineSyncMidiClipsMessage
478
590
  | SonareEngineSyncMarkersMessage
479
591
  | SonareEngineSyncMetronomeMessage
480
592
  | SonareEngineSyncAutomationMessage
593
+ | SonareEngineSyncParametersMessage
481
594
  | SonareEngineSyncTempoMessage
482
595
  | SonareEngineSyncMixerMessage
483
596
  | SonareEngineSyncCaptureMessage
@@ -500,10 +613,15 @@ export type SonareEngineSyncMessage =
500
613
  | SonareEngineSyncMidiFxMessage
501
614
  | SonareEngineSyncMidiNoteMessage
502
615
  | SonareEngineSyncMidiCcMessage
616
+ | SonareEngineSyncMidiUmpMessage
503
617
  | SonareEngineSyncMidiSysexMessage
504
618
  | SonareEngineSyncMidiPanicMessage
505
619
  | SonareEngineSyncMidiDestinationExternalMessage
506
- | SonareEngineSyncExternalMidiClockMessage;
620
+ | SonareEngineSyncExternalMidiClockMessage
621
+ | SonareEngineSyncMidiInputSourceMessage
622
+ | SonareEngineSyncMidiCcBindingMessage
623
+ | SonareEngineSyncMidiInputEventMessage
624
+ | SonareEngineDestroyMessage;
507
625
 
508
626
  export interface WorkletPort {
509
627
  postMessage?: (message: unknown, transfer?: Transferable[]) => void;
@@ -518,6 +636,12 @@ export interface SonareEngineCaptureRequestMessage {
518
636
  op: 'status' | 'read' | 'reset';
519
637
  }
520
638
 
639
+ /**
640
+ * Public capture response shape retained for source compatibility.
641
+ *
642
+ * The worklet wire protocol is stricter than this legacy structural type; use
643
+ * `SonareEngineCaptureResponseMessageInternal` inside the implementation.
644
+ */
521
645
  export interface SonareEngineCaptureResponseMessage {
522
646
  type: 'captureResponse';
523
647
  requestId: number;
@@ -527,6 +651,40 @@ export interface SonareEngineCaptureResponseMessage {
527
651
  error?: string;
528
652
  }
529
653
 
654
+ interface SonareEngineCaptureStatusResponseMessageInternal {
655
+ type: 'captureResponse';
656
+ requestId: number;
657
+ ok: true;
658
+ status: EngineCaptureStatus;
659
+ }
660
+
661
+ interface SonareEngineCaptureReadResponseMessageInternal {
662
+ type: 'captureResponse';
663
+ requestId: number;
664
+ ok: true;
665
+ channels: Float32Array[];
666
+ }
667
+
668
+ interface SonareEngineCaptureResetResponseMessageInternal {
669
+ type: 'captureResponse';
670
+ requestId: number;
671
+ ok: true;
672
+ }
673
+
674
+ interface SonareEngineCaptureErrorResponseMessageInternal {
675
+ type: 'captureResponse';
676
+ requestId: number;
677
+ ok: false;
678
+ error: string;
679
+ }
680
+
681
+ /** Strict wire response type used only by the worklet implementation. */
682
+ export type SonareEngineCaptureResponseMessageInternal =
683
+ | SonareEngineCaptureStatusResponseMessageInternal
684
+ | SonareEngineCaptureReadResponseMessageInternal
685
+ | SonareEngineCaptureResetResponseMessageInternal
686
+ | SonareEngineCaptureErrorResponseMessageInternal;
687
+
530
688
  export interface SonareEngineTransportRequestMessage {
531
689
  type: 'transportRequest';
532
690
  requestId: number;