@libraz/libsonare 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +178 -0
- package/README.md +26 -170
- package/dist/_chain_config.d.ts +14 -0
- package/dist/_chain_config.d.ts.map +1 -0
- package/dist/_effects_common.d.ts +6 -0
- package/dist/_effects_common.d.ts.map +1 -0
- package/dist/_feature_validation.d.ts +8 -0
- package/dist/_feature_validation.d.ts.map +1 -0
- package/dist/_fft_options.d.ts +24 -0
- package/dist/_fft_options.d.ts.map +1 -0
- package/dist/align_take.d.ts +50 -0
- package/dist/align_take.d.ts.map +1 -0
- package/dist/analysis.d.ts +29 -5837
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +873 -722
- package/dist/analysis.js.map +1 -1
- package/dist/analysis_helpers.d.ts +9 -0
- package/dist/analysis_helpers.d.ts.map +1 -0
- package/dist/audio.d.ts +163 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/clip_page_streamer.d.ts +133 -0
- package/dist/clip_page_streamer.d.ts.map +1 -0
- package/dist/codes.d.ts +44 -0
- package/dist/codes.d.ts.map +1 -0
- package/dist/effects_mastering.d.ts +23 -0
- package/dist/effects_mastering.d.ts.map +1 -0
- package/dist/effects_note_ops.d.ts +477 -0
- package/dist/effects_note_ops.d.ts.map +1 -0
- package/dist/effects_percussive.d.ts +185 -0
- package/dist/effects_percussive.d.ts.map +1 -0
- package/dist/effects_separation.d.ts +65 -0
- package/dist/effects_separation.d.ts.map +1 -0
- package/dist/effects_spectral.d.ts +28 -0
- package/dist/effects_spectral.d.ts.map +1 -0
- package/dist/effects_timepitch.d.ts +129 -0
- package/dist/effects_timepitch.d.ts.map +1 -0
- package/dist/effects_voice_change.d.ts +53 -0
- package/dist/effects_voice_change.d.ts.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/feature_core.d.ts +341 -0
- package/dist/feature_core.d.ts.map +1 -0
- package/dist/feature_decompose.d.ts +278 -0
- package/dist/feature_decompose.d.ts.map +1 -0
- package/dist/feature_inverse.d.ts +128 -0
- package/dist/feature_inverse.d.ts.map +1 -0
- package/dist/feature_loudness.d.ts +66 -0
- package/dist/feature_loudness.d.ts.map +1 -0
- package/dist/feature_music.d.ts +307 -0
- package/dist/feature_music.d.ts.map +1 -0
- package/dist/feature_pitch.d.ts +108 -0
- package/dist/feature_pitch.d.ts.map +1 -0
- package/dist/feature_resample.d.ts +16 -0
- package/dist/feature_resample.d.ts.map +1 -0
- package/dist/feature_spectral.d.ts +137 -0
- package/dist/feature_spectral.d.ts.map +1 -0
- package/dist/feature_spectrogram.d.ts +198 -0
- package/dist/feature_spectrogram.d.ts.map +1 -0
- package/dist/features.d.ts +10 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/hrtf/default.shrf +0 -0
- package/dist/index.d.ts +74 -7329
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3816 -1359
- package/dist/index.js.map +1 -1
- package/dist/instrument_types.d.ts +517 -0
- package/dist/instrument_types.d.ts.map +1 -0
- package/dist/live_audio.d.ts +35 -0
- package/dist/live_audio.d.ts.map +1 -0
- package/dist/mastering_chain.d.ts +213 -0
- package/dist/mastering_chain.d.ts.map +1 -0
- package/dist/mastering_core.d.ts +457 -0
- package/dist/mastering_core.d.ts.map +1 -0
- package/dist/mastering_dynamics.d.ts +80 -0
- package/dist/mastering_dynamics.d.ts.map +1 -0
- package/dist/metering.d.ts +287 -0
- package/dist/metering.d.ts.map +1 -0
- package/dist/mixer.d.ts +464 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixing_assistant.d.ts +62 -0
- package/dist/mixing_assistant.d.ts.map +1 -0
- package/dist/mixing_oneshot.d.ts +40 -0
- package/dist/mixing_oneshot.d.ts.map +1 -0
- package/dist/module_state.d.ts +15 -0
- package/dist/module_state.d.ts.map +1 -0
- package/dist/opfs_clip_pages.d.ts +28 -0
- package/dist/opfs_clip_pages.d.ts.map +1 -0
- package/dist/playback_renderer.d.ts +128 -0
- package/dist/playback_renderer.d.ts.map +1 -0
- package/dist/polyphony.d.ts +202 -0
- package/dist/polyphony.d.ts.map +1 -0
- package/dist/project.d.ts +8 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project_class.d.ts +562 -0
- package/dist/project_class.d.ts.map +1 -0
- package/dist/project_internal.d.ts +194 -0
- package/dist/project_internal.d.ts.map +1 -0
- package/dist/project_synth.d.ts +74 -0
- package/dist/project_synth.d.ts.map +1 -0
- package/dist/project_types.d.ts +652 -0
- package/dist/project_types.d.ts.map +1 -0
- package/dist/public_types.d.ts +185 -0
- package/dist/public_types.d.ts.map +1 -0
- package/dist/public_types_acoustic.d.ts +215 -0
- package/dist/public_types_acoustic.d.ts.map +1 -0
- package/dist/public_types_mastering.d.ts +510 -0
- package/dist/public_types_mastering.d.ts.map +1 -0
- package/dist/public_types_mixing.d.ts +436 -0
- package/dist/public_types_mixing.d.ts.map +1 -0
- package/dist/public_types_music.d.ts +619 -0
- package/dist/public_types_music.d.ts.map +1 -0
- package/dist/public_types_playback.d.ts +164 -0
- package/dist/public_types_playback.d.ts.map +1 -0
- package/dist/public_types_realtime.d.ts +174 -0
- package/dist/public_types_realtime.d.ts.map +1 -0
- package/dist/public_types_repair.d.ts +424 -0
- package/dist/public_types_repair.d.ts.map +1 -0
- package/dist/public_types_spectral.d.ts +697 -0
- package/dist/public_types_spectral.d.ts.map +1 -0
- package/dist/quick_analysis.d.ts +445 -0
- package/dist/quick_analysis.d.ts.map +1 -0
- package/dist/realtime_engine.d.ts +850 -0
- package/dist/realtime_engine.d.ts.map +1 -0
- package/dist/realtime_voice_changer.d.ts +158 -0
- package/dist/realtime_voice_changer.d.ts.map +1 -0
- package/dist/repair_dereverb.d.ts +187 -0
- package/dist/repair_dereverb.d.ts.map +1 -0
- package/dist/repair_impulsive.d.ts +186 -0
- package/dist/repair_impulsive.d.ts.map +1 -0
- package/dist/repair_noise.d.ts +239 -0
- package/dist/repair_noise.d.ts.map +1 -0
- package/dist/repair_trim.d.ts +123 -0
- package/dist/repair_trim.d.ts.map +1 -0
- package/dist/sample_bank.d.ts +84 -0
- package/dist/sample_bank.d.ts.map +1 -0
- package/dist/scale.d.ts +10 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/schemas/mixer-scene.schema.json +393 -0
- package/dist/schemas/playback-renderer-config.schema.json +392 -0
- package/dist/sonare-analysis.d.ts +8 -0
- package/dist/sonare-analysis.js +2 -2
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.d.ts +3909 -0
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/stream_analyzer.d.ts +163 -0
- package/dist/stream_analyzer.d.ts.map +1 -0
- package/dist/stream_types.d.ts +214 -0
- package/dist/stream_types.d.ts.map +1 -0
- package/dist/streaming_mixing.d.ts +6 -0
- package/dist/streaming_mixing.d.ts.map +1 -0
- package/dist/streaming_processors.d.ts +335 -0
- package/dist/streaming_processors.d.ts.map +1 -0
- package/dist/transcribe.d.ts +77 -0
- package/dist/transcribe.d.ts.map +1 -0
- package/dist/validation.d.ts +140 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/web_midi.d.ts +77 -0
- package/dist/web_midi.d.ts.map +1 -0
- package/dist/worker.d.ts +5 -48
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +94 -41
- package/dist/worker.js.map +1 -1
- package/dist/worker_client.d.ts +96 -0
- package/dist/worker_client.d.ts.map +1 -0
- package/dist/worker_protocol.d.ts +43 -0
- package/dist/worker_protocol.d.ts.map +1 -0
- package/dist/worklet/audio_types.d.ts +21 -0
- package/dist/worklet/audio_types.d.ts.map +1 -0
- package/dist/worklet/engine-automation.d.ts +29 -0
- package/dist/worklet/engine-automation.d.ts.map +1 -0
- package/dist/worklet/engine-capture-facade.d.ts +35 -0
- package/dist/worklet/engine-capture-facade.d.ts.map +1 -0
- package/dist/worklet/engine-clips.d.ts +23 -0
- package/dist/worklet/engine-clips.d.ts.map +1 -0
- package/dist/worklet/engine-markers.d.ts +40 -0
- package/dist/worklet/engine-markers.d.ts.map +1 -0
- package/dist/worklet/engine-mixer-facade.d.ts +128 -0
- package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
- package/dist/worklet/engine-node.d.ts +81 -0
- package/dist/worklet/engine-node.d.ts.map +1 -0
- package/dist/worklet/engine-offline.d.ts +81 -0
- package/dist/worklet/engine-offline.d.ts.map +1 -0
- package/dist/worklet/engine-options.d.ts +12 -0
- package/dist/worklet/engine-options.d.ts.map +1 -0
- package/dist/worklet/engine-parameter-facade.d.ts +106 -0
- package/dist/worklet/engine-parameter-facade.d.ts.map +1 -0
- package/dist/worklet/engine-processor.d.ts +68 -0
- package/dist/worklet/engine-processor.d.ts.map +1 -0
- package/dist/worklet/engine-register.d.ts +2 -0
- package/dist/worklet/engine-register.d.ts.map +1 -0
- package/dist/worklet/engine-strips.d.ts +73 -0
- package/dist/worklet/engine-strips.d.ts.map +1 -0
- package/dist/worklet/engine-sync.d.ts +37 -0
- package/dist/worklet/engine-sync.d.ts.map +1 -0
- package/dist/worklet/engine-tempo-facade.d.ts +48 -0
- package/dist/worklet/engine-tempo-facade.d.ts.map +1 -0
- package/dist/worklet/engine.d.ts +397 -0
- package/dist/worklet/engine.d.ts.map +1 -0
- package/dist/worklet/guards.d.ts +47 -0
- package/dist/worklet/guards.d.ts.map +1 -0
- package/dist/worklet/messages.d.ts +710 -0
- package/dist/worklet/messages.d.ts.map +1 -0
- package/dist/worklet/mixer-processor.d.ts +46 -0
- package/dist/worklet/mixer-processor.d.ts.map +1 -0
- package/dist/worklet/playback-processor.d.ts +62 -0
- package/dist/worklet/playback-processor.d.ts.map +1 -0
- package/dist/worklet/protocol.d.ts +323 -0
- package/dist/worklet/protocol.d.ts.map +1 -0
- package/dist/worklet/voice-changer-processor.d.ts +41 -0
- package/dist/worklet/voice-changer-processor.d.ts.map +1 -0
- package/dist/worklet.d.ts +16 -2142
- package/dist/worklet.d.ts.map +1 -0
- package/dist/worklet.js +2889 -466
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -12
- package/src/_effects_common.ts +17 -0
- package/src/_feature_validation.ts +34 -0
- package/src/_fft_options.ts +39 -0
- package/src/align_take.ts +64 -0
- package/src/analysis.ts +56 -3
- package/src/analysis_helpers.ts +7 -0
- package/src/audio.ts +106 -3
- package/src/codes.ts +39 -2
- package/src/effects_mastering.ts +97 -22
- package/src/effects_note_ops.ts +635 -0
- package/src/effects_percussive.ts +217 -0
- package/src/effects_separation.ts +150 -0
- package/src/effects_spectral.ts +60 -0
- package/src/effects_timepitch.ts +377 -0
- package/src/errors.ts +23 -1
- package/src/feature_core.ts +127 -2
- package/src/feature_decompose.ts +633 -0
- package/src/feature_inverse.ts +454 -0
- package/src/feature_loudness.ts +125 -0
- package/src/feature_music.ts +107 -14
- package/src/feature_pitch.ts +96 -1
- package/src/feature_spectral.ts +16 -611
- package/src/feature_spectrogram.ts +63 -450
- package/src/features.ts +36 -22
- package/src/index.ts +285 -30
- package/src/instrument_types.ts +645 -0
- package/src/live_audio.ts +27 -1
- package/src/mastering_chain.ts +184 -0
- package/src/mastering_core.ts +346 -32
- package/src/mastering_dynamics.ts +22 -11
- package/src/metering.ts +67 -24
- package/src/mixer.ts +212 -3
- package/src/mixing_assistant.ts +138 -0
- package/src/mixing_oneshot.ts +10 -5
- package/src/module_state.ts +24 -2
- package/src/playback_renderer.ts +252 -0
- package/src/polyphony.ts +279 -0
- package/src/project.ts +64 -24
- package/src/project_class.ts +502 -29
- package/src/project_internal.ts +151 -42
- package/src/project_synth.ts +67 -1
- package/src/project_types.ts +302 -270
- package/src/public_types.ts +122 -3
- package/src/public_types_acoustic.ts +112 -3
- package/src/public_types_mastering.ts +254 -73
- package/src/public_types_mixing.ts +363 -1
- package/src/public_types_music.ts +312 -2
- package/src/public_types_playback.ts +195 -0
- package/src/public_types_realtime.ts +39 -7
- package/src/public_types_repair.ts +446 -0
- package/src/public_types_spectral.ts +487 -1
- package/src/quick_analysis.ts +203 -26
- package/src/realtime_engine.ts +750 -28
- package/src/realtime_voice_changer.ts +55 -1
- package/src/repair_dereverb.ts +299 -0
- package/src/repair_impulsive.ts +395 -0
- package/src/repair_noise.ts +425 -0
- package/src/repair_trim.ts +226 -0
- package/src/sample_bank.ts +113 -0
- package/src/sonare.js.d.ts +1125 -30
- package/src/stream_analyzer.ts +36 -4
- package/src/stream_types.ts +37 -0
- package/src/streaming_mixing.ts +1 -1
- package/src/streaming_processors.ts +212 -10
- package/src/transcribe.ts +89 -0
- package/src/validation.ts +285 -11
- package/src/web_midi.ts +1 -6
- package/src/worker.ts +18 -2
- package/src/worklet/audio_types.ts +37 -0
- package/src/worklet/engine-mixer-facade.ts +444 -10
- package/src/worklet/engine-node.ts +85 -28
- package/src/worklet/engine-offline.ts +14 -8
- package/src/worklet/engine-parameter-facade.ts +21 -0
- package/src/worklet/engine-processor.ts +307 -71
- package/src/worklet/engine-register.ts +32 -18
- package/src/worklet/engine-strips.ts +280 -9
- package/src/worklet/engine-sync.ts +14 -6
- package/src/worklet/engine.ts +365 -31
- package/src/worklet/guards.ts +140 -44
- package/src/worklet/messages.ts +239 -2
- package/src/worklet/mixer-processor.ts +109 -47
- package/src/worklet/playback-processor.ts +300 -0
- package/src/worklet/protocol.ts +53 -3
- package/src/worklet/voice-changer-processor.ts +17 -11
- package/src/worklet.ts +22 -0
- package/src/effects_transform.ts +0 -718
- package/src/mastering_repair.ts +0 -273
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
isEngineTransportResponseMessage,
|
|
10
10
|
isExternalMidiBatchMessage,
|
|
11
11
|
isMeterSnapshot,
|
|
12
|
+
requireChannelCount,
|
|
13
|
+
requireIntegerOption,
|
|
12
14
|
} from './guards';
|
|
13
15
|
import type {
|
|
14
16
|
SonareEngineCaptureRequestMessage,
|
|
@@ -29,6 +31,7 @@ import {
|
|
|
29
31
|
createSonareMeterRingBuffer,
|
|
30
32
|
createSonareScopeRingBuffer,
|
|
31
33
|
isRecord,
|
|
34
|
+
isUint32Slot,
|
|
32
35
|
pushSonareEngineCommandRingBuffer,
|
|
33
36
|
readSonareClipPageRequestRingBuffer,
|
|
34
37
|
readSonareEngineTelemetryRingBuffer,
|
|
@@ -63,13 +66,6 @@ function isTrackMonitorMode(value: number | bigint | undefined): boolean {
|
|
|
63
66
|
return typeof mode === 'number' && Number.isSafeInteger(mode) && mode >= 0 && mode <= 2;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
/** A track-monitor command cannot infer a lane: its target is a uint32 index. */
|
|
67
|
-
function isTrackMonitorLaneIndex(value: number | undefined): boolean {
|
|
68
|
-
return (
|
|
69
|
-
typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && value <= 0xffff_ffff
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
69
|
function isValidCommandRecord(command: SonareEngineCommandRecord): boolean {
|
|
74
70
|
const type = Number(command.type);
|
|
75
71
|
if (
|
|
@@ -81,12 +77,15 @@ function isValidCommandRecord(command: SonareEngineCommandRecord): boolean {
|
|
|
81
77
|
return false;
|
|
82
78
|
}
|
|
83
79
|
return (
|
|
84
|
-
|
|
80
|
+
// The record writer refuses a targetId outside the slot by throwing, and
|
|
81
|
+
// this path answers false instead, so it must reject the same domain here.
|
|
82
|
+
(command.targetId === undefined || isUint32Slot(command.targetId)) &&
|
|
85
83
|
(command.argFloat === undefined || Number.isFinite(command.argFloat)) &&
|
|
86
84
|
isFiniteInteger(command.argInt) &&
|
|
87
85
|
isFiniteInteger(command.sampleTime) &&
|
|
86
|
+
// A track-monitor command cannot infer a lane: its target is required.
|
|
88
87
|
(type !== SonareEngineCommandType.SetTrackMonitorMode ||
|
|
89
|
-
(
|
|
88
|
+
(isUint32Slot(command.targetId) && isTrackMonitorMode(command.argInt)))
|
|
90
89
|
);
|
|
91
90
|
}
|
|
92
91
|
|
|
@@ -172,6 +171,12 @@ export class SonareRealtimeEngineNode {
|
|
|
172
171
|
this.resolveReady();
|
|
173
172
|
}
|
|
174
173
|
this.node.port.onmessage = (event: MessageEvent<unknown>) => {
|
|
174
|
+
// A host that kept its own reference to this handler (or a port
|
|
175
|
+
// implementation that ignores the detach in destroy()) must not be able
|
|
176
|
+
// to reach listeners of a node the host already disposed.
|
|
177
|
+
if (this.destroyed) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
175
180
|
const captureRequestId = engineCaptureResponseRequestId(event.data);
|
|
176
181
|
if (captureRequestId !== undefined) {
|
|
177
182
|
const pending = this.captureRequests.get(captureRequestId);
|
|
@@ -208,7 +213,7 @@ export class SonareRealtimeEngineNode {
|
|
|
208
213
|
} else if (isRecord(event.data) && event.data.type === 'syncError') {
|
|
209
214
|
const syncError: SonareEngineSyncErrorMessage = {
|
|
210
215
|
type: 'syncError',
|
|
211
|
-
syncType: String(event.data.syncType)
|
|
216
|
+
syncType: String(event.data.syncType),
|
|
212
217
|
message: String(event.data.message ?? 'AudioWorklet sync failed'),
|
|
213
218
|
};
|
|
214
219
|
for (const listener of this.syncErrorListeners) {
|
|
@@ -268,43 +273,86 @@ export class SonareRealtimeEngineNode {
|
|
|
268
273
|
);
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
// Resolved before the transport branch, so a capacity the ring could not
|
|
277
|
+
// hold is refused by name whichever transport the caller ends up with.
|
|
278
|
+
const commandRingCapacity = requireIntegerOption(
|
|
279
|
+
options.commandRingCapacity,
|
|
280
|
+
128,
|
|
281
|
+
'commandRingCapacity',
|
|
282
|
+
1,
|
|
283
|
+
);
|
|
284
|
+
if (commandRingCapacity > 65536) {
|
|
285
|
+
throw new RangeError('commandRingCapacity must be at most 65536');
|
|
286
|
+
}
|
|
287
|
+
const telemetryRingCapacity = requireIntegerOption(
|
|
288
|
+
options.telemetryRingCapacity,
|
|
289
|
+
128,
|
|
290
|
+
'telemetryRingCapacity',
|
|
291
|
+
1,
|
|
292
|
+
);
|
|
293
|
+
const meterRingCapacity = requireIntegerOption(
|
|
294
|
+
options.meterRingCapacity,
|
|
295
|
+
128,
|
|
296
|
+
'meterRingCapacity',
|
|
297
|
+
1,
|
|
298
|
+
);
|
|
299
|
+
const scopeRingCapacity = requireIntegerOption(
|
|
300
|
+
options.scopeRingCapacity,
|
|
301
|
+
64,
|
|
302
|
+
'scopeRingCapacity',
|
|
303
|
+
1,
|
|
304
|
+
);
|
|
305
|
+
const scopeBands = requireIntegerOption(options.scopeBands, 48, 'scopeBands', 1);
|
|
306
|
+
const clipPageRequestRingCapacity = requireIntegerOption(
|
|
307
|
+
options.clipPageRequestRingCapacity,
|
|
308
|
+
128,
|
|
309
|
+
'clipPageRequestRingCapacity',
|
|
310
|
+
1,
|
|
311
|
+
);
|
|
312
|
+
const externalMidiRingCapacity = requireIntegerOption(
|
|
313
|
+
options.externalMidiRingCapacity,
|
|
314
|
+
256,
|
|
315
|
+
'externalMidiRingCapacity',
|
|
316
|
+
1,
|
|
317
|
+
);
|
|
271
318
|
const commandRing =
|
|
272
|
-
mode === 'sab'
|
|
273
|
-
? createSonareEngineCommandRingBuffer(options.commandRingCapacity ?? 128)
|
|
274
|
-
: undefined;
|
|
319
|
+
mode === 'sab' ? createSonareEngineCommandRingBuffer(commandRingCapacity) : undefined;
|
|
275
320
|
const telemetryRing =
|
|
276
|
-
mode === 'sab'
|
|
277
|
-
? createSonareEngineTelemetryRingBuffer(options.telemetryRingCapacity ?? 128)
|
|
278
|
-
: undefined;
|
|
321
|
+
mode === 'sab' ? createSonareEngineTelemetryRingBuffer(telemetryRingCapacity) : undefined;
|
|
279
322
|
// Meter ring: the engine publishes meters into a SAB ring. Lock-free meter
|
|
280
323
|
// delivery matches the telemetry path and keeps the audio render callback
|
|
281
324
|
// allocation-free in SAB mode.
|
|
282
|
-
const meterRing =
|
|
283
|
-
mode === 'sab' ? createSonareMeterRingBuffer(options.meterRingCapacity ?? 128) : undefined;
|
|
325
|
+
const meterRing = mode === 'sab' ? createSonareMeterRingBuffer(meterRingCapacity) : undefined;
|
|
284
326
|
// Scope ring (FFT spectrum + goniometer): opt-in. The per-block FFT is
|
|
285
327
|
// heavier than the meter path, so it is created only when the caller
|
|
286
|
-
// requests scope telemetry via scopeIntervalFrames > 0.
|
|
287
|
-
|
|
328
|
+
// requests scope telemetry via scopeIntervalFrames > 0. Zero is the off
|
|
329
|
+
// switch, so the interval's floor is 0 rather than the capacities' 1.
|
|
330
|
+
const scopeIntervalFrames = requireIntegerOption(
|
|
331
|
+
options.scopeIntervalFrames,
|
|
332
|
+
0,
|
|
333
|
+
'scopeIntervalFrames',
|
|
334
|
+
0,
|
|
335
|
+
);
|
|
288
336
|
const scopeRing =
|
|
289
337
|
mode === 'sab' && scopeIntervalFrames > 0
|
|
290
|
-
? createSonareScopeRingBuffer(
|
|
338
|
+
? createSonareScopeRingBuffer(scopeRingCapacity, scopeBands)
|
|
291
339
|
: undefined;
|
|
292
340
|
// Paged-clip cache misses are a worklet-to-main-thread control signal. A
|
|
293
341
|
// dedicated SPSC SAB ring keeps process() free of both embind request
|
|
294
342
|
// objects and postMessage structured cloning.
|
|
295
343
|
const clipPageRequestRing =
|
|
296
344
|
mode === 'sab'
|
|
297
|
-
? createSonareClipPageRequestRingBuffer(
|
|
345
|
+
? createSonareClipPageRequestRingBuffer(clipPageRequestRingCapacity)
|
|
298
346
|
: undefined;
|
|
299
347
|
const externalMidiRing =
|
|
300
|
-
mode === 'sab'
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));
|
|
348
|
+
mode === 'sab' ? createSonareExternalMidiRingBuffer(externalMidiRingCapacity) : undefined;
|
|
349
|
+
const channelCount = requireChannelCount(options.channelCount, 2);
|
|
350
|
+
const cueOutput = options.cueOutput === true;
|
|
304
351
|
const processorOptions: SonareRealtimeEngineWorkletProcessorOptions = {
|
|
305
352
|
sampleRate: options.sampleRate ?? context.sampleRate,
|
|
306
353
|
blockSize,
|
|
307
354
|
channelCount,
|
|
355
|
+
cueOutput,
|
|
308
356
|
commandSharedBuffer: commandRing?.sharedBuffer,
|
|
309
357
|
commandRingCapacity: commandRing?.capacity,
|
|
310
358
|
telemetrySharedBuffer: telemetryRing?.sharedBuffer,
|
|
@@ -329,8 +377,10 @@ export class SonareRealtimeEngineNode {
|
|
|
329
377
|
new AudioWorkletNode(ctx, name, nodeOptions));
|
|
330
378
|
const node = factory(context, processorName, {
|
|
331
379
|
numberOfInputs: 1,
|
|
332
|
-
|
|
333
|
-
|
|
380
|
+
// The cue bus needs its own output; a single-output node keeps the
|
|
381
|
+
// historical mix where process() folds the cue into the program.
|
|
382
|
+
numberOfOutputs: cueOutput ? 2 : 1,
|
|
383
|
+
outputChannelCount: cueOutput ? [channelCount, channelCount] : [channelCount],
|
|
334
384
|
processorOptions,
|
|
335
385
|
});
|
|
336
386
|
return new SonareRealtimeEngineNode(
|
|
@@ -343,6 +393,7 @@ export class SonareRealtimeEngineNode {
|
|
|
343
393
|
audioWorklet,
|
|
344
394
|
clipPageRequestsRealtimeSafe: mode === 'sab',
|
|
345
395
|
externalMidiRealtimeSafe: mode === 'sab',
|
|
396
|
+
cueOutput,
|
|
346
397
|
engineAbiVersion: detectedCapabilities?.engineAbiVersion,
|
|
347
398
|
expectedEngineAbiVersion: detectedCapabilities?.expectedEngineAbiVersion,
|
|
348
399
|
abiCompatible: detectedCapabilities?.abiCompatible,
|
|
@@ -595,6 +646,12 @@ export class SonareRealtimeEngineNode {
|
|
|
595
646
|
this.scopeListeners.clear();
|
|
596
647
|
this.midiOutListeners.clear();
|
|
597
648
|
this.clipPageRequestListeners.clear();
|
|
649
|
+
this.syncErrorListeners.clear();
|
|
650
|
+
// The port outlives destroy(): the worklet tears itself down asynchronously
|
|
651
|
+
// after the 'destroy' message above, so a syncError already in flight would
|
|
652
|
+
// still be dispatched here. Detaching the handler is what makes the
|
|
653
|
+
// listener sets stay empty rather than being refilled by a late message.
|
|
654
|
+
this.node.port.onmessage = null;
|
|
598
655
|
}
|
|
599
656
|
|
|
600
657
|
private emitTelemetry(telemetry: SonareEngineTelemetryRecord): void {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EngineCaptureStatus, EngineMarker, EngineTrackLane, RealtimeEngine } from '../index';
|
|
2
|
+
import { requireChannelCount, requireInteger, requireIntegerOption } from './guards';
|
|
2
3
|
import type { SonareEngineSyncCaptureMessage, SonareEngineTransportFacade } from './messages';
|
|
3
4
|
|
|
4
5
|
/** Capture configuration options accepted by the engine's `configureCapture`. */
|
|
@@ -12,20 +13,22 @@ export interface CaptureOptions {
|
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Normalizes capture options into the resolved config carried by the
|
|
15
|
-
* `syncCapture` message, applying
|
|
16
|
+
* `syncCapture` message, applying defaults and refusing a non-integer count.
|
|
16
17
|
*
|
|
17
18
|
* @param options Raw capture options.
|
|
18
19
|
* @param defaultChannels Channel count to use when `options.channels` is unset.
|
|
20
|
+
* @throws RangeError if a frame count, channel count or offset is not an integer.
|
|
19
21
|
*/
|
|
20
22
|
export function buildCaptureConfig(
|
|
21
23
|
options: CaptureOptions,
|
|
22
24
|
defaultChannels: number,
|
|
23
25
|
): Omit<SonareEngineSyncCaptureMessage, 'type'> {
|
|
24
26
|
return {
|
|
25
|
-
bufferFrames
|
|
26
|
-
|
|
27
|
+
// bufferFrames has no default; NaN makes the guard refuse an absent value.
|
|
28
|
+
bufferFrames: requireIntegerOption(options.bufferFrames, Number.NaN, 'bufferFrames', 1),
|
|
29
|
+
channels: requireChannelCount(options.channels, defaultChannels),
|
|
27
30
|
source: options.source ?? 'output',
|
|
28
|
-
recordOffsetSamples:
|
|
31
|
+
recordOffsetSamples: requireInteger(options.recordOffsetSamples, 0, 'recordOffsetSamples'),
|
|
29
32
|
inputMonitor: {
|
|
30
33
|
enabled: Boolean(options.inputMonitor?.enabled),
|
|
31
34
|
gain: options.inputMonitor?.gain ?? 1,
|
|
@@ -111,7 +114,7 @@ export function normalizeTrackLanes(
|
|
|
111
114
|
const ids: number[] = [];
|
|
112
115
|
for (const entry of entries) {
|
|
113
116
|
if (!Number.isInteger(entry.trackId) || entry.trackId <= 0) {
|
|
114
|
-
throw new
|
|
117
|
+
throw new RangeError(`Invalid track id for mixer lane: ${String(entry.trackId)}`);
|
|
115
118
|
}
|
|
116
119
|
ids.push(entry.trackId);
|
|
117
120
|
}
|
|
@@ -145,12 +148,15 @@ export function resolveMarkerSet(
|
|
|
145
148
|
const seen = new Set<number>();
|
|
146
149
|
let counter = nextMarkerId;
|
|
147
150
|
for (const marker of markers) {
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
// Non-negative as well as finite, which is the domain the native setter
|
|
152
|
+
// enforces; checking only finiteness here hands a negative ppq on to be
|
|
153
|
+
// refused a layer down, under that layer's name for the argument.
|
|
154
|
+
if (!Number.isFinite(marker.ppq) || marker.ppq < 0) {
|
|
155
|
+
throw new RangeError(`Invalid marker ppq: ${String(marker.ppq)}`);
|
|
150
156
|
}
|
|
151
157
|
if (marker.id !== undefined) {
|
|
152
158
|
if (!Number.isInteger(marker.id) || marker.id <= 0) {
|
|
153
|
-
throw new
|
|
159
|
+
throw new RangeError(`Invalid marker id: ${String(marker.id)}`);
|
|
154
160
|
}
|
|
155
161
|
if (seen.has(marker.id)) {
|
|
156
162
|
throw new Error(`Duplicate marker id: ${marker.id}`);
|
|
@@ -190,6 +190,27 @@ export function resolveBusInsertAutomationId(
|
|
|
190
190
|
return ctx.offlineEngine.resolveBusInsertAutomationId(busId, insertIndex, paramName);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Resolves a hosted instrument's continuous parameter to its reserved
|
|
195
|
+
* instrument-automation id, so an instrument parameter is driven from a
|
|
196
|
+
* breakpoint lane at audio-block precision like a strip insert.
|
|
197
|
+
*
|
|
198
|
+
* The instrument must already be bound on the offline engine for the
|
|
199
|
+
* destination (the worklet mirrors every instrument sync onto it), otherwise
|
|
200
|
+
* the resolve returns -1.
|
|
201
|
+
*
|
|
202
|
+
* @param destinationId MIDI destination the instrument is bound to.
|
|
203
|
+
* @param paramName Instrument JSON-key parameter name (e.g. `cutoffHz`).
|
|
204
|
+
* @returns Reserved instrument-automation id, or -1 when destination/key unknown.
|
|
205
|
+
*/
|
|
206
|
+
export function resolveInstrumentAutomationId(
|
|
207
|
+
ctx: EngineParameterContext,
|
|
208
|
+
destinationId: number,
|
|
209
|
+
paramName: string,
|
|
210
|
+
): number {
|
|
211
|
+
return ctx.offlineEngine.resolveInstrumentAutomationId(destinationId, paramName);
|
|
212
|
+
}
|
|
213
|
+
|
|
193
214
|
/**
|
|
194
215
|
* Returns the number of automation lanes installed on the engine, including
|
|
195
216
|
* lanes whose breakpoint list is currently empty.
|