@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
package/src/realtime_engine.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
panLawCode,
|
|
3
|
+
panModeCode,
|
|
4
|
+
sendTimingCode,
|
|
5
|
+
sidechainSourceKindCode,
|
|
6
|
+
trackMonitorModeCode,
|
|
7
|
+
} from './codes';
|
|
2
8
|
import { ErrorCode, SonareError } from './errors';
|
|
3
9
|
import { getSonareModule } from './module_state';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
10
|
+
import type {
|
|
11
|
+
Articulation,
|
|
12
|
+
ControllerBinding,
|
|
13
|
+
MpeDimension,
|
|
14
|
+
NoteTracking,
|
|
15
|
+
ProjectMidiCcBinding,
|
|
16
|
+
SynthPatch,
|
|
17
|
+
} from './project';
|
|
18
|
+
import { normalizeSynthInstrument } from './project_internal';
|
|
19
|
+
import type {
|
|
20
|
+
EqBand,
|
|
21
|
+
PanLawInput,
|
|
22
|
+
PanMode,
|
|
23
|
+
SendTiming,
|
|
24
|
+
SidechainSourceKind,
|
|
25
|
+
UmpWords,
|
|
26
|
+
} from './public_types';
|
|
6
27
|
import type {
|
|
7
28
|
WasmClipPageRequest,
|
|
8
29
|
WasmEngineAutomationPoint,
|
|
@@ -94,15 +115,37 @@ export interface EngineBus {
|
|
|
94
115
|
* to stereo.
|
|
95
116
|
*/
|
|
96
117
|
channelLayout?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Bus this bus's output sums into instead of the master mix (bus-to-bus
|
|
120
|
+
* routing); 0 or absent keeps it on the master mix.
|
|
121
|
+
*/
|
|
122
|
+
outputBusId?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Sends to other buses, in the same shape as a track lane's sends. A
|
|
125
|
+
* pre-fader send taps before `gainDb`, a post-fader one after it.
|
|
126
|
+
*/
|
|
127
|
+
sends?: EngineTrackSend[];
|
|
97
128
|
}
|
|
98
129
|
|
|
99
130
|
export interface EngineMidiEvent {
|
|
100
|
-
|
|
131
|
+
/** Absolute render frame for this event. Default `0`. */
|
|
132
|
+
renderFrame?: number;
|
|
101
133
|
word0?: number;
|
|
102
134
|
word1?: number;
|
|
103
135
|
word2?: number;
|
|
104
136
|
word3?: number;
|
|
105
137
|
wordCount?: number;
|
|
138
|
+
/**
|
|
139
|
+
* Redundant with `word0`, which already carries the UMP group in bits 24..27.
|
|
140
|
+
* The engine reads the group from `word0` — the form that reaches a device or
|
|
141
|
+
* a file — so packing it there is sufficient and a value here that contradicts
|
|
142
|
+
* `word0` is ignored. Must still be in `[0, 15]`; anything else is rejected as
|
|
143
|
+
* a malformed event. Default `0`.
|
|
144
|
+
*
|
|
145
|
+
* Utility (`word0` type nibble `0x0`) and UMP Stream (`0xF`) messages have no
|
|
146
|
+
* group field — those bits are Reserved and `form`/`status` respectively — so
|
|
147
|
+
* they always read as group `0` and packing a group into them has no effect.
|
|
148
|
+
*/
|
|
106
149
|
group?: number;
|
|
107
150
|
sysexHandle?: number;
|
|
108
151
|
data0?: number;
|
|
@@ -119,6 +162,20 @@ export interface EngineMidiClipSchedule {
|
|
|
119
162
|
loop?: boolean;
|
|
120
163
|
loopLengthSamples?: number;
|
|
121
164
|
events: EngineMidiEvent[];
|
|
165
|
+
/**
|
|
166
|
+
* Linear gain applied to the destination instrument's rendered audio while
|
|
167
|
+
* this clip is the most recently started active clip on it. Absent defaults
|
|
168
|
+
* to `1` (unity).
|
|
169
|
+
*/
|
|
170
|
+
gain?: number;
|
|
171
|
+
/**
|
|
172
|
+
* Linear fade lengths over the clip's full length (not per internal loop
|
|
173
|
+
* repeat). Absent defaults to `0` (no fade). `fadeOutSamples` above `0` is
|
|
174
|
+
* rejected when `lengthSamples` is absent or `<= 0` (open-ended): an
|
|
175
|
+
* open-ended clip has no end to fade out towards.
|
|
176
|
+
*/
|
|
177
|
+
fadeInSamples?: number;
|
|
178
|
+
fadeOutSamples?: number;
|
|
122
179
|
}
|
|
123
180
|
|
|
124
181
|
export const EXPECTED_ENGINE_ABI_VERSION = 3;
|
|
@@ -131,6 +188,76 @@ export interface MidiCcBindOptions {
|
|
|
131
188
|
maxValue?: number;
|
|
132
189
|
}
|
|
133
190
|
|
|
191
|
+
/** Request form of {@link RealtimeEngine.renderOffline}. */
|
|
192
|
+
export interface RenderOfflineRequest {
|
|
193
|
+
/** One buffer per output plane; their common length is the render span. */
|
|
194
|
+
channels: Float32Array[];
|
|
195
|
+
/** Render block size. Default `128`. */
|
|
196
|
+
blockSize?: number;
|
|
197
|
+
/**
|
|
198
|
+
* Whether this call ends the timeline. `true` (the default, and what a
|
|
199
|
+
* one-shot bounce wants) releases every sounding note and flushes the PDC /
|
|
200
|
+
* alignment delay lines before returning. `false` renders one CHUNK of a
|
|
201
|
+
* longer timeline: a note held across the chunk boundary keeps sounding into
|
|
202
|
+
* the next call and the delay lines carry their history over, so consecutive
|
|
203
|
+
* chunks concatenate to exactly what one continuous render of the same span
|
|
204
|
+
* produces. Call {@link RealtimeEngine.finishOfflineRender} once after the
|
|
205
|
+
* last chunk.
|
|
206
|
+
*
|
|
207
|
+
* Sample-exact concatenation requires every chunk to use the same `blockSize`
|
|
208
|
+
* and a frame count that is a whole number of blocks: each call restarts the
|
|
209
|
+
* block grid at its own frame 0 and renders a short final block for the
|
|
210
|
+
* remainder, and the clip / automation / MIDI-clip snapshots are frozen once
|
|
211
|
+
* per block, so a chunk that ends mid-block shifts every later block
|
|
212
|
+
* boundary. Audio stays continuous either way; only bit-identity is lost.
|
|
213
|
+
*/
|
|
214
|
+
finalize?: boolean;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const UMP_WORD_MIN = -0x80000000;
|
|
218
|
+
const UMP_WORD_MAX = 0xffffffff;
|
|
219
|
+
|
|
220
|
+
// A word may be spelled `(0x4 << 28) | …`, which is a signed int once bit 31 is
|
|
221
|
+
// set, so the signed 32-bit range is accepted alongside the unsigned one.
|
|
222
|
+
function assertUmpWords(fnName: string, words: UmpWords): UmpWords {
|
|
223
|
+
if (!(words instanceof Uint32Array) && !Array.isArray(words)) {
|
|
224
|
+
throw new TypeError(`${fnName}: words must be a Uint32Array or a number array`);
|
|
225
|
+
}
|
|
226
|
+
if (words.length < 1 || words.length > 4) {
|
|
227
|
+
throw new RangeError(`${fnName}: words must hold 1 to 4 words`);
|
|
228
|
+
}
|
|
229
|
+
for (let i = 0; i < words.length; i++) {
|
|
230
|
+
const word = words[i];
|
|
231
|
+
if (
|
|
232
|
+
typeof word !== 'number' ||
|
|
233
|
+
!Number.isInteger(word) ||
|
|
234
|
+
word < UMP_WORD_MIN ||
|
|
235
|
+
word > UMP_WORD_MAX
|
|
236
|
+
) {
|
|
237
|
+
throw new RangeError(`${fnName}: words[${i}] must be an integer 32-bit word`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return words;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* One normalizer for both call forms, so the request object and the positional
|
|
245
|
+
* overload cannot drift in their defaults.
|
|
246
|
+
*/
|
|
247
|
+
function normalizeRenderOfflineRequest(
|
|
248
|
+
channelsOrRequest: Float32Array[] | RenderOfflineRequest,
|
|
249
|
+
blockSize: number,
|
|
250
|
+
): { channels: Float32Array[]; blockSize: number; finalize: boolean } {
|
|
251
|
+
const request = Array.isArray(channelsOrRequest)
|
|
252
|
+
? { channels: channelsOrRequest, blockSize }
|
|
253
|
+
: channelsOrRequest;
|
|
254
|
+
return {
|
|
255
|
+
channels: request.channels,
|
|
256
|
+
blockSize: request.blockSize ?? 128,
|
|
257
|
+
finalize: request.finalize ?? true,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
134
261
|
export interface EngineCapabilities {
|
|
135
262
|
engineAbiVersion: number;
|
|
136
263
|
expectedEngineAbiVersion: number;
|
|
@@ -162,6 +289,7 @@ export function engineCapabilities(): EngineCapabilities {
|
|
|
162
289
|
|
|
163
290
|
export class RealtimeEngine {
|
|
164
291
|
private native: WasmRealtimeEngine;
|
|
292
|
+
private released = false;
|
|
165
293
|
|
|
166
294
|
constructor(
|
|
167
295
|
sampleRate = 48000,
|
|
@@ -186,6 +314,15 @@ export class RealtimeEngine {
|
|
|
186
314
|
);
|
|
187
315
|
}
|
|
188
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Size the engine's queues and scratch for a sample rate and block size.
|
|
319
|
+
*
|
|
320
|
+
* `commandCapacity` must not exceed 65536 and `telemetryCapacity` must not
|
|
321
|
+
* exceed 16384; a larger value throws and leaves the engine untouched. The
|
|
322
|
+
* telemetry number is not a queue depth paid for one-for-one: the engine
|
|
323
|
+
* reserves that many meter records per metered lane, so its memory cost is
|
|
324
|
+
* far larger than the number given here.
|
|
325
|
+
*/
|
|
189
326
|
prepare(
|
|
190
327
|
sampleRate: number,
|
|
191
328
|
maxBlockSize: number,
|
|
@@ -249,12 +386,16 @@ export class RealtimeEngine {
|
|
|
249
386
|
* scheduled MIDI clips routed to that destination render through the synth.
|
|
250
387
|
* Unknown preset names throw. An object patch's `destinationId` is a JS
|
|
251
388
|
* binding convenience, not part of the NativeSynth patch itself.
|
|
389
|
+
*
|
|
390
|
+
* An `engineMode: 'sample'` patch also carries the {@link SampleBank} its
|
|
391
|
+
* keymap names. The synth takes a share of the bank, so it may be released
|
|
392
|
+
* right after this call; a sample patch bound without one renders silence.
|
|
252
393
|
*/
|
|
253
394
|
setSynthInstrument(
|
|
254
395
|
patch: SynthPatch | string = {},
|
|
255
396
|
destinationId = (typeof patch === 'object' ? patch.destinationId : undefined) ?? 0,
|
|
256
397
|
): void {
|
|
257
|
-
this.native.setSynthInstrument(destinationId, patch);
|
|
398
|
+
this.native.setSynthInstrument(destinationId, normalizeSynthInstrument(patch));
|
|
258
399
|
}
|
|
259
400
|
|
|
260
401
|
/**
|
|
@@ -282,6 +423,8 @@ export class RealtimeEngine {
|
|
|
282
423
|
gain?: number;
|
|
283
424
|
polyphony?: number;
|
|
284
425
|
preferModelForModeledFamilies?: boolean;
|
|
426
|
+
clearBankRig?: boolean;
|
|
427
|
+
gsEfxRealization?: 'modern' | 'classic';
|
|
285
428
|
} = {},
|
|
286
429
|
destinationId = config.destinationId ?? 0,
|
|
287
430
|
): void {
|
|
@@ -329,6 +472,135 @@ export class RealtimeEngine {
|
|
|
329
472
|
return this.native.midiCcBindingCount();
|
|
330
473
|
}
|
|
331
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Replace a destination instrument's controller profile with a named preset
|
|
477
|
+
* (see {@link controllerProfileNames}). Installing a profile drops every
|
|
478
|
+
* channel's accumulated axis values: the new bindings say nothing about what
|
|
479
|
+
* the old ones had reached. An unknown name throws, and so does a destination
|
|
480
|
+
* with no instrument or one whose instrument holds no profile.
|
|
481
|
+
*/
|
|
482
|
+
setControllerProfile(destinationId: number, presetName: string): void {
|
|
483
|
+
this.native.setControllerProfile(destinationId, presetName);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Add one {@link ControllerBinding} on top of the destination's current profile. */
|
|
487
|
+
bindController(destinationId: number, binding: ControllerBinding): void {
|
|
488
|
+
this.native.bindController(destinationId, binding);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Drop every binding of the destination's controller profile. The instrument
|
|
493
|
+
* keeps a profile; it resolves nothing until something is bound again.
|
|
494
|
+
*/
|
|
495
|
+
clearControllerBindings(destinationId: number): void {
|
|
496
|
+
this.native.clearControllerBindings(destinationId);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
controllerBindingCount(destinationId: number): number {
|
|
500
|
+
return this.native.controllerBindingCount(destinationId);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Whether note-on velocity is expression for this instrument. No fixed
|
|
505
|
+
* default is possible — a wind controller ships sending breath-derived
|
|
506
|
+
* velocity on one model and a constant on the next — so each preset states it
|
|
507
|
+
* and a host building its own profile sets it. When false the synth takes
|
|
508
|
+
* every note at full scale and the bound axes carry the dynamics alone.
|
|
509
|
+
*/
|
|
510
|
+
setControllerVelocityMeaningful(destinationId: number, meaningful: boolean): void {
|
|
511
|
+
this.native.setControllerVelocityMeaningful(destinationId, meaningful);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
controllerVelocityMeaningful(destinationId: number): boolean {
|
|
515
|
+
return this.native.controllerVelocityMeaningful(destinationId);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Say which note a value addressed to a whole MIDI channel belongs to when
|
|
520
|
+
* several are sounding on it, for one per-note dimension
|
|
521
|
+
* ({@link MPE_DIMENSIONS}, {@link NOTE_TRACKINGS}).
|
|
522
|
+
*
|
|
523
|
+
* Set per dimension because the useful answers differ: pressure following the
|
|
524
|
+
* newest note while bend reaches every one is a real configuration, not a
|
|
525
|
+
* mistake. MPE poses this question and declines to answer it, so this is a
|
|
526
|
+
* choice rather than a rule — and it is read only inside an MPE zone, and
|
|
527
|
+
* only while more than one note is sounding on the channel, which an MPE
|
|
528
|
+
* sender avoids by giving each note its own member channel.
|
|
529
|
+
*
|
|
530
|
+
* Both arguments are required and are a name or its ordinal; an unknown
|
|
531
|
+
* spelling is refused rather than resolved to a default, as are a destination
|
|
532
|
+
* with no instrument and one whose instrument holds no controller profile.
|
|
533
|
+
*/
|
|
534
|
+
setControllerNoteTracking(
|
|
535
|
+
destinationId: number,
|
|
536
|
+
dimension: MpeDimension | number,
|
|
537
|
+
tracking: NoteTracking | number,
|
|
538
|
+
): void {
|
|
539
|
+
this.native.setControllerNoteTracking(destinationId, dimension, tracking);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Read back {@link setControllerNoteTracking} for one dimension, as the
|
|
544
|
+
* canonical name.
|
|
545
|
+
*/
|
|
546
|
+
controllerNoteTracking(
|
|
547
|
+
destinationId: number,
|
|
548
|
+
dimension: MpeDimension | number,
|
|
549
|
+
): NoteTracking | number {
|
|
550
|
+
return this.native.controllerNoteTracking(destinationId, dimension);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Set how one MIDI channel (0–15) of a destination's instrument treats a
|
|
555
|
+
* note-on while another note on that channel is still held: `'poly'` takes a
|
|
556
|
+
* new voice each time, `'mono-retrigger'` stops and restarts the note (what
|
|
557
|
+
* GS MONO MODE and CC126 mean), `'mono-legato'` carries the sounding voice
|
|
558
|
+
* and only moves its pitch — a wind player's slur, which no MIDI message can
|
|
559
|
+
* reach by design.
|
|
560
|
+
*
|
|
561
|
+
* `'mono-legato'` is a request, not a guarantee: an engine whose exciter is
|
|
562
|
+
* spent at the onset — anything struck or plucked — and a target pitch below
|
|
563
|
+
* what the engine's delay line can hold both fall back to an ordinary note,
|
|
564
|
+
* which {@link legatoFallbackCount} counts. A channel outside [0,15] and an
|
|
565
|
+
* articulation outside the enum are refused rather than clamped, and so is a
|
|
566
|
+
* destination with no instrument or one whose instrument has no articulation
|
|
567
|
+
* of its own.
|
|
568
|
+
*/
|
|
569
|
+
setArticulation(
|
|
570
|
+
destinationId: number,
|
|
571
|
+
channel: number,
|
|
572
|
+
articulation: Articulation | number,
|
|
573
|
+
): void {
|
|
574
|
+
this.native.setArticulation(destinationId, channel, articulation);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Read back {@link setArticulation} as the canonical name. An ordinal this
|
|
579
|
+
* build cannot spell is handed back as the number, the way every other enum
|
|
580
|
+
* leaves this surface.
|
|
581
|
+
*/
|
|
582
|
+
articulation(destinationId: number, channel: number): Articulation | number {
|
|
583
|
+
return this.native.articulation(destinationId, channel);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* How many times a legato continuation was asked for on this destination and
|
|
588
|
+
* refused, so the note started a voice of its own instead. Counted rather
|
|
589
|
+
* than inferred: a refusal sounds like an ordinary note, so nothing in the
|
|
590
|
+
* audio separates "this engine declines legato" from "the mode was never
|
|
591
|
+
* set". Saturates at 4294967295 rather than wrapping — matching the C ABI, so
|
|
592
|
+
* the same phrase reports the same number on every surface — after which it
|
|
593
|
+
* reads as "at least this many".
|
|
594
|
+
*
|
|
595
|
+
* Throws on a destination with no instrument, and on one whose instrument has
|
|
596
|
+
* no articulation of its own — the same two refusals
|
|
597
|
+
* {@link setArticulation} keeps apart. Reporting 0 for the second would read
|
|
598
|
+
* as "every slur took", which is the reading this counter exists to prevent.
|
|
599
|
+
*/
|
|
600
|
+
legatoFallbackCount(destinationId: number): number {
|
|
601
|
+
return this.native.legatoFallbackCount(destinationId);
|
|
602
|
+
}
|
|
603
|
+
|
|
332
604
|
/** Install/replace a live non-destructive MIDI-FX insert for one destination. */
|
|
333
605
|
setMidiFx(destinationId: number, configJson: string): void {
|
|
334
606
|
this.native.setMidiFx(destinationId, configJson);
|
|
@@ -383,6 +655,12 @@ export class RealtimeEngine {
|
|
|
383
655
|
* block / animation frame. `maxRecords` caps the number of output events
|
|
384
656
|
* returned — the shared unit across every surface. Events past the cap stay
|
|
385
657
|
* queued for the next call (lossless); call again to drain the rest.
|
|
658
|
+
*
|
|
659
|
+
* One queued record lowers to at most 4 MIDI 1.0 messages (a MIDI 2.0
|
|
660
|
+
* registered or assignable controller becomes CC 101/100 or 99/98 plus Data
|
|
661
|
+
* Entry 6/38), so a positive `maxRecords` below 4 could never consume a record
|
|
662
|
+
* and is rejected with an `InvalidParameter` `SonareError` instead of
|
|
663
|
+
* returning nothing forever.
|
|
386
664
|
*/
|
|
387
665
|
drainExternalMidi(maxRecords = 1024): WasmExternalMidiEvent[] {
|
|
388
666
|
return this.native.drainExternalMidi(maxRecords);
|
|
@@ -398,7 +676,11 @@ export class RealtimeEngine {
|
|
|
398
676
|
}
|
|
399
677
|
|
|
400
678
|
externalMidiScratchRenderFrame(): number {
|
|
401
|
-
|
|
679
|
+
// embind marshals the int64 render frame as a BigInt; the declared `number`
|
|
680
|
+
// has to be a real number or the first consumer that does arithmetic on it
|
|
681
|
+
// dies with "Cannot mix BigInt". Same normalization as the telemetry, meter
|
|
682
|
+
// and scope scratch frames.
|
|
683
|
+
return Number(this.native.externalMidiScratchRenderFrame());
|
|
402
684
|
}
|
|
403
685
|
|
|
404
686
|
externalMidiScratchByteWord(): number {
|
|
@@ -443,6 +725,50 @@ export class RealtimeEngine {
|
|
|
443
725
|
this.native.pushMidiInputCc(group, channel, controller, value, portTimeSamples);
|
|
444
726
|
}
|
|
445
727
|
|
|
728
|
+
/**
|
|
729
|
+
* Push a live MIDI pitch bend to the engine-owned MIDI input source.
|
|
730
|
+
*
|
|
731
|
+
* `bend14` is unsigned 14-bit with centre 8192 (0..16383) — the dimension is
|
|
732
|
+
* not 7-bit, so a value past 16383 is refused rather than narrowed. The input
|
|
733
|
+
* source must be enabled with {@link setMidiInputSource} first.
|
|
734
|
+
*/
|
|
735
|
+
pushMidiInputPitchBend(
|
|
736
|
+
group: number,
|
|
737
|
+
channel: number,
|
|
738
|
+
bend14: number,
|
|
739
|
+
portTimeSamples = 0,
|
|
740
|
+
): void {
|
|
741
|
+
this.native.pushMidiInputPitchBend(group, channel, bend14, portTimeSamples);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Push a live MIDI channel pressure to the engine-owned MIDI input source.
|
|
746
|
+
* `pressure` is 7-bit (0..127). Under MPE this is the member channel's
|
|
747
|
+
* per-note pressure.
|
|
748
|
+
*/
|
|
749
|
+
pushMidiInputChannelPressure(
|
|
750
|
+
group: number,
|
|
751
|
+
channel: number,
|
|
752
|
+
pressure: number,
|
|
753
|
+
portTimeSamples = 0,
|
|
754
|
+
): void {
|
|
755
|
+
this.native.pushMidiInputChannelPressure(group, channel, pressure, portTimeSamples);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Push a live MIDI polyphonic key pressure to the engine-owned MIDI input
|
|
760
|
+
* source. `note` and `pressure` are 7-bit (0..127).
|
|
761
|
+
*/
|
|
762
|
+
pushMidiInputPolyPressure(
|
|
763
|
+
group: number,
|
|
764
|
+
channel: number,
|
|
765
|
+
note: number,
|
|
766
|
+
pressure: number,
|
|
767
|
+
portTimeSamples = 0,
|
|
768
|
+
): void {
|
|
769
|
+
this.native.pushMidiInputPolyPressure(group, channel, note, pressure, portTimeSamples);
|
|
770
|
+
}
|
|
771
|
+
|
|
446
772
|
pushMidiNoteOn(
|
|
447
773
|
destinationId: number,
|
|
448
774
|
group: number,
|
|
@@ -482,9 +808,76 @@ export class RealtimeEngine {
|
|
|
482
808
|
this.native.pushMidiCc(destinationId, group, channel, controller, value, renderFrame);
|
|
483
809
|
}
|
|
484
810
|
|
|
485
|
-
/**
|
|
486
|
-
|
|
487
|
-
|
|
811
|
+
/**
|
|
812
|
+
* Queue an immediate (live) MIDI pitch bend to a MIDI destination. `bend14`
|
|
813
|
+
* is unsigned 14-bit with centre 8192 (0..16383); `renderFrame` is the frame
|
|
814
|
+
* to fire at, or -1 for immediate. Mirrors the Node/Python/C-ABI
|
|
815
|
+
* `pushMidiPitchBend`.
|
|
816
|
+
*/
|
|
817
|
+
pushMidiPitchBend(
|
|
818
|
+
destinationId: number,
|
|
819
|
+
group: number,
|
|
820
|
+
channel: number,
|
|
821
|
+
bend14: number,
|
|
822
|
+
renderFrame = -1,
|
|
823
|
+
): void {
|
|
824
|
+
this.native.pushMidiPitchBend(destinationId, group, channel, bend14, renderFrame);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Queue an immediate (live) MIDI channel pressure to a MIDI destination.
|
|
829
|
+
* `pressure` is 7-bit (0..127); `renderFrame` is the frame to fire at, or -1
|
|
830
|
+
* for immediate. Mirrors the Node/Python/C-ABI `pushMidiChannelPressure`.
|
|
831
|
+
*/
|
|
832
|
+
pushMidiChannelPressure(
|
|
833
|
+
destinationId: number,
|
|
834
|
+
group: number,
|
|
835
|
+
channel: number,
|
|
836
|
+
pressure: number,
|
|
837
|
+
renderFrame = -1,
|
|
838
|
+
): void {
|
|
839
|
+
this.native.pushMidiChannelPressure(destinationId, group, channel, pressure, renderFrame);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Queue an immediate (live) MIDI polyphonic key pressure to a MIDI
|
|
844
|
+
* destination. `note` and `pressure` are 7-bit (0..127); `renderFrame` is the
|
|
845
|
+
* frame to fire at, or -1 for immediate. Mirrors the Node/Python/C-ABI
|
|
846
|
+
* `pushMidiPolyPressure`.
|
|
847
|
+
*/
|
|
848
|
+
pushMidiPolyPressure(
|
|
849
|
+
destinationId: number,
|
|
850
|
+
group: number,
|
|
851
|
+
channel: number,
|
|
852
|
+
note: number,
|
|
853
|
+
pressure: number,
|
|
854
|
+
renderFrame = -1,
|
|
855
|
+
): void {
|
|
856
|
+
this.native.pushMidiPolyPressure(destinationId, group, channel, note, pressure, renderFrame);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Queue an immediate (live) raw UMP message to a MIDI destination. `words` is
|
|
861
|
+
* 1 to 4 words, most significant first, and its length must match the message
|
|
862
|
+
* type of `words[0]`. MIDI 2.0 channel-voice messages (MT 0x4) arrive at full
|
|
863
|
+
* width; SysEx7 / data messages (MT 0x3 / 0x5) are refused, use
|
|
864
|
+
* {@link pushMidiSysex}. Throws when the slot ring or command queue is full
|
|
865
|
+
* (retry after a process block). `renderFrame` is the render-frame time to
|
|
866
|
+
* apply, or -1 for immediate. A bare number is accepted as a one-word
|
|
867
|
+
* message.
|
|
868
|
+
*/
|
|
869
|
+
pushMidiUmp(destinationId: number, words: UmpWords | number, renderFrame = -1): void {
|
|
870
|
+
const list = typeof words === 'number' ? [words] : words;
|
|
871
|
+
this.native.pushMidiUmp(destinationId, assertUmpWords('pushMidiUmp', list), renderFrame);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Push one raw UMP message (1 to 4 words) to the engine-owned MIDI input
|
|
876
|
+
* source. The message rules match {@link pushMidiUmp}. `portTimeSamples` is
|
|
877
|
+
* the port timestamp in samples.
|
|
878
|
+
*/
|
|
879
|
+
pushMidiInputUmp(words: UmpWords, portTimeSamples = 0): void {
|
|
880
|
+
this.native.pushMidiInputUmp(assertUmpWords('pushMidiInputUmp', words), portTimeSamples);
|
|
488
881
|
}
|
|
489
882
|
|
|
490
883
|
/**
|
|
@@ -540,11 +933,21 @@ export class RealtimeEngine {
|
|
|
540
933
|
this.native.settleParameters();
|
|
541
934
|
}
|
|
542
935
|
|
|
936
|
+
/** Snap only insert automation slots after structural replay. */
|
|
937
|
+
settleInsertParameters(): void {
|
|
938
|
+
this.native.settleInsertParameters();
|
|
939
|
+
}
|
|
940
|
+
|
|
543
941
|
/** Drains queued commands on an offline/control-only engine immediately. */
|
|
544
942
|
flushControlCommands(): void {
|
|
545
943
|
this.native.flushControlCommands();
|
|
546
944
|
}
|
|
547
945
|
|
|
946
|
+
/** Applies commands already due on a control-only mirror, retaining future commands. */
|
|
947
|
+
applyCommandsDueNowPreservingFuture(): void {
|
|
948
|
+
this.native.applyCommandsDueNowPreservingFuture();
|
|
949
|
+
}
|
|
950
|
+
|
|
548
951
|
seekPpq(ppq: number, renderFrame = -1): void {
|
|
549
952
|
this.native.seekPpq(ppq, renderFrame);
|
|
550
953
|
}
|
|
@@ -582,11 +985,11 @@ export class RealtimeEngine {
|
|
|
582
985
|
return this.native.parameterCount();
|
|
583
986
|
}
|
|
584
987
|
|
|
585
|
-
parameterInfoByIndex(index: number): EngineParameterInfo {
|
|
988
|
+
parameterInfoByIndex(index: number): Required<EngineParameterInfo> {
|
|
586
989
|
return this.native.parameterInfoByIndex(index);
|
|
587
990
|
}
|
|
588
991
|
|
|
589
|
-
parameterInfo(id: number): EngineParameterInfo {
|
|
992
|
+
parameterInfo(id: number): Required<EngineParameterInfo> {
|
|
590
993
|
return this.native.parameterInfo(id);
|
|
591
994
|
}
|
|
592
995
|
|
|
@@ -671,6 +1074,19 @@ export class RealtimeEngine {
|
|
|
671
1074
|
return this.native.clipCount();
|
|
672
1075
|
}
|
|
673
1076
|
|
|
1077
|
+
/**
|
|
1078
|
+
* Normalizes each send's pre/post tap point to the integer the native layer
|
|
1079
|
+
* reads (defaults to post-fader when omitted). Shared by track lanes and
|
|
1080
|
+
* buses, which carry the same send shape.
|
|
1081
|
+
*/
|
|
1082
|
+
private static normalizeSends(sends: EngineTrackSend[]): EngineTrackSend[] {
|
|
1083
|
+
return sends.map((send) => ({
|
|
1084
|
+
...send,
|
|
1085
|
+
// Post-fader (0) is the default for an omitted sendTiming.
|
|
1086
|
+
sendTiming: send.sendTiming === undefined ? 0 : sendTimingCode(send.sendTiming),
|
|
1087
|
+
}));
|
|
1088
|
+
}
|
|
1089
|
+
|
|
674
1090
|
setTrackLanes(lanes: Array<number | EngineTrackLane>): void {
|
|
675
1091
|
this.native.setTrackLanes(
|
|
676
1092
|
lanes.map((lane) => {
|
|
@@ -680,16 +1096,7 @@ export class RealtimeEngine {
|
|
|
680
1096
|
if (!lane.sends) {
|
|
681
1097
|
return lane;
|
|
682
1098
|
}
|
|
683
|
-
|
|
684
|
-
// layer reads (defaults to post-fader when omitted).
|
|
685
|
-
return {
|
|
686
|
-
...lane,
|
|
687
|
-
sends: lane.sends.map((send) => ({
|
|
688
|
-
...send,
|
|
689
|
-
// Post-fader (0) is the default for an omitted sendTiming.
|
|
690
|
-
sendTiming: send.sendTiming === undefined ? 0 : sendTimingCode(send.sendTiming),
|
|
691
|
-
})),
|
|
692
|
-
};
|
|
1099
|
+
return { ...lane, sends: RealtimeEngine.normalizeSends(lane.sends) };
|
|
693
1100
|
}),
|
|
694
1101
|
);
|
|
695
1102
|
}
|
|
@@ -703,7 +1110,42 @@ export class RealtimeEngine {
|
|
|
703
1110
|
}
|
|
704
1111
|
|
|
705
1112
|
setTrackBuses(buses: EngineBus[]): void {
|
|
706
|
-
|
|
1113
|
+
// Array.isArray guards a caller-fabricated array-like (e.g. `{ length }`)
|
|
1114
|
+
// meant to probe the native array-length read: passing it through
|
|
1115
|
+
// unmodified lets that guard see the real (missing) length rather than
|
|
1116
|
+
// failing here on a `.map` that array-likes do not implement.
|
|
1117
|
+
this.native.setTrackBuses(
|
|
1118
|
+
Array.isArray(buses)
|
|
1119
|
+
? buses.map((bus) =>
|
|
1120
|
+
bus.sends ? { ...bus, sends: RealtimeEngine.normalizeSends(bus.sends) } : bus,
|
|
1121
|
+
)
|
|
1122
|
+
: buses,
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Keys one insert of a bus strip from a track lane or another bus
|
|
1128
|
+
* (ducking/sidechainRouter inserts). `sourceId` 0 removes the binding.
|
|
1129
|
+
*/
|
|
1130
|
+
setBusSidechain(
|
|
1131
|
+
busId: number,
|
|
1132
|
+
insertIndex: number,
|
|
1133
|
+
sourceKind: SidechainSourceKind | number,
|
|
1134
|
+
sourceId: number,
|
|
1135
|
+
): void {
|
|
1136
|
+
this.native.setBusSidechain(busId, insertIndex, sidechainSourceKindCode(sourceKind), sourceId);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Keys one insert of the master strip from a track lane or a bus. Same
|
|
1141
|
+
* source rules as {@link setBusSidechain}.
|
|
1142
|
+
*/
|
|
1143
|
+
setMasterSidechain(
|
|
1144
|
+
insertIndex: number,
|
|
1145
|
+
sourceKind: SidechainSourceKind | number,
|
|
1146
|
+
sourceId: number,
|
|
1147
|
+
): void {
|
|
1148
|
+
this.native.setMasterSidechain(insertIndex, sidechainSourceKindCode(sourceKind), sourceId);
|
|
707
1149
|
}
|
|
708
1150
|
|
|
709
1151
|
setBusStripJson(busId: number, sceneJson: string): void {
|
|
@@ -747,6 +1189,19 @@ export class RealtimeEngine {
|
|
|
747
1189
|
this.native.setTrackStripInsertBypassed(trackId, insertIndex, bypassed, resetOnBypass);
|
|
748
1190
|
}
|
|
749
1191
|
|
|
1192
|
+
/** Bus-strip counterpart of {@link setTrackStripEqBand}. */
|
|
1193
|
+
setBusStripEqBand(busId: number, bandIndex: number, band: EqBand | string): void {
|
|
1194
|
+
this.native.setBusStripEqBandJson(
|
|
1195
|
+
busId,
|
|
1196
|
+
bandIndex,
|
|
1197
|
+
typeof band === 'string' ? band : JSON.stringify(band),
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
setBusStripEqBandJson(busId: number, bandIndex: number, bandJson: string): void {
|
|
1202
|
+
this.native.setBusStripEqBandJson(busId, bandIndex, bandJson);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
750
1205
|
setMasterStripJson(sceneJson: string): void {
|
|
751
1206
|
try {
|
|
752
1207
|
JSON.parse(sceneJson);
|
|
@@ -778,10 +1233,12 @@ export class RealtimeEngine {
|
|
|
778
1233
|
|
|
779
1234
|
/**
|
|
780
1235
|
* Changes one track-strip insert parameter in realtime, addressed by the
|
|
781
|
-
* processor's JSON-key parameter name
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
1236
|
+
* processor's JSON-key parameter name — one of the entries
|
|
1237
|
+
* {@link masteringInsertParamInfo} reports with a non-null `id`; a
|
|
1238
|
+
* construction-only entry (`id` null) takes effect only when the insert is
|
|
1239
|
+
* built. Applied at the next block head via the engine command queue; safe
|
|
1240
|
+
* during playback. Throws if the track, insert, or name is unknown, the
|
|
1241
|
+
* param is not realtime-safe, or the command queue is full.
|
|
785
1242
|
*/
|
|
786
1243
|
setTrackStripInsertParamByName(
|
|
787
1244
|
trackId: number,
|
|
@@ -792,11 +1249,43 @@ export class RealtimeEngine {
|
|
|
792
1249
|
this.native.setTrackStripInsertParamByName(trackId, insertIndex, paramName, value);
|
|
793
1250
|
}
|
|
794
1251
|
|
|
1252
|
+
/** Apply a live insert edit on this engine's owning thread without draining its command queue. */
|
|
1253
|
+
applyTrackStripInsertParamByNameNow(
|
|
1254
|
+
trackId: number,
|
|
1255
|
+
insertIndex: number,
|
|
1256
|
+
paramName: string,
|
|
1257
|
+
value: number,
|
|
1258
|
+
): boolean {
|
|
1259
|
+
return this.native.applyTrackStripInsertParamByNameNow(trackId, insertIndex, paramName, value);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/** Restore a retained insert value exactly after a strip scene is replayed. */
|
|
1263
|
+
restoreTrackStripInsertParamByName(
|
|
1264
|
+
trackId: number,
|
|
1265
|
+
insertIndex: number,
|
|
1266
|
+
paramName: string,
|
|
1267
|
+
value: number,
|
|
1268
|
+
): void {
|
|
1269
|
+
this.native.restoreTrackStripInsertParamByName(trackId, insertIndex, paramName, value);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
795
1272
|
/** Master-strip counterpart of {@link setTrackStripInsertParamByName}. */
|
|
796
1273
|
setMasterStripInsertParamByName(insertIndex: number, paramName: string, value: number): void {
|
|
797
1274
|
this.native.setMasterStripInsertParamByName(insertIndex, paramName, value);
|
|
798
1275
|
}
|
|
799
1276
|
|
|
1277
|
+
applyMasterStripInsertParamByNameNow(
|
|
1278
|
+
insertIndex: number,
|
|
1279
|
+
paramName: string,
|
|
1280
|
+
value: number,
|
|
1281
|
+
): boolean {
|
|
1282
|
+
return this.native.applyMasterStripInsertParamByNameNow(insertIndex, paramName, value);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
restoreMasterStripInsertParamByName(insertIndex: number, paramName: string, value: number): void {
|
|
1286
|
+
this.native.restoreMasterStripInsertParamByName(insertIndex, paramName, value);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
800
1289
|
/** Bus-strip counterpart of {@link setTrackStripInsertParamByName}. */
|
|
801
1290
|
setBusStripInsertParamByName(
|
|
802
1291
|
busId: number,
|
|
@@ -807,6 +1296,42 @@ export class RealtimeEngine {
|
|
|
807
1296
|
this.native.setBusStripInsertParamByName(busId, insertIndex, paramName, value);
|
|
808
1297
|
}
|
|
809
1298
|
|
|
1299
|
+
applyBusStripInsertParamByNameNow(
|
|
1300
|
+
busId: number,
|
|
1301
|
+
insertIndex: number,
|
|
1302
|
+
paramName: string,
|
|
1303
|
+
value: number,
|
|
1304
|
+
): boolean {
|
|
1305
|
+
return this.native.applyBusStripInsertParamByNameNow(busId, insertIndex, paramName, value);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
restoreBusStripInsertParamByName(
|
|
1309
|
+
busId: number,
|
|
1310
|
+
insertIndex: number,
|
|
1311
|
+
paramName: string,
|
|
1312
|
+
value: number,
|
|
1313
|
+
): void {
|
|
1314
|
+
this.native.restoreBusStripInsertParamByName(busId, insertIndex, paramName, value);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Forgets the remembered manual insert-parameter values of one track strip
|
|
1319
|
+
* and discards its queued insert edits. Call before {@link setTrackStripJson}
|
|
1320
|
+
* replaces the strip when its old values must not carry over; the setter
|
|
1321
|
+
* never does this itself, since a queued edit may already target the new chain.
|
|
1322
|
+
*/
|
|
1323
|
+
clearTrackInsertParameterBases(trackId: number): void {
|
|
1324
|
+
this.native.clearTrackInsertParameterBases(trackId);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
clearBusInsertParameterBases(busId: number): void {
|
|
1328
|
+
this.native.clearBusInsertParameterBases(busId);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
clearMasterInsertParameterBases(): void {
|
|
1332
|
+
this.native.clearMasterInsertParameterBases();
|
|
1333
|
+
}
|
|
1334
|
+
|
|
810
1335
|
/** Bus-strip counterpart of {@link setTrackStripInsertBypassed}. */
|
|
811
1336
|
setBusStripInsertBypassed(
|
|
812
1337
|
busId: number,
|
|
@@ -845,6 +1370,42 @@ export class RealtimeEngine {
|
|
|
845
1370
|
return this.native.resolveBusInsertAutomationId(busId, insertIndex, paramName);
|
|
846
1371
|
}
|
|
847
1372
|
|
|
1373
|
+
/**
|
|
1374
|
+
* Resolves a hosted instrument's continuous parameter (by its JSON-key name)
|
|
1375
|
+
* to the reserved automation id usable with `setAutomationLane` /
|
|
1376
|
+
* `setParameter`, so an instrument parameter is driven at audio-block
|
|
1377
|
+
* precision exactly like a strip insert. Returns `-1` when the destination
|
|
1378
|
+
* has no bound instrument, the instrument exposes no automatable parameters,
|
|
1379
|
+
* or the name is unknown.
|
|
1380
|
+
*
|
|
1381
|
+
* For the NativeSynth ({@link setSynthInstrument}) the names are the
|
|
1382
|
+
* continuous {@link SynthPatch} fields: `gain`, `busDrive`, `cutoffHz`,
|
|
1383
|
+
* `resonanceQ`, `drive`, `keyTrack`, `envToCutoffCents`, `velToCutoffCents`,
|
|
1384
|
+
* `ampAttackMs`, `ampDecayMs`, `ampSustain`, `ampReleaseMs`,
|
|
1385
|
+
* `filterAttackMs`, `filterDecayMs`, `filterSustain`, `filterReleaseMs`,
|
|
1386
|
+
* `lfoRateHz`, `lfoToPitchCents`, `lfo2RateHz`, `glideMs`, `bodyMix`,
|
|
1387
|
+
* `stereoSpread`, `detuneCents`, `driftCents`, `pitchOffsetCents`,
|
|
1388
|
+
* `hpCutoffHz`, `sampleHoldHz`, `bitDepth`.
|
|
1389
|
+
*
|
|
1390
|
+
* Structural fields (`preset`, `engineMode`, `waveform`, `filterModel`,
|
|
1391
|
+
* `unison`, `polyphony`, `body`, `modRoutings`) are not automatable and
|
|
1392
|
+
* return `-1`: they resize voice pools or swap DSP topology, which is not
|
|
1393
|
+
* audio-thread safe. Rebind the instrument with a new patch instead.
|
|
1394
|
+
*
|
|
1395
|
+
* `gain`, `busDrive`, `cutoffHz`, `resonanceQ`, `envToCutoffCents`,
|
|
1396
|
+
* `lfoToPitchCents` and `pitchOffsetCents` reach voices that are already
|
|
1397
|
+
* sounding from the next block; the rest are cached into per-voice state at
|
|
1398
|
+
* note-on and take effect from the next note, so a lane that moves one of
|
|
1399
|
+
* them under a held note looks inert until the next one speaks — that is the
|
|
1400
|
+
* behaviour, not a dropped write.
|
|
1401
|
+
*
|
|
1402
|
+
* The id survives an unbind/rebind of the same destination and applies
|
|
1403
|
+
* nothing while that destination is unbound.
|
|
1404
|
+
*/
|
|
1405
|
+
resolveInstrumentAutomationId(destinationId: number, paramName: string): number {
|
|
1406
|
+
return this.native.resolveInstrumentAutomationId(destinationId, paramName);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
848
1409
|
/** Sets a track lane strip's pan position in realtime (glitch-free). */
|
|
849
1410
|
setTrackStripPan(trackId: number, pan: number): void {
|
|
850
1411
|
this.native.setTrackStripPan(trackId, pan);
|
|
@@ -865,6 +1426,29 @@ export class RealtimeEngine {
|
|
|
865
1426
|
this.native.setTrackStripDualPan(trackId, leftPan, rightPan);
|
|
866
1427
|
}
|
|
867
1428
|
|
|
1429
|
+
/**
|
|
1430
|
+
* Sets a bus strip's output pan position in realtime (glitch-free). Throws
|
|
1431
|
+
* for an unknown bus or one wider than stereo.
|
|
1432
|
+
*/
|
|
1433
|
+
setBusStripPan(busId: number, pan: number): void {
|
|
1434
|
+
this.native.setBusStripPan(busId, pan);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/** Sets a bus strip's pan law in realtime. */
|
|
1438
|
+
setBusStripPanLaw(busId: number, panLaw: PanLawInput): void {
|
|
1439
|
+
this.native.setBusStripPanLaw(busId, panLawCode(panLaw));
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/** Sets a bus strip's pan mode in realtime. */
|
|
1443
|
+
setBusStripPanMode(busId: number, panMode: PanMode | number): void {
|
|
1444
|
+
this.native.setBusStripPanMode(busId, panModeCode(panMode));
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/** Sets a bus strip's dual-pan left/right positions in realtime. */
|
|
1448
|
+
setBusStripDualPan(busId: number, leftPan: number, rightPan: number): void {
|
|
1449
|
+
this.native.setBusStripDualPan(busId, leftPan, rightPan);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
868
1452
|
/**
|
|
869
1453
|
* Sets a track lane strip's inter-channel alignment delay (whole samples).
|
|
870
1454
|
* Adjusts strip latency, so PDC and reported graph latency are refreshed.
|
|
@@ -919,6 +1503,57 @@ export class RealtimeEngine {
|
|
|
919
1503
|
return this.native.clipPageRequestOverflowCount();
|
|
920
1504
|
}
|
|
921
1505
|
|
|
1506
|
+
/** Cumulative warp-stretch requests dropped because the native queue was full. */
|
|
1507
|
+
warpStretchOverflowCount(): number {
|
|
1508
|
+
return this.native.warpStretchOverflowCount();
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Sets the number of concurrent time-stretch voices. `voices` must be an
|
|
1513
|
+
* integer in `[0, 64]`; a non-integer, negative, or larger value throws and
|
|
1514
|
+
* leaves the capacity unchanged. Default is 8. Capacity 0 disables
|
|
1515
|
+
* time-stretch, so every warped clip plays resampled instead and none of
|
|
1516
|
+
* that counts toward {@link warpStretchOverflowCount}. A change applied
|
|
1517
|
+
* while the engine is running restarts the splice state of any clip
|
|
1518
|
+
* stretching through a voice at that moment. Control-thread only.
|
|
1519
|
+
*/
|
|
1520
|
+
setWarpVoiceCapacity(voices: number): void {
|
|
1521
|
+
this.native.setWarpVoiceCapacity(voices);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/** Reads the current time-stretch voice capacity (default 8). */
|
|
1525
|
+
warpVoiceCapacity(): number {
|
|
1526
|
+
return this.native.warpVoiceCapacity();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Sets the clip-page look-ahead window in timeline frames.
|
|
1531
|
+
*
|
|
1532
|
+
* The player reports the pages it is *about to* read that are not resident
|
|
1533
|
+
* yet, so a streaming host can service them before the audio thread reaches
|
|
1534
|
+
* them. Without look-ahead a page miss is only reported after the read
|
|
1535
|
+
* already produced silence, which costs one block of silence at every page
|
|
1536
|
+
* boundary the host has not primed — the reason a sliding-window streamer
|
|
1537
|
+
* cannot keep a live playhead fed from miss reports alone.
|
|
1538
|
+
*
|
|
1539
|
+
* Look-ahead requests drain through the same `popClipPageRequest` queue and
|
|
1540
|
+
* are queued *after* the block's genuine misses, so a host that keeps only
|
|
1541
|
+
* the newest request per clip (as {@link ClipPageStreamer} does) tracks the
|
|
1542
|
+
* look-ahead frontier.
|
|
1543
|
+
*
|
|
1544
|
+
* `prepare` defaults this to half a second at the engine's sample rate. `0`
|
|
1545
|
+
* disables the look-ahead. A clip whose pages are all resident produces no
|
|
1546
|
+
* requests at all, with or without look-ahead. Safe to call during playback.
|
|
1547
|
+
*/
|
|
1548
|
+
setClipPagePrefetchFrames(frames: number): void {
|
|
1549
|
+
this.native.setClipPagePrefetchFrames(frames);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
/** Current clip-page look-ahead window in timeline frames. */
|
|
1553
|
+
clipPagePrefetchFrames(): number {
|
|
1554
|
+
return this.native.clipPagePrefetchFrames();
|
|
1555
|
+
}
|
|
1556
|
+
|
|
922
1557
|
setCaptureBuffer(numChannels: number, capacityFrames: number): void {
|
|
923
1558
|
this.native.setCaptureBuffer(numChannels, capacityFrames);
|
|
924
1559
|
}
|
|
@@ -1027,14 +1662,52 @@ export class RealtimeEngine {
|
|
|
1027
1662
|
return this.native.processWithMonitor(channels);
|
|
1028
1663
|
}
|
|
1029
1664
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1665
|
+
/**
|
|
1666
|
+
* Render `channels` offline from the current transport position. Requesting
|
|
1667
|
+
* more planes than `prepare` reserved throws an `InvalidParameter`
|
|
1668
|
+
* `SonareError` rather than returning silence that reads as a finished render.
|
|
1669
|
+
*
|
|
1670
|
+
* Set `finalize: false` to render one chunk of a longer timeline; see
|
|
1671
|
+
* {@link RenderOfflineRequest.finalize} and {@link finishOfflineRender}.
|
|
1672
|
+
*/
|
|
1673
|
+
renderOffline(request: RenderOfflineRequest): Float32Array[];
|
|
1674
|
+
renderOffline(channels: Float32Array[], blockSize?: number): Float32Array[];
|
|
1675
|
+
renderOffline(
|
|
1676
|
+
channelsOrRequest: Float32Array[] | RenderOfflineRequest,
|
|
1677
|
+
blockSize = 128,
|
|
1678
|
+
): Float32Array[] {
|
|
1679
|
+
const request = normalizeRenderOfflineRequest(channelsOrRequest, blockSize);
|
|
1680
|
+
return this.native.renderOffline(request.channels, request.blockSize, request.finalize);
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* End a chunked offline render: release every note the sequencer still holds
|
|
1685
|
+
* and flush the PDC / alignment delay lines. Required after
|
|
1686
|
+
* `renderOffline({ finalize: false })`; the finalizing form does it itself.
|
|
1687
|
+
*
|
|
1688
|
+
* Skipping it leaves every note still sounding at the last chunk held. On an
|
|
1689
|
+
* engine-internal instrument the tail simply never releases; on a destination
|
|
1690
|
+
* marked external ({@link RealtimeEngine.setMidiDestinationExternal}) the
|
|
1691
|
+
* note-ons already left through the external MIDI queue, so the note-offs
|
|
1692
|
+
* emitted here are the only ones the receiving device will get and the notes
|
|
1693
|
+
* otherwise hang outside the engine.
|
|
1694
|
+
*/
|
|
1695
|
+
finishOfflineRender(): void {
|
|
1696
|
+
this.native.finishOfflineRender();
|
|
1032
1697
|
}
|
|
1033
1698
|
|
|
1699
|
+
/**
|
|
1700
|
+
* Bounce the timeline to an interleaved buffer. `numChannels` above the
|
|
1701
|
+
* prepared channel count throws an `InvalidParameter` `SonareError`.
|
|
1702
|
+
*/
|
|
1034
1703
|
bounceOffline(options: EngineBounceOptions): EngineBounceResult {
|
|
1035
1704
|
return this.native.bounceOffline(options);
|
|
1036
1705
|
}
|
|
1037
1706
|
|
|
1707
|
+
/**
|
|
1708
|
+
* Freeze the current graph to audio. `numChannels` above the prepared channel
|
|
1709
|
+
* count throws an `InvalidParameter` `SonareError`.
|
|
1710
|
+
*/
|
|
1038
1711
|
freezeOffline(options: EngineFreezeOptions): EngineFreezeResult {
|
|
1039
1712
|
return this.native.freezeOffline(options);
|
|
1040
1713
|
}
|
|
@@ -1144,9 +1817,19 @@ export class RealtimeEngine {
|
|
|
1144
1817
|
return this.native.scopeScratchPointRight(index);
|
|
1145
1818
|
}
|
|
1146
1819
|
|
|
1820
|
+
/** Release the underlying WASM object. Idempotent, as the Node facade is. */
|
|
1147
1821
|
destroy(): void {
|
|
1822
|
+
if (this.released) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
this.released = true;
|
|
1148
1826
|
this.native.delete();
|
|
1149
1827
|
}
|
|
1828
|
+
|
|
1829
|
+
/** Alias for {@link destroy}, matching embind's own release method name. */
|
|
1830
|
+
delete(): void {
|
|
1831
|
+
this.destroy();
|
|
1832
|
+
}
|
|
1150
1833
|
}
|
|
1151
1834
|
|
|
1152
1835
|
export class ClipPageProvider {
|