@libraz/libsonare 1.7.2 → 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 -203
- 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 -5840
- 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 -7431
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3758 -1362
- 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 -2515
- package/dist/worklet.d.ts.map +1 -0
- package/dist/worklet.js +2763 -501
- 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 +282 -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 +61 -24
- package/src/project_class.ts +450 -27
- package/src/project_internal.ts +149 -42
- package/src/project_synth.ts +67 -1
- package/src/project_types.ts +268 -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 +711 -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 +1122 -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 +194 -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 +78 -26
- package/src/worklet/engine-offline.ts +14 -8
- package/src/worklet/engine-parameter-facade.ts +21 -0
- package/src/worklet/engine-processor.ts +272 -83
- 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 +227 -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 +17 -0
- package/src/effects_transform.ts +0 -718
- package/src/mastering_repair.ts +0 -273
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { EngineClip } from '../index';
|
|
2
2
|
import { RealtimeEngine } from '../index';
|
|
3
|
-
import type
|
|
3
|
+
import { copyPlanesToOutput, type WorkletInput, type WorkletOutput } from './audio_types';
|
|
4
|
+
import { emptyStripJson } from './engine-mixer-facade';
|
|
5
|
+
import { requireChannelCount, requireIntegerOption } from './guards';
|
|
4
6
|
import {
|
|
5
7
|
DEFAULT_METRONOME_CONFIG,
|
|
6
8
|
type ResolvedMetronomeConfig,
|
|
@@ -39,37 +41,12 @@ import {
|
|
|
39
41
|
type SonareEngineTelemetryRecord,
|
|
40
42
|
type SonareEngineTelemetryRingBuffer,
|
|
41
43
|
SonareEngineTelemetryType,
|
|
42
|
-
type SonareWorkletMeterSnapshot,
|
|
43
44
|
scopeRingFromSharedBuffer,
|
|
44
45
|
telemetryFromEngine,
|
|
45
46
|
writeInt64Words,
|
|
46
47
|
writeSonareEngineTelemetryRingBuffer,
|
|
47
48
|
} from './protocol';
|
|
48
49
|
|
|
49
|
-
/**
|
|
50
|
-
* Copies one plane per output channel, zero-filling the tail past `frames` and
|
|
51
|
-
* any channel the source does not cover. Shared by the program and cue outputs
|
|
52
|
-
* so the two cannot drift in their padding behaviour. Allocation-free.
|
|
53
|
-
*/
|
|
54
|
-
function copyPlanesToOutput(
|
|
55
|
-
output: Float32Array[],
|
|
56
|
-
planes: readonly Float32Array[],
|
|
57
|
-
frames: number,
|
|
58
|
-
): void {
|
|
59
|
-
for (let ch = 0; ch < output.length; ch++) {
|
|
60
|
-
const target = output[ch];
|
|
61
|
-
const source = planes[ch] ?? planes[0];
|
|
62
|
-
if (source) {
|
|
63
|
-
target.set(source.subarray(0, Math.min(target.length, frames)));
|
|
64
|
-
if (target.length > frames) {
|
|
65
|
-
target.fill(0, frames);
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
target.fill(0);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
50
|
function captureTransferList(channels: readonly Float32Array[]): Transferable[] {
|
|
74
51
|
const transfers: ArrayBuffer[] = [];
|
|
75
52
|
const seen = new Set<ArrayBuffer>();
|
|
@@ -141,9 +118,15 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
141
118
|
) {
|
|
142
119
|
this.sampleRate = options.sampleRate ?? 48000;
|
|
143
120
|
this.blockSize = options.blockSize ?? 128;
|
|
144
|
-
this.channelCount =
|
|
121
|
+
this.channelCount = requireChannelCount(options.channelCount, 2);
|
|
145
122
|
this.transport = transport;
|
|
146
|
-
|
|
123
|
+
// Zero is the off switch for meter publication, so the floor is 0.
|
|
124
|
+
this.meterIntervalFrames = requireIntegerOption(
|
|
125
|
+
options.meterIntervalFrames,
|
|
126
|
+
2048,
|
|
127
|
+
'meterIntervalFrames',
|
|
128
|
+
0,
|
|
129
|
+
);
|
|
147
130
|
this.commandRing = options.commandSharedBuffer
|
|
148
131
|
? this.commandRingFromSharedBuffer(options.commandSharedBuffer, options.commandRingCapacity)
|
|
149
132
|
: undefined;
|
|
@@ -175,10 +158,13 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
175
158
|
options.externalMidiRingCapacity,
|
|
176
159
|
)
|
|
177
160
|
: undefined;
|
|
161
|
+
if (this.commandRing && this.commandRing.capacity > 65536) {
|
|
162
|
+
throw new RangeError('commandRingCapacity must be at most 65536');
|
|
163
|
+
}
|
|
178
164
|
this.engine = new RealtimeEngine(
|
|
179
165
|
this.sampleRate,
|
|
180
166
|
this.blockSize,
|
|
181
|
-
1024,
|
|
167
|
+
Math.max(1024, this.commandRing?.capacity ?? 0),
|
|
182
168
|
1024,
|
|
183
169
|
this.channelCount,
|
|
184
170
|
);
|
|
@@ -201,7 +187,13 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
201
187
|
// band count follows the ring's record layout so writeScopeRing never
|
|
202
188
|
// overruns its slot.
|
|
203
189
|
if (this.scopeRing) {
|
|
204
|
-
|
|
190
|
+
// Zero is the engine's own off switch for the scope tap, so the floor is 0.
|
|
191
|
+
const interval = requireIntegerOption(
|
|
192
|
+
options.scopeIntervalFrames,
|
|
193
|
+
this.blockSize,
|
|
194
|
+
'scopeIntervalFrames',
|
|
195
|
+
0,
|
|
196
|
+
);
|
|
205
197
|
this.engine.configureScopeTelemetry(interval, this.scopeRing.bands);
|
|
206
198
|
}
|
|
207
199
|
}
|
|
@@ -395,6 +387,14 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
395
387
|
}
|
|
396
388
|
break;
|
|
397
389
|
}
|
|
390
|
+
case 'syncClipPagePrefetchFrames': {
|
|
391
|
+
this.engine.setClipPagePrefetchFrames(message.frames);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case 'syncWarpVoiceCapacity': {
|
|
395
|
+
this.engine.setWarpVoiceCapacity(message.voices);
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
398
|
case 'syncClipPageCommit': {
|
|
399
399
|
const providerId = this.pagedClipProviders.get(message.clipId);
|
|
400
400
|
const clip = message.clip ?? this.pendingPagedClips.get(message.clipId);
|
|
@@ -455,11 +455,69 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
455
455
|
);
|
|
456
456
|
}
|
|
457
457
|
break;
|
|
458
|
-
case 'syncMixer':
|
|
458
|
+
case 'syncMixer': {
|
|
459
|
+
if (message.sidechainDelta === true) {
|
|
460
|
+
// Sidechain setters send a binding delta through this message type.
|
|
461
|
+
// Their `lanes` field is context, not a request to rebuild routing;
|
|
462
|
+
// rebuilding it here would discard live insert smoother slots.
|
|
463
|
+
for (const binding of message.laneSidechains ?? []) {
|
|
464
|
+
this.engine.setLaneSidechain(
|
|
465
|
+
binding.trackId,
|
|
466
|
+
binding.insertIndex,
|
|
467
|
+
binding.sourceTrackId,
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
for (const binding of message.busSidechains ?? []) {
|
|
471
|
+
this.engine.setBusSidechain(
|
|
472
|
+
binding.busId,
|
|
473
|
+
binding.insertIndex,
|
|
474
|
+
binding.sourceKind,
|
|
475
|
+
binding.sourceId,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
for (const binding of message.masterSidechains ?? []) {
|
|
479
|
+
this.engine.setMasterSidechain(
|
|
480
|
+
binding.insertIndex,
|
|
481
|
+
binding.sourceKind,
|
|
482
|
+
binding.sourceId,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
// SAB commands are published before the control thread posts this
|
|
488
|
+
// structural sync. Drain the whole snapshot so a future command past
|
|
489
|
+
// the normal 64-per-render budget cannot enter native after the strip
|
|
490
|
+
// replacement has invalidated its target.
|
|
491
|
+
if (this.commandRing) {
|
|
492
|
+
const write = Atomics.load(this.commandRing.header, 0);
|
|
493
|
+
const read = Atomics.load(this.commandRing.header, 1);
|
|
494
|
+
this.drainCommands(Math.min(this.commandRing.capacity, Math.max(0, write - read)));
|
|
495
|
+
}
|
|
496
|
+
// Complete earlier immediate edits in message order. Scheduled
|
|
497
|
+
// commands remain queued for their render frame.
|
|
498
|
+
this.engine.applyCommandsDueNowPreservingFuture();
|
|
499
|
+
this.engine.settleInsertParameters();
|
|
459
500
|
if (message.buses) {
|
|
460
501
|
this.engine.setTrackBuses(message.buses);
|
|
461
502
|
}
|
|
462
503
|
this.engine.setTrackLanes(message.lanes);
|
|
504
|
+
for (const target of message.forceInsertResets ?? []) {
|
|
505
|
+
const emptyJson = emptyStripJson(target);
|
|
506
|
+
switch (target.kind) {
|
|
507
|
+
case 'track':
|
|
508
|
+
this.engine.clearTrackInsertParameterBases(target.trackId);
|
|
509
|
+
this.engine.setTrackStripJson(target.trackId, emptyJson);
|
|
510
|
+
break;
|
|
511
|
+
case 'bus':
|
|
512
|
+
this.engine.clearBusInsertParameterBases(target.busId);
|
|
513
|
+
this.engine.setBusStripJson(target.busId, emptyJson);
|
|
514
|
+
break;
|
|
515
|
+
case 'master':
|
|
516
|
+
this.engine.clearMasterInsertParameterBases();
|
|
517
|
+
this.engine.setMasterStripJson(emptyJson);
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
463
521
|
for (const strip of message.trackStrips ?? []) {
|
|
464
522
|
this.engine.setTrackStripJson(strip.trackId, strip.sceneJson);
|
|
465
523
|
}
|
|
@@ -472,7 +530,46 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
472
530
|
for (const binding of message.laneSidechains ?? []) {
|
|
473
531
|
this.engine.setLaneSidechain(binding.trackId, binding.insertIndex, binding.sourceTrackId);
|
|
474
532
|
}
|
|
533
|
+
for (const binding of message.busSidechains ?? []) {
|
|
534
|
+
this.engine.setBusSidechain(
|
|
535
|
+
binding.busId,
|
|
536
|
+
binding.insertIndex,
|
|
537
|
+
binding.sourceKind,
|
|
538
|
+
binding.sourceId,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
for (const binding of message.masterSidechains ?? []) {
|
|
542
|
+
this.engine.setMasterSidechain(binding.insertIndex, binding.sourceKind, binding.sourceId);
|
|
543
|
+
}
|
|
544
|
+
for (const override of message.insertParamOverrides ?? []) {
|
|
545
|
+
switch (override.kind) {
|
|
546
|
+
case 'track':
|
|
547
|
+
this.engine.restoreTrackStripInsertParamByName(
|
|
548
|
+
override.trackId,
|
|
549
|
+
override.insertIndex,
|
|
550
|
+
override.paramName,
|
|
551
|
+
override.value,
|
|
552
|
+
);
|
|
553
|
+
break;
|
|
554
|
+
case 'bus':
|
|
555
|
+
this.engine.restoreBusStripInsertParamByName(
|
|
556
|
+
override.busId,
|
|
557
|
+
override.insertIndex,
|
|
558
|
+
override.paramName,
|
|
559
|
+
override.value,
|
|
560
|
+
);
|
|
561
|
+
break;
|
|
562
|
+
case 'master':
|
|
563
|
+
this.engine.restoreMasterStripInsertParamByName(
|
|
564
|
+
override.insertIndex,
|
|
565
|
+
override.paramName,
|
|
566
|
+
override.value,
|
|
567
|
+
);
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
475
571
|
break;
|
|
572
|
+
}
|
|
476
573
|
case 'syncCapture':
|
|
477
574
|
this.engine.setCaptureBuffer(message.channels, message.bufferFrames);
|
|
478
575
|
this.engine.setCaptureSource(message.source);
|
|
@@ -485,6 +582,9 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
485
582
|
case 'syncMasterStripEqBand':
|
|
486
583
|
this.engine.setMasterStripEqBandJson(message.bandIndex, message.bandJson);
|
|
487
584
|
break;
|
|
585
|
+
case 'syncBusStripEqBand':
|
|
586
|
+
this.engine.setBusStripEqBandJson(message.busId, message.bandIndex, message.bandJson);
|
|
587
|
+
break;
|
|
488
588
|
case 'syncTrackStripInsertBypassed':
|
|
489
589
|
this.engine.setTrackStripInsertBypassed(
|
|
490
590
|
message.trackId,
|
|
@@ -501,26 +601,65 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
501
601
|
);
|
|
502
602
|
break;
|
|
503
603
|
case 'syncTrackStripInsertParamByName':
|
|
504
|
-
this.engine.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
604
|
+
this.engine.applyCommandsDueNowPreservingFuture();
|
|
605
|
+
if (
|
|
606
|
+
!this.engine.applyTrackStripInsertParamByNameNow(
|
|
607
|
+
message.trackId,
|
|
608
|
+
message.insertIndex,
|
|
609
|
+
message.paramName,
|
|
610
|
+
message.value,
|
|
611
|
+
)
|
|
612
|
+
) {
|
|
613
|
+
// A burst can exhaust the fixed smoother slots before the next
|
|
614
|
+
// render quantum. Apply the last knob value exactly in that case.
|
|
615
|
+
this.engine.restoreTrackStripInsertParamByName(
|
|
616
|
+
message.trackId,
|
|
617
|
+
message.insertIndex,
|
|
618
|
+
message.paramName,
|
|
619
|
+
message.value,
|
|
620
|
+
);
|
|
621
|
+
}
|
|
510
622
|
break;
|
|
511
623
|
case 'syncMasterStripInsertParamByName':
|
|
512
|
-
this.engine.
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
624
|
+
this.engine.applyCommandsDueNowPreservingFuture();
|
|
625
|
+
if (
|
|
626
|
+
!this.engine.applyMasterStripInsertParamByNameNow(
|
|
627
|
+
message.insertIndex,
|
|
628
|
+
message.paramName,
|
|
629
|
+
message.value,
|
|
630
|
+
)
|
|
631
|
+
) {
|
|
632
|
+
this.engine.restoreMasterStripInsertParamByName(
|
|
633
|
+
message.insertIndex,
|
|
634
|
+
message.paramName,
|
|
635
|
+
message.value,
|
|
636
|
+
);
|
|
637
|
+
}
|
|
517
638
|
break;
|
|
518
639
|
case 'syncBusStripInsertParamByName':
|
|
519
|
-
this.engine.
|
|
640
|
+
this.engine.applyCommandsDueNowPreservingFuture();
|
|
641
|
+
if (
|
|
642
|
+
!this.engine.applyBusStripInsertParamByNameNow(
|
|
643
|
+
message.busId,
|
|
644
|
+
message.insertIndex,
|
|
645
|
+
message.paramName,
|
|
646
|
+
message.value,
|
|
647
|
+
)
|
|
648
|
+
) {
|
|
649
|
+
this.engine.restoreBusStripInsertParamByName(
|
|
650
|
+
message.busId,
|
|
651
|
+
message.insertIndex,
|
|
652
|
+
message.paramName,
|
|
653
|
+
message.value,
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
case 'syncBusStripInsertBypassed':
|
|
658
|
+
this.engine.setBusStripInsertBypassed(
|
|
520
659
|
message.busId,
|
|
521
660
|
message.insertIndex,
|
|
522
|
-
message.
|
|
523
|
-
message.
|
|
661
|
+
message.bypassed,
|
|
662
|
+
message.resetOnBypass,
|
|
524
663
|
);
|
|
525
664
|
break;
|
|
526
665
|
case 'syncTrackStripPan':
|
|
@@ -535,6 +674,18 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
535
674
|
case 'syncTrackStripDualPan':
|
|
536
675
|
this.engine.setTrackStripDualPan(message.trackId, message.leftPan, message.rightPan);
|
|
537
676
|
break;
|
|
677
|
+
case 'syncBusStripPan':
|
|
678
|
+
this.engine.setBusStripPan(message.busId, message.pan);
|
|
679
|
+
break;
|
|
680
|
+
case 'syncBusStripPanLaw':
|
|
681
|
+
this.engine.setBusStripPanLaw(message.busId, message.panLaw);
|
|
682
|
+
break;
|
|
683
|
+
case 'syncBusStripPanMode':
|
|
684
|
+
this.engine.setBusStripPanMode(message.busId, message.panMode);
|
|
685
|
+
break;
|
|
686
|
+
case 'syncBusStripDualPan':
|
|
687
|
+
this.engine.setBusStripDualPan(message.busId, message.leftPan, message.rightPan);
|
|
688
|
+
break;
|
|
538
689
|
case 'syncTrackStripChannelDelaySamples':
|
|
539
690
|
this.engine.setTrackStripChannelDelaySamples(message.trackId, message.delaySamples);
|
|
540
691
|
break;
|
|
@@ -586,8 +737,36 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
586
737
|
message.renderFrame,
|
|
587
738
|
);
|
|
588
739
|
break;
|
|
740
|
+
case 'syncMidiPitchBend':
|
|
741
|
+
this.engine.pushMidiPitchBend(
|
|
742
|
+
message.destinationId,
|
|
743
|
+
message.group,
|
|
744
|
+
message.channel,
|
|
745
|
+
message.data0,
|
|
746
|
+
message.renderFrame,
|
|
747
|
+
);
|
|
748
|
+
break;
|
|
749
|
+
case 'syncMidiChannelPressure':
|
|
750
|
+
this.engine.pushMidiChannelPressure(
|
|
751
|
+
message.destinationId,
|
|
752
|
+
message.group,
|
|
753
|
+
message.channel,
|
|
754
|
+
message.data0,
|
|
755
|
+
message.renderFrame,
|
|
756
|
+
);
|
|
757
|
+
break;
|
|
758
|
+
case 'syncMidiPolyPressure':
|
|
759
|
+
this.engine.pushMidiPolyPressure(
|
|
760
|
+
message.destinationId,
|
|
761
|
+
message.group,
|
|
762
|
+
message.channel,
|
|
763
|
+
message.data0,
|
|
764
|
+
message.data1,
|
|
765
|
+
message.renderFrame,
|
|
766
|
+
);
|
|
767
|
+
break;
|
|
589
768
|
case 'syncMidiUmp':
|
|
590
|
-
this.engine.pushMidiUmp(message.destinationId, message.word0, message.renderFrame);
|
|
769
|
+
this.engine.pushMidiUmp(message.destinationId, [message.word0], message.renderFrame);
|
|
591
770
|
break;
|
|
592
771
|
case 'syncMidiSysex':
|
|
593
772
|
this.engine.pushMidiSysex(message.destinationId, message.data, message.renderFrame);
|
|
@@ -640,6 +819,31 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
640
819
|
message.portTimeSamples,
|
|
641
820
|
);
|
|
642
821
|
break;
|
|
822
|
+
case 'syncMidiInputPitchBend':
|
|
823
|
+
this.engine.pushMidiInputPitchBend(
|
|
824
|
+
message.group,
|
|
825
|
+
message.channel,
|
|
826
|
+
message.data0,
|
|
827
|
+
message.portTimeSamples,
|
|
828
|
+
);
|
|
829
|
+
break;
|
|
830
|
+
case 'syncMidiInputChannelPressure':
|
|
831
|
+
this.engine.pushMidiInputChannelPressure(
|
|
832
|
+
message.group,
|
|
833
|
+
message.channel,
|
|
834
|
+
message.data0,
|
|
835
|
+
message.portTimeSamples,
|
|
836
|
+
);
|
|
837
|
+
break;
|
|
838
|
+
case 'syncMidiInputPolyPressure':
|
|
839
|
+
this.engine.pushMidiInputPolyPressure(
|
|
840
|
+
message.group,
|
|
841
|
+
message.channel,
|
|
842
|
+
message.data0,
|
|
843
|
+
message.data1,
|
|
844
|
+
message.portTimeSamples,
|
|
845
|
+
);
|
|
846
|
+
break;
|
|
643
847
|
}
|
|
644
848
|
}
|
|
645
849
|
|
|
@@ -727,11 +931,11 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
727
931
|
}
|
|
728
932
|
}
|
|
729
933
|
|
|
730
|
-
private drainCommands(): void {
|
|
934
|
+
private drainCommands(limit = 64): void {
|
|
731
935
|
if (!this.commandRing) {
|
|
732
936
|
return;
|
|
733
937
|
}
|
|
734
|
-
for (let i = 0; i <
|
|
938
|
+
for (let i = 0; i < limit; i++) {
|
|
735
939
|
const command = popSonareEngineCommandRingBuffer(this.commandRing);
|
|
736
940
|
if (!command) {
|
|
737
941
|
return;
|
|
@@ -947,14 +1151,15 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
947
1151
|
if (meter.frame !== this.lastMeterFrame) {
|
|
948
1152
|
this.lastMeterFrame = meter.frame;
|
|
949
1153
|
}
|
|
950
|
-
//
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
//
|
|
954
|
-
|
|
955
|
-
|
|
1154
|
+
// The ring branch returned above, so this is the structured-clone
|
|
1155
|
+
// fallback. `onMeter` and `postMessage` are alternative channels for the
|
|
1156
|
+
// same record, not a broadcast pair: the AudioWorklet registration
|
|
1157
|
+
// resolves BOTH to `port.postMessage`, so calling them in turn delivered
|
|
1158
|
+
// every record twice and doubled the host's peak-hold decay rate. Take
|
|
1159
|
+
// the first channel the transport offers.
|
|
1160
|
+
if (this.transport?.onMeter) {
|
|
1161
|
+
this.transport.onMeter(meter);
|
|
956
1162
|
} else {
|
|
957
|
-
this.transport?.onMeter?.(meter);
|
|
958
1163
|
this.transport?.postMessage?.(meter);
|
|
959
1164
|
}
|
|
960
1165
|
}
|
|
@@ -973,34 +1178,18 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
973
1178
|
Atomics.store(ring.header, 0, writeIndex + 1);
|
|
974
1179
|
}
|
|
975
1180
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
ring.records[offset + 5] = meter.rmsDbL;
|
|
989
|
-
ring.records[offset + 6] = meter.rmsDbR;
|
|
990
|
-
ring.records[offset + 7] = meter.correlation;
|
|
991
|
-
ring.records[offset + 8] = meter.truePeakDbL;
|
|
992
|
-
ring.records[offset + 9] = meter.truePeakDbR;
|
|
993
|
-
ring.records[offset + 10] = meter.momentaryLufs;
|
|
994
|
-
ring.records[offset + 11] = meter.shortTermLufs;
|
|
995
|
-
ring.records[offset + 12] = meter.integratedLufs;
|
|
996
|
-
ring.records[offset + 13] = meter.gainReductionDb;
|
|
997
|
-
Atomics.store(ring.header, 0, writeIndex + 1);
|
|
998
|
-
// writeIndex is a free-running monotonic counter, so an overflow guard here
|
|
999
|
-
// would fire on essentially every write past the first `capacity` records
|
|
1000
|
-
// and store an ever-growing value, not a dropped-record count. Readers
|
|
1001
|
-
// already detect silent overrun via firstReadable = max(readIndex,
|
|
1002
|
-
// writeIndex - capacity), so header slot 3 is left at its initial 0.
|
|
1003
|
-
}
|
|
1181
|
+
// A snapshot-object ring writer used to live here as well. It was
|
|
1182
|
+
// unreachable: publishMeters returns after the writeMeterScratch loop
|
|
1183
|
+
// whenever a ring exists, so the ring is never fed from a materialised
|
|
1184
|
+
// snapshot. writeMeterScratch above is the only ring writer, and it keeps the
|
|
1185
|
+
// render callback allocation-free by reading the engine's scratch registers
|
|
1186
|
+
// directly.
|
|
1187
|
+
//
|
|
1188
|
+
// writeIndex is a free-running monotonic counter, so an overflow guard would
|
|
1189
|
+
// fire on essentially every write past the first `capacity` records and store
|
|
1190
|
+
// an ever-growing value, not a dropped-record count. Readers already detect
|
|
1191
|
+
// silent overrun via firstReadable = max(readIndex, writeIndex - capacity),
|
|
1192
|
+
// so header slot 3 is left at its initial 0.
|
|
1004
1193
|
|
|
1005
1194
|
// Drains the engine's scope producer (FFT spectrum + goniometer points) into
|
|
1006
1195
|
// the lock-free SAB scope ring. No allocation on the render path: records are
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isEngineTransportRequestMessage,
|
|
10
10
|
} from './guards';
|
|
11
11
|
import type { SonareRealtimeEngineWorkletProcessorOptions, WorkletPort } from './messages';
|
|
12
|
+
import { isRecord } from './protocol';
|
|
12
13
|
|
|
13
14
|
export function registerSonareRealtimeEngineWorkletProcessor(
|
|
14
15
|
name = 'sonare-realtime-engine-processor',
|
|
@@ -43,15 +44,7 @@ export function registerSonareRealtimeEngineWorkletProcessor(
|
|
|
43
44
|
}
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
this.bridge.receiveCommand(event.data);
|
|
48
|
-
} else if (isEngineSyncMessage(event.data)) {
|
|
49
|
-
this.bridge.receiveSync(event.data);
|
|
50
|
-
} else if (isEngineCaptureRequestMessage(event.data)) {
|
|
51
|
-
this.bridge.receiveCaptureRequest(event.data);
|
|
52
|
-
} else if (isEngineTransportRequestMessage(event.data)) {
|
|
53
|
-
this.bridge.receiveTransportRequest(event.data);
|
|
54
|
-
}
|
|
47
|
+
this.routeMessage(event.data);
|
|
55
48
|
};
|
|
56
49
|
if (port?.addEventListener) {
|
|
57
50
|
port.addEventListener('message', onMessage);
|
|
@@ -72,18 +65,39 @@ export function registerSonareRealtimeEngineWorkletProcessor(
|
|
|
72
65
|
return true;
|
|
73
66
|
}
|
|
74
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Single dispatch point for the port, shared with the buffered replay so
|
|
70
|
+
* the two cannot recognize different message sets.
|
|
71
|
+
*/
|
|
72
|
+
private routeMessage(data: unknown): void {
|
|
73
|
+
const bridge = this.bridge;
|
|
74
|
+
if (!bridge) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (isEngineCommandRecord(data)) {
|
|
78
|
+
bridge.receiveCommand(data);
|
|
79
|
+
} else if (isEngineSyncMessage(data)) {
|
|
80
|
+
bridge.receiveSync(data);
|
|
81
|
+
} else if (isEngineCaptureRequestMessage(data)) {
|
|
82
|
+
bridge.receiveCaptureRequest(data);
|
|
83
|
+
} else if (isEngineTransportRequestMessage(data)) {
|
|
84
|
+
bridge.receiveTransportRequest(data);
|
|
85
|
+
} else if (isRecord(data) && typeof data.type === 'string' && data.type.startsWith('sync')) {
|
|
86
|
+
// A sync the guard does not accept would otherwise vanish, leaving the
|
|
87
|
+
// live engine out of step with the offline mirror and nothing to see.
|
|
88
|
+
// Report it through the same channel as a sync the engine rejected.
|
|
89
|
+
this.port?.postMessage?.({
|
|
90
|
+
type: 'syncError',
|
|
91
|
+
syncType: data.type,
|
|
92
|
+
message: `Unrecognized worklet sync message: ${data.type}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
75
97
|
private replayPendingMessages(): void {
|
|
76
98
|
const messages = this.pendingMessages.splice(0);
|
|
77
99
|
for (const data of messages) {
|
|
78
|
-
|
|
79
|
-
this.bridge?.receiveCommand(data);
|
|
80
|
-
} else if (isEngineSyncMessage(data)) {
|
|
81
|
-
this.bridge?.receiveSync(data);
|
|
82
|
-
} else if (isEngineCaptureRequestMessage(data)) {
|
|
83
|
-
this.bridge?.receiveCaptureRequest(data);
|
|
84
|
-
} else if (isEngineTransportRequestMessage(data)) {
|
|
85
|
-
this.bridge?.receiveTransportRequest(data);
|
|
86
|
-
}
|
|
100
|
+
this.routeMessage(data);
|
|
87
101
|
}
|
|
88
102
|
}
|
|
89
103
|
|