@libraz/libsonare 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +178 -0
- package/README.md +26 -170
- package/dist/_chain_config.d.ts +14 -0
- package/dist/_chain_config.d.ts.map +1 -0
- package/dist/_effects_common.d.ts +6 -0
- package/dist/_effects_common.d.ts.map +1 -0
- package/dist/_feature_validation.d.ts +8 -0
- package/dist/_feature_validation.d.ts.map +1 -0
- package/dist/_fft_options.d.ts +24 -0
- package/dist/_fft_options.d.ts.map +1 -0
- package/dist/align_take.d.ts +50 -0
- package/dist/align_take.d.ts.map +1 -0
- package/dist/analysis.d.ts +29 -5837
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +873 -722
- package/dist/analysis.js.map +1 -1
- package/dist/analysis_helpers.d.ts +9 -0
- package/dist/analysis_helpers.d.ts.map +1 -0
- package/dist/audio.d.ts +163 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/clip_page_streamer.d.ts +133 -0
- package/dist/clip_page_streamer.d.ts.map +1 -0
- package/dist/codes.d.ts +44 -0
- package/dist/codes.d.ts.map +1 -0
- package/dist/effects_mastering.d.ts +23 -0
- package/dist/effects_mastering.d.ts.map +1 -0
- package/dist/effects_note_ops.d.ts +477 -0
- package/dist/effects_note_ops.d.ts.map +1 -0
- package/dist/effects_percussive.d.ts +185 -0
- package/dist/effects_percussive.d.ts.map +1 -0
- package/dist/effects_separation.d.ts +65 -0
- package/dist/effects_separation.d.ts.map +1 -0
- package/dist/effects_spectral.d.ts +28 -0
- package/dist/effects_spectral.d.ts.map +1 -0
- package/dist/effects_timepitch.d.ts +129 -0
- package/dist/effects_timepitch.d.ts.map +1 -0
- package/dist/effects_voice_change.d.ts +53 -0
- package/dist/effects_voice_change.d.ts.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/feature_core.d.ts +341 -0
- package/dist/feature_core.d.ts.map +1 -0
- package/dist/feature_decompose.d.ts +278 -0
- package/dist/feature_decompose.d.ts.map +1 -0
- package/dist/feature_inverse.d.ts +128 -0
- package/dist/feature_inverse.d.ts.map +1 -0
- package/dist/feature_loudness.d.ts +66 -0
- package/dist/feature_loudness.d.ts.map +1 -0
- package/dist/feature_music.d.ts +307 -0
- package/dist/feature_music.d.ts.map +1 -0
- package/dist/feature_pitch.d.ts +108 -0
- package/dist/feature_pitch.d.ts.map +1 -0
- package/dist/feature_resample.d.ts +16 -0
- package/dist/feature_resample.d.ts.map +1 -0
- package/dist/feature_spectral.d.ts +137 -0
- package/dist/feature_spectral.d.ts.map +1 -0
- package/dist/feature_spectrogram.d.ts +198 -0
- package/dist/feature_spectrogram.d.ts.map +1 -0
- package/dist/features.d.ts +10 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/hrtf/default.shrf +0 -0
- package/dist/index.d.ts +74 -7329
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3816 -1359
- package/dist/index.js.map +1 -1
- package/dist/instrument_types.d.ts +517 -0
- package/dist/instrument_types.d.ts.map +1 -0
- package/dist/live_audio.d.ts +35 -0
- package/dist/live_audio.d.ts.map +1 -0
- package/dist/mastering_chain.d.ts +213 -0
- package/dist/mastering_chain.d.ts.map +1 -0
- package/dist/mastering_core.d.ts +457 -0
- package/dist/mastering_core.d.ts.map +1 -0
- package/dist/mastering_dynamics.d.ts +80 -0
- package/dist/mastering_dynamics.d.ts.map +1 -0
- package/dist/metering.d.ts +287 -0
- package/dist/metering.d.ts.map +1 -0
- package/dist/mixer.d.ts +464 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixing_assistant.d.ts +62 -0
- package/dist/mixing_assistant.d.ts.map +1 -0
- package/dist/mixing_oneshot.d.ts +40 -0
- package/dist/mixing_oneshot.d.ts.map +1 -0
- package/dist/module_state.d.ts +15 -0
- package/dist/module_state.d.ts.map +1 -0
- package/dist/opfs_clip_pages.d.ts +28 -0
- package/dist/opfs_clip_pages.d.ts.map +1 -0
- package/dist/playback_renderer.d.ts +128 -0
- package/dist/playback_renderer.d.ts.map +1 -0
- package/dist/polyphony.d.ts +202 -0
- package/dist/polyphony.d.ts.map +1 -0
- package/dist/project.d.ts +8 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project_class.d.ts +562 -0
- package/dist/project_class.d.ts.map +1 -0
- package/dist/project_internal.d.ts +194 -0
- package/dist/project_internal.d.ts.map +1 -0
- package/dist/project_synth.d.ts +74 -0
- package/dist/project_synth.d.ts.map +1 -0
- package/dist/project_types.d.ts +652 -0
- package/dist/project_types.d.ts.map +1 -0
- package/dist/public_types.d.ts +185 -0
- package/dist/public_types.d.ts.map +1 -0
- package/dist/public_types_acoustic.d.ts +215 -0
- package/dist/public_types_acoustic.d.ts.map +1 -0
- package/dist/public_types_mastering.d.ts +510 -0
- package/dist/public_types_mastering.d.ts.map +1 -0
- package/dist/public_types_mixing.d.ts +436 -0
- package/dist/public_types_mixing.d.ts.map +1 -0
- package/dist/public_types_music.d.ts +619 -0
- package/dist/public_types_music.d.ts.map +1 -0
- package/dist/public_types_playback.d.ts +164 -0
- package/dist/public_types_playback.d.ts.map +1 -0
- package/dist/public_types_realtime.d.ts +174 -0
- package/dist/public_types_realtime.d.ts.map +1 -0
- package/dist/public_types_repair.d.ts +424 -0
- package/dist/public_types_repair.d.ts.map +1 -0
- package/dist/public_types_spectral.d.ts +697 -0
- package/dist/public_types_spectral.d.ts.map +1 -0
- package/dist/quick_analysis.d.ts +445 -0
- package/dist/quick_analysis.d.ts.map +1 -0
- package/dist/realtime_engine.d.ts +850 -0
- package/dist/realtime_engine.d.ts.map +1 -0
- package/dist/realtime_voice_changer.d.ts +158 -0
- package/dist/realtime_voice_changer.d.ts.map +1 -0
- package/dist/repair_dereverb.d.ts +187 -0
- package/dist/repair_dereverb.d.ts.map +1 -0
- package/dist/repair_impulsive.d.ts +186 -0
- package/dist/repair_impulsive.d.ts.map +1 -0
- package/dist/repair_noise.d.ts +239 -0
- package/dist/repair_noise.d.ts.map +1 -0
- package/dist/repair_trim.d.ts +123 -0
- package/dist/repair_trim.d.ts.map +1 -0
- package/dist/sample_bank.d.ts +84 -0
- package/dist/sample_bank.d.ts.map +1 -0
- package/dist/scale.d.ts +10 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/schemas/mixer-scene.schema.json +393 -0
- package/dist/schemas/playback-renderer-config.schema.json +392 -0
- package/dist/sonare-analysis.d.ts +8 -0
- package/dist/sonare-analysis.js +2 -2
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.d.ts +3909 -0
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/stream_analyzer.d.ts +163 -0
- package/dist/stream_analyzer.d.ts.map +1 -0
- package/dist/stream_types.d.ts +214 -0
- package/dist/stream_types.d.ts.map +1 -0
- package/dist/streaming_mixing.d.ts +6 -0
- package/dist/streaming_mixing.d.ts.map +1 -0
- package/dist/streaming_processors.d.ts +335 -0
- package/dist/streaming_processors.d.ts.map +1 -0
- package/dist/transcribe.d.ts +77 -0
- package/dist/transcribe.d.ts.map +1 -0
- package/dist/validation.d.ts +140 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/web_midi.d.ts +77 -0
- package/dist/web_midi.d.ts.map +1 -0
- package/dist/worker.d.ts +5 -48
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +94 -41
- package/dist/worker.js.map +1 -1
- package/dist/worker_client.d.ts +96 -0
- package/dist/worker_client.d.ts.map +1 -0
- package/dist/worker_protocol.d.ts +43 -0
- package/dist/worker_protocol.d.ts.map +1 -0
- package/dist/worklet/audio_types.d.ts +21 -0
- package/dist/worklet/audio_types.d.ts.map +1 -0
- package/dist/worklet/engine-automation.d.ts +29 -0
- package/dist/worklet/engine-automation.d.ts.map +1 -0
- package/dist/worklet/engine-capture-facade.d.ts +35 -0
- package/dist/worklet/engine-capture-facade.d.ts.map +1 -0
- package/dist/worklet/engine-clips.d.ts +23 -0
- package/dist/worklet/engine-clips.d.ts.map +1 -0
- package/dist/worklet/engine-markers.d.ts +40 -0
- package/dist/worklet/engine-markers.d.ts.map +1 -0
- package/dist/worklet/engine-mixer-facade.d.ts +128 -0
- package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
- package/dist/worklet/engine-node.d.ts +81 -0
- package/dist/worklet/engine-node.d.ts.map +1 -0
- package/dist/worklet/engine-offline.d.ts +81 -0
- package/dist/worklet/engine-offline.d.ts.map +1 -0
- package/dist/worklet/engine-options.d.ts +12 -0
- package/dist/worklet/engine-options.d.ts.map +1 -0
- package/dist/worklet/engine-parameter-facade.d.ts +106 -0
- package/dist/worklet/engine-parameter-facade.d.ts.map +1 -0
- package/dist/worklet/engine-processor.d.ts +68 -0
- package/dist/worklet/engine-processor.d.ts.map +1 -0
- package/dist/worklet/engine-register.d.ts +2 -0
- package/dist/worklet/engine-register.d.ts.map +1 -0
- package/dist/worklet/engine-strips.d.ts +73 -0
- package/dist/worklet/engine-strips.d.ts.map +1 -0
- package/dist/worklet/engine-sync.d.ts +37 -0
- package/dist/worklet/engine-sync.d.ts.map +1 -0
- package/dist/worklet/engine-tempo-facade.d.ts +48 -0
- package/dist/worklet/engine-tempo-facade.d.ts.map +1 -0
- package/dist/worklet/engine.d.ts +397 -0
- package/dist/worklet/engine.d.ts.map +1 -0
- package/dist/worklet/guards.d.ts +47 -0
- package/dist/worklet/guards.d.ts.map +1 -0
- package/dist/worklet/messages.d.ts +710 -0
- package/dist/worklet/messages.d.ts.map +1 -0
- package/dist/worklet/mixer-processor.d.ts +46 -0
- package/dist/worklet/mixer-processor.d.ts.map +1 -0
- package/dist/worklet/playback-processor.d.ts +62 -0
- package/dist/worklet/playback-processor.d.ts.map +1 -0
- package/dist/worklet/protocol.d.ts +323 -0
- package/dist/worklet/protocol.d.ts.map +1 -0
- package/dist/worklet/voice-changer-processor.d.ts +41 -0
- package/dist/worklet/voice-changer-processor.d.ts.map +1 -0
- package/dist/worklet.d.ts +16 -2142
- package/dist/worklet.d.ts.map +1 -0
- package/dist/worklet.js +2889 -466
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -12
- package/src/_effects_common.ts +17 -0
- package/src/_feature_validation.ts +34 -0
- package/src/_fft_options.ts +39 -0
- package/src/align_take.ts +64 -0
- package/src/analysis.ts +56 -3
- package/src/analysis_helpers.ts +7 -0
- package/src/audio.ts +106 -3
- package/src/codes.ts +39 -2
- package/src/effects_mastering.ts +97 -22
- package/src/effects_note_ops.ts +635 -0
- package/src/effects_percussive.ts +217 -0
- package/src/effects_separation.ts +150 -0
- package/src/effects_spectral.ts +60 -0
- package/src/effects_timepitch.ts +377 -0
- package/src/errors.ts +23 -1
- package/src/feature_core.ts +127 -2
- package/src/feature_decompose.ts +633 -0
- package/src/feature_inverse.ts +454 -0
- package/src/feature_loudness.ts +125 -0
- package/src/feature_music.ts +107 -14
- package/src/feature_pitch.ts +96 -1
- package/src/feature_spectral.ts +16 -611
- package/src/feature_spectrogram.ts +63 -450
- package/src/features.ts +36 -22
- package/src/index.ts +285 -30
- package/src/instrument_types.ts +645 -0
- package/src/live_audio.ts +27 -1
- package/src/mastering_chain.ts +184 -0
- package/src/mastering_core.ts +346 -32
- package/src/mastering_dynamics.ts +22 -11
- package/src/metering.ts +67 -24
- package/src/mixer.ts +212 -3
- package/src/mixing_assistant.ts +138 -0
- package/src/mixing_oneshot.ts +10 -5
- package/src/module_state.ts +24 -2
- package/src/playback_renderer.ts +252 -0
- package/src/polyphony.ts +279 -0
- package/src/project.ts +64 -24
- package/src/project_class.ts +502 -29
- package/src/project_internal.ts +151 -42
- package/src/project_synth.ts +67 -1
- package/src/project_types.ts +302 -270
- package/src/public_types.ts +122 -3
- package/src/public_types_acoustic.ts +112 -3
- package/src/public_types_mastering.ts +254 -73
- package/src/public_types_mixing.ts +363 -1
- package/src/public_types_music.ts +312 -2
- package/src/public_types_playback.ts +195 -0
- package/src/public_types_realtime.ts +39 -7
- package/src/public_types_repair.ts +446 -0
- package/src/public_types_spectral.ts +487 -1
- package/src/quick_analysis.ts +203 -26
- package/src/realtime_engine.ts +750 -28
- package/src/realtime_voice_changer.ts +55 -1
- package/src/repair_dereverb.ts +299 -0
- package/src/repair_impulsive.ts +395 -0
- package/src/repair_noise.ts +425 -0
- package/src/repair_trim.ts +226 -0
- package/src/sample_bank.ts +113 -0
- package/src/sonare.js.d.ts +1125 -30
- package/src/stream_analyzer.ts +36 -4
- package/src/stream_types.ts +37 -0
- package/src/streaming_mixing.ts +1 -1
- package/src/streaming_processors.ts +212 -10
- package/src/transcribe.ts +89 -0
- package/src/validation.ts +285 -11
- package/src/web_midi.ts +1 -6
- package/src/worker.ts +18 -2
- package/src/worklet/audio_types.ts +37 -0
- package/src/worklet/engine-mixer-facade.ts +444 -10
- package/src/worklet/engine-node.ts +85 -28
- package/src/worklet/engine-offline.ts +14 -8
- package/src/worklet/engine-parameter-facade.ts +21 -0
- package/src/worklet/engine-processor.ts +307 -71
- package/src/worklet/engine-register.ts +32 -18
- package/src/worklet/engine-strips.ts +280 -9
- package/src/worklet/engine-sync.ts +14 -6
- package/src/worklet/engine.ts +365 -31
- package/src/worklet/guards.ts +140 -44
- package/src/worklet/messages.ts +239 -2
- package/src/worklet/mixer-processor.ts +109 -47
- package/src/worklet/playback-processor.ts +300 -0
- package/src/worklet/protocol.ts +53 -3
- package/src/worklet/voice-changer-processor.ts +17 -11
- package/src/worklet.ts +22 -0
- package/src/effects_transform.ts +0 -718
- package/src/mastering_repair.ts +0 -273
package/src/stream_analyzer.ts
CHANGED
|
@@ -53,6 +53,7 @@ export function streamAnalyzerConfigDefaults(): StreamConfigDefaults {
|
|
|
53
53
|
*/
|
|
54
54
|
export class StreamAnalyzer {
|
|
55
55
|
private analyzer: WasmStreamAnalyzer;
|
|
56
|
+
private released = false;
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* Create a new StreamAnalyzer.
|
|
@@ -107,6 +108,9 @@ export class StreamAnalyzer {
|
|
|
107
108
|
/**
|
|
108
109
|
* Process audio samples.
|
|
109
110
|
*
|
|
111
|
+
* Feeding a finalized analyzer is an invalid-state error; call `reset()`
|
|
112
|
+
* first to start a new stream.
|
|
113
|
+
*
|
|
110
114
|
* @param samples - Audio samples (mono, float32)
|
|
111
115
|
*/
|
|
112
116
|
process(samples: Float32Array): void {
|
|
@@ -115,7 +119,8 @@ export class StreamAnalyzer {
|
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Process audio samples with a contiguous explicit sample offset. A gap,
|
|
118
|
-
* seek, or switch from `process()` requires `reset()` first
|
|
122
|
+
* seek, or switch from `process()` requires `reset()` first, as does feeding
|
|
123
|
+
* a finalized analyzer.
|
|
119
124
|
*
|
|
120
125
|
* @param samples - Audio samples (mono, float32)
|
|
121
126
|
* @param sampleOffset - Cumulative sample count at start of this chunk
|
|
@@ -126,6 +131,12 @@ export class StreamAnalyzer {
|
|
|
126
131
|
|
|
127
132
|
/**
|
|
128
133
|
* Drain any high-rate resampler tail, then zero-pad the final partial frame.
|
|
134
|
+
*
|
|
135
|
+
* Repeating a successful call is a no-op, and a call that fails leaves the
|
|
136
|
+
* stream un-finalized so a retry resumes from the same point. Call `reset()`
|
|
137
|
+
* before reusing the analyzer for another stream: more audio fed to a
|
|
138
|
+
* finalized analyzer is rejected rather than silently analyzed without the
|
|
139
|
+
* overlap context the finalized tail consumed.
|
|
129
140
|
*/
|
|
130
141
|
finalize(): void {
|
|
131
142
|
this.analyzer.finalize();
|
|
@@ -194,6 +205,7 @@ export class StreamAnalyzer {
|
|
|
194
205
|
droppedOutputFrames: s.droppedOutputFrames,
|
|
195
206
|
droppedChordProgressionEntries: s.droppedChordProgressionEntries,
|
|
196
207
|
droppedBarProgressionEntries: s.droppedBarProgressionEntries,
|
|
208
|
+
nonFiniteDiscardBlocks: s.nonFiniteDiscardBlocks,
|
|
197
209
|
estimate: {
|
|
198
210
|
bpm: s.estimate.bpm,
|
|
199
211
|
bpmConfidence: s.estimate.bpmConfidence,
|
|
@@ -274,7 +286,15 @@ export class StreamAnalyzer {
|
|
|
274
286
|
/**
|
|
275
287
|
* Set normalization gain for loud/compressed audio.
|
|
276
288
|
*
|
|
277
|
-
*
|
|
289
|
+
* Throws for a value outside 0.01..100 rather than clamping into it. The
|
|
290
|
+
* usual recipe (`gain = targetLevel / measuredLevel`) can land outside that
|
|
291
|
+
* range for a buffer that is not in the conventional ±1 float domain — an
|
|
292
|
+
* integer-scaled one asks for about 3e-4 — and no getter exposes the
|
|
293
|
+
* effective gain, so a clamped request would leave the analysis far off
|
|
294
|
+
* target undetectably. Convert such a buffer before feeding it instead.
|
|
295
|
+
*
|
|
296
|
+
* @param gain - Gain factor to apply (e.g., 0.5 for -6dB reduction, range
|
|
297
|
+
* 0.01..100)
|
|
278
298
|
*/
|
|
279
299
|
setNormalizationGain(gain: number): void {
|
|
280
300
|
this.analyzer.setNormalizationGain(gain);
|
|
@@ -283,7 +303,10 @@ export class StreamAnalyzer {
|
|
|
283
303
|
/**
|
|
284
304
|
* Set tuning reference frequency for non-standard tuning.
|
|
285
305
|
*
|
|
286
|
-
*
|
|
306
|
+
* Throws for a value outside 220..880 Hz rather than clamping into it, so
|
|
307
|
+
* this and `tuningRefHz` at create time accept exactly the same range.
|
|
308
|
+
*
|
|
309
|
+
* @param refHz - Reference frequency for A4 (default 440 Hz, range 220..880)
|
|
287
310
|
* @example
|
|
288
311
|
* // If audio is 1 semitone sharp (A4 = 466.16 Hz)
|
|
289
312
|
* analyzer.setTuningRefHz(466.16);
|
|
@@ -294,11 +317,20 @@ export class StreamAnalyzer {
|
|
|
294
317
|
this.analyzer.setTuningRefHz(refHz);
|
|
295
318
|
}
|
|
296
319
|
|
|
297
|
-
/** Release the underlying WASM object.
|
|
320
|
+
/** Release the underlying WASM object. Idempotent, as the Node facade is. */
|
|
298
321
|
delete(): void {
|
|
322
|
+
if (this.released) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
this.released = true;
|
|
299
326
|
this.analyzer.delete();
|
|
300
327
|
}
|
|
301
328
|
|
|
329
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
330
|
+
destroy(): void {
|
|
331
|
+
this.delete();
|
|
332
|
+
}
|
|
333
|
+
|
|
302
334
|
/** Alias for {@link delete}, kept for backward compatibility (historical name). */
|
|
303
335
|
dispose(): void {
|
|
304
336
|
this.delete();
|
package/src/stream_types.ts
CHANGED
|
@@ -14,9 +14,21 @@ export interface ChordChange {
|
|
|
14
14
|
* A chord detected at bar boundary (beat-synchronized)
|
|
15
15
|
*/
|
|
16
16
|
export interface BarChord {
|
|
17
|
+
/**
|
|
18
|
+
* Bar number, not the index of this entry in the array: bars with no
|
|
19
|
+
* confident chord are not recorded and the oldest entries are dropped at the
|
|
20
|
+
* history cap. Group bars by pattern position with this, never with the array
|
|
21
|
+
* index. In `votedPattern` it is the pattern position instead.
|
|
22
|
+
*/
|
|
17
23
|
barIndex: number;
|
|
18
24
|
root: PitchClass;
|
|
19
25
|
quality: ChordQuality;
|
|
26
|
+
/**
|
|
27
|
+
* Start of the bar, on the same timeline as `StreamFrame.timestamp`
|
|
28
|
+
* (including a `sampleOffset` anchor). Consecutive bars are `barDuration`
|
|
29
|
+
* apart rather than snapped to the analysis frame grid. Unused in
|
|
30
|
+
* `votedPattern`.
|
|
31
|
+
*/
|
|
20
32
|
startTime: number;
|
|
21
33
|
confidence: number;
|
|
22
34
|
}
|
|
@@ -35,6 +47,11 @@ export interface PatternScore {
|
|
|
35
47
|
export interface ProgressiveEstimate {
|
|
36
48
|
bpm: number;
|
|
37
49
|
bpmConfidence: number;
|
|
50
|
+
/**
|
|
51
|
+
* Tempo candidates the most recent BPM estimate chose from; 0 until an
|
|
52
|
+
* estimate has run. Same quantity as the batch analysis result's field of the
|
|
53
|
+
* same name.
|
|
54
|
+
*/
|
|
38
55
|
bpmCandidateCount: number;
|
|
39
56
|
key: PitchClass;
|
|
40
57
|
keyMinor: boolean;
|
|
@@ -54,6 +71,11 @@ export interface ProgressiveEstimate {
|
|
|
54
71
|
allPatternScores: PatternScore[];
|
|
55
72
|
accumulatedSeconds: number;
|
|
56
73
|
usedFrames: number;
|
|
74
|
+
/**
|
|
75
|
+
* True when the key or BPM was re-estimated since the previous stats
|
|
76
|
+
* snapshot. One change sets it on exactly one snapshot however the caller
|
|
77
|
+
* chunks its input, and a call that produced no frame does not repeat it.
|
|
78
|
+
*/
|
|
57
79
|
updated: boolean;
|
|
58
80
|
}
|
|
59
81
|
|
|
@@ -68,6 +90,19 @@ export interface AnalyzerStats {
|
|
|
68
90
|
droppedOutputFrames: number;
|
|
69
91
|
droppedChordProgressionEntries: number;
|
|
70
92
|
droppedBarProgressionEntries: number;
|
|
93
|
+
/**
|
|
94
|
+
* Blocks in which a non-finite input sample was replaced before it could
|
|
95
|
+
* reach the analyzer's recursive state.
|
|
96
|
+
*
|
|
97
|
+
* Unlike the drop counts above, nothing is missing from the output: every
|
|
98
|
+
* estimate is produced as usual and simply stops describing the input, so
|
|
99
|
+
* this is the only report that the stream was degraded. The unit is one
|
|
100
|
+
* {@link StreamAnalyzer.process} call, never a sample, so a block carrying a
|
|
101
|
+
* thousand NaNs adds one. Cleared by {@link StreamAnalyzer.reset} alongside
|
|
102
|
+
* the drop counts, because that call rebuilds the timeline and the count
|
|
103
|
+
* describes a segment rather than the analyzer.
|
|
104
|
+
*/
|
|
105
|
+
nonFiniteDiscardBlocks: number;
|
|
71
106
|
estimate: ProgressiveEstimate;
|
|
72
107
|
}
|
|
73
108
|
|
|
@@ -164,6 +199,8 @@ export interface StreamConfig {
|
|
|
164
199
|
nMels?: number;
|
|
165
200
|
fmin?: number;
|
|
166
201
|
fmax?: number;
|
|
202
|
+
/** A4 tuning reference in Hz. Defaults to 440; must be within 220..880, the
|
|
203
|
+
* same range `setTuningRefHz` accepts live. */
|
|
167
204
|
tuningRefHz?: number;
|
|
168
205
|
/** Unsupported: no read path surfaces per-frame magnitude spectra. */
|
|
169
206
|
computeMagnitude?: boolean;
|
package/src/streaming_mixing.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
EqBand,
|
|
5
5
|
EqMatchOptions,
|
|
6
6
|
EqSpectrumSnapshot,
|
|
7
|
+
EqStereoPlacement,
|
|
7
8
|
StreamingEqualizerConfig,
|
|
8
9
|
StreamingMasteringChainConfig,
|
|
9
10
|
StreamingRetuneConfig,
|
|
@@ -21,6 +22,14 @@ type EqPhaseMode =
|
|
|
21
22
|
| 'linear_phase'
|
|
22
23
|
| number;
|
|
23
24
|
|
|
25
|
+
const EQ_PLACEMENTS: Record<string, number> = {
|
|
26
|
+
stereo: 0,
|
|
27
|
+
left: 1,
|
|
28
|
+
right: 2,
|
|
29
|
+
mid: 3,
|
|
30
|
+
side: 4,
|
|
31
|
+
};
|
|
32
|
+
|
|
24
33
|
const EQ_PHASE_MODES: Record<string, number> = {
|
|
25
34
|
zero: 1,
|
|
26
35
|
'zero-latency': 1,
|
|
@@ -41,14 +50,51 @@ const EQ_PHASE_MODES: Record<string, number> = {
|
|
|
41
50
|
* Block-by-block streaming variant of {@link masteringChain}.
|
|
42
51
|
*
|
|
43
52
|
* Maintains processor state across {@link processMono}/{@link processStereo}
|
|
44
|
-
* calls. Only ProcessorBase-backed stages are supported.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
53
|
+
* calls. Only ProcessorBase-backed stages are supported: `eq.tilt`,
|
|
54
|
+
* `dynamics.deesser`, `dynamics.transientShaper`, `dynamics.compressor`,
|
|
55
|
+
* `dynamics.multibandComp`, `saturation.tape`, `saturation.exciter`,
|
|
56
|
+
* `spectral.airBand`, `stereo.imager` (stereo only), `stereo.monoMaker`
|
|
57
|
+
* (stereo only), `maximizer.truePeakLimiter`. Configurations that enable ANY of
|
|
58
|
+
* the five whole-signal repair stages (`repair.declick`, `repair.declip`,
|
|
59
|
+
* `repair.decrackle`, `repair.dehum`, `repair.dereverb`) throw at construction.
|
|
60
|
+
*
|
|
61
|
+
* `repair.denoise` runs here, but only with a noise estimator that is recursive
|
|
62
|
+
* in time. Its default ranks every frame of the whole signal by energy, which a
|
|
63
|
+
* stream never reaches the end of, so it is refused by name rather than
|
|
64
|
+
* substituted; set `repair.denoise.noiseEstimator` to `1` (MCRA), `2` (IMCRA)
|
|
65
|
+
* or `3` (speech-presence probability). The two minimum-tracking estimators
|
|
66
|
+
* (`1` and `2`) seed their noise floor from the first frame they see and hold it
|
|
67
|
+
* for the half second their minimum window spans, so a stream opened in the
|
|
68
|
+
* middle of the programme is over-suppressed until it turns over; `3` tracks no
|
|
69
|
+
* minimum and is unaffected. Prefer `3` past that opening too: `1` and `2`
|
|
70
|
+
* over-report the floor for as long as the programme stays intermittent, and on
|
|
71
|
+
* a gated tone they leave the result below the untreated input.
|
|
72
|
+
*
|
|
73
|
+
* An enabled `loudness` stage also throws unless
|
|
74
|
+
* {@link StreamingMasteringChainConfig.loudnessStaticGainDb} supplies a
|
|
75
|
+
* precomputed normalization gain.
|
|
48
76
|
*
|
|
49
77
|
* Call {@link delete} (or use a `try/finally`) to release the underlying WASM
|
|
50
78
|
* object — the embind handle is not garbage-collected automatically.
|
|
51
79
|
*
|
|
80
|
+
* Reachable from the AudioWorklet realm through the `sonare/worklet` entry, but
|
|
81
|
+
* the realtime contract is the caller's to keep:
|
|
82
|
+
*
|
|
83
|
+
* - {@link prepare} builds the processors and allocates. Call it once from a
|
|
84
|
+
* message handler, never from `AudioWorkletProcessor.process()`.
|
|
85
|
+
* - {@link processMono}/{@link processStereo} return fresh arrays. On the render
|
|
86
|
+
* thread, reuse the returned reference for the block rather than retaining it.
|
|
87
|
+
* - An enabled `loudness` stage needs `loudnessStaticGainDb` measured offline,
|
|
88
|
+
* because whole-signal integrated LUFS cannot be measured block by block. Pass
|
|
89
|
+
* `loudnessStaticGainPeakDb` too and the static gain is clamped exactly as the
|
|
90
|
+
* offline chain clamps it, so the live preview matches the render.
|
|
91
|
+
* - {@link flush} output starts {@link latencySamples} samples early; discard
|
|
92
|
+
* that many leading samples when time alignment matters.
|
|
93
|
+
*
|
|
94
|
+
* The chain is a host-side stage, not an engine insert: it does not participate
|
|
95
|
+
* in the engine's PDC or bypass, so latency compensation against other engine
|
|
96
|
+
* outputs is also the caller's.
|
|
97
|
+
*
|
|
52
98
|
* @example
|
|
53
99
|
* ```typescript
|
|
54
100
|
* const chain = new StreamingMasteringChain({ eq: { tiltDb: 1.0 } });
|
|
@@ -62,6 +108,7 @@ const EQ_PHASE_MODES: Record<string, number> = {
|
|
|
62
108
|
*/
|
|
63
109
|
export class StreamingMasteringChain {
|
|
64
110
|
private chain: import('./sonare.js').WasmStreamingMasteringChain;
|
|
111
|
+
private released = false;
|
|
65
112
|
|
|
66
113
|
constructor(config: StreamingMasteringChainConfig) {
|
|
67
114
|
const module = getSonareModule();
|
|
@@ -134,10 +181,78 @@ export class StreamingMasteringChain {
|
|
|
134
181
|
return this.chain.stageNames();
|
|
135
182
|
}
|
|
136
183
|
|
|
137
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Samples a stage replaced with a finite in-domain one, keeping the output
|
|
186
|
+
* finite and in range.
|
|
187
|
+
*
|
|
188
|
+
* A non-finite sample supplied by the caller is rejected before any stage
|
|
189
|
+
* runs, so a replacement is always of a value a stage itself produced.
|
|
190
|
+
*
|
|
191
|
+
* Only the true-peak limiters replace anything, so with the maximizer's
|
|
192
|
+
* limiter and the loudness stage both disabled a zero here means no stage was
|
|
193
|
+
* able to replace anything rather than that nothing needed replacing.
|
|
194
|
+
*
|
|
195
|
+
* Cumulative over every block since {@link prepare}, and aggregated over the
|
|
196
|
+
* stages and channels, so it identifies neither which block nor which stage.
|
|
197
|
+
* Read it per block and compare against the previous reading to localize one.
|
|
198
|
+
*
|
|
199
|
+
* {@link prepare} rebuilds the stages and so clears it; {@link reset} does
|
|
200
|
+
* not, because it drops processor state without rebuilding.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* chain.processMono(block);
|
|
205
|
+
* if (chain.nonFiniteSubstitutionCount() > previous) {
|
|
206
|
+
* // the block just produced is not derived from `block` everywhere
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
nonFiniteSubstitutionCount(): number {
|
|
211
|
+
return this.chain.nonFiniteSubstitutionCount();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Processing calls in which a stage discarded its own recursive state
|
|
216
|
+
* because a non-finite value had reached it.
|
|
217
|
+
*
|
|
218
|
+
* The companion to {@link nonFiniteSubstitutionCount}, and not the same
|
|
219
|
+
* measurement -- a caller who assumes they are will read one and think
|
|
220
|
+
* they have the other. That one counts SAMPLES a stage replaced and so
|
|
221
|
+
* sums across stages; a discard is a whole stage returning to its
|
|
222
|
+
* post-reset value and is counted once per call however many stages did
|
|
223
|
+
* it. A stage may run more than once per call, which is why this is a
|
|
224
|
+
* delta over the call and never a sum.
|
|
225
|
+
*
|
|
226
|
+
* Non-finite input is rejected before any stage runs, so what a stage
|
|
227
|
+
* discards is always state it produced itself -- a finite sample large
|
|
228
|
+
* enough to overflow inside a filter, most often. Unlike the substitution
|
|
229
|
+
* count every stage can contribute, so a zero here means no stage
|
|
230
|
+
* discarded rather than that none could.
|
|
231
|
+
*
|
|
232
|
+
* Both {@link processMono}/{@link processStereo} and
|
|
233
|
+
* {@link flushMono}/{@link flushStereo} count, since a flush drives the
|
|
234
|
+
* same stages. {@link prepare} rebuilds the stages and so clears it (as it
|
|
235
|
+
* does {@link nonFiniteSubstitutionCount}, so the two counters on one
|
|
236
|
+
* handle share an epoch); {@link reset} does not, because it drops
|
|
237
|
+
* processor state without rebuilding.
|
|
238
|
+
*/
|
|
239
|
+
nonFiniteDiscardCount(): number {
|
|
240
|
+
return this.chain.nonFiniteDiscardCount();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Release the underlying WASM object. Idempotent, as the Node facade is. */
|
|
138
244
|
delete(): void {
|
|
245
|
+
if (this.released) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.released = true;
|
|
139
249
|
this.chain.delete();
|
|
140
250
|
}
|
|
251
|
+
|
|
252
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
253
|
+
destroy(): void {
|
|
254
|
+
this.delete();
|
|
255
|
+
}
|
|
141
256
|
}
|
|
142
257
|
|
|
143
258
|
// ============================================================================
|
|
@@ -167,6 +282,7 @@ export class StreamingMasteringChain {
|
|
|
167
282
|
*/
|
|
168
283
|
export class StreamingEqualizer {
|
|
169
284
|
private eq: import('./sonare.js').WasmStreamingEqualizer;
|
|
285
|
+
private released = false;
|
|
170
286
|
|
|
171
287
|
constructor(config: StreamingEqualizerConfig = {}) {
|
|
172
288
|
const module = getSonareModule();
|
|
@@ -249,6 +365,33 @@ export class StreamingEqualizer {
|
|
|
249
365
|
return this.eq.latencySamples();
|
|
250
366
|
}
|
|
251
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Number of blocks in which the EQ discarded recursive state because a
|
|
370
|
+
* non-finite value had reached it.
|
|
371
|
+
*
|
|
372
|
+
* Advisory telemetry, and the only thing that separates a degraded EQ from
|
|
373
|
+
* a clean one. A discard returns the affected filter cells to their
|
|
374
|
+
* post-reset value, so the EQ recovers in silence and the output stays
|
|
375
|
+
* finite and in range while carrying samples unrelated to the input;
|
|
376
|
+
* nothing else reports that this happened.
|
|
377
|
+
*
|
|
378
|
+
* The count covers every IIR plane the band layout uses -- stereo, per
|
|
379
|
+
* channel, and mid/side -- together with the automatic output gain and the
|
|
380
|
+
* detector state the dynamic bands drive. Linear-phase bands are not
|
|
381
|
+
* included and have nothing to include: an FIR keeps no recursive state,
|
|
382
|
+
* so a non-finite sample leaves its history on its own.
|
|
383
|
+
*
|
|
384
|
+
* Unlike a mixer strip's meters, nothing here lags: this EQ has no meter of
|
|
385
|
+
* its own, so a discard is always attributed to the block that carried it.
|
|
386
|
+
*
|
|
387
|
+
* Cumulative since this handle was created and never cleared, so two
|
|
388
|
+
* readings bracket a span of audio. The unit is one processed block, never
|
|
389
|
+
* a channel or a plane.
|
|
390
|
+
*/
|
|
391
|
+
nonFiniteDiscardCount(): number {
|
|
392
|
+
return this.eq.nonFiniteDiscardCount();
|
|
393
|
+
}
|
|
394
|
+
|
|
252
395
|
/**
|
|
253
396
|
* Process one mono block, returning the equalized samples (same length).
|
|
254
397
|
*/
|
|
@@ -269,6 +412,39 @@ export class StreamingEqualizer {
|
|
|
269
412
|
return this.eq.processStereo(left, right);
|
|
270
413
|
}
|
|
271
414
|
|
|
415
|
+
/**
|
|
416
|
+
* The composite magnitude of the bands, in dB, at each requested frequency —
|
|
417
|
+
* the curve to draw over {@link spectrum}.
|
|
418
|
+
*
|
|
419
|
+
* Built from the same coefficient design, tilt expansion and cut-slope
|
|
420
|
+
* cascade the audio path uses, so it states what the equalizer does rather
|
|
421
|
+
* than what its settings look like, and it carries the output gain, the gain
|
|
422
|
+
* scale and whatever each dynamic band is applying at the moment of the call.
|
|
423
|
+
* Disabled, bypassed and — when anything is soloed — unsoloed bands drop out,
|
|
424
|
+
* and a soloed band is drawn as the band pass it is heard as.
|
|
425
|
+
*
|
|
426
|
+
* `placement` selects which signal path the curve is for. A band placed on
|
|
427
|
+
* `'Stereo'` is on every path; one placed elsewhere appears only on its own,
|
|
428
|
+
* a mid band having no per-channel magnitude to fold into a left or right
|
|
429
|
+
* curve. Frequencies are clamped to [0 Hz, Nyquist].
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```ts
|
|
433
|
+
* const freqs = new Float32Array([100, 1000, 10000]);
|
|
434
|
+
* const db = eq.magnitudeResponse(freqs);
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
magnitudeResponse(
|
|
438
|
+
frequenciesHz: Float32Array,
|
|
439
|
+
placement: EqStereoPlacement = 'Stereo',
|
|
440
|
+
): Float32Array {
|
|
441
|
+
const value = EQ_PLACEMENTS[placement.toLowerCase()];
|
|
442
|
+
if (value === undefined) {
|
|
443
|
+
throw new Error(`unknown EQ band placement: ${placement}`);
|
|
444
|
+
}
|
|
445
|
+
return this.eq.magnitudeResponse(value, frequenciesHz);
|
|
446
|
+
}
|
|
447
|
+
|
|
272
448
|
/**
|
|
273
449
|
* Read the latest pre/post spectrum snapshot for metering. `seq` increments
|
|
274
450
|
* each time a new snapshot is published.
|
|
@@ -288,10 +464,19 @@ export class StreamingEqualizer {
|
|
|
288
464
|
this.eq.match(source, reference, options as Record<string, unknown>);
|
|
289
465
|
}
|
|
290
466
|
|
|
291
|
-
/** Release the underlying WASM object.
|
|
467
|
+
/** Release the underlying WASM object. Idempotent, as the Node facade is. */
|
|
292
468
|
delete(): void {
|
|
469
|
+
if (this.released) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
this.released = true;
|
|
293
473
|
this.eq.delete();
|
|
294
474
|
}
|
|
475
|
+
|
|
476
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
477
|
+
destroy(): void {
|
|
478
|
+
this.delete();
|
|
479
|
+
}
|
|
295
480
|
}
|
|
296
481
|
|
|
297
482
|
// ============================================================================
|
|
@@ -307,6 +492,7 @@ export class StreamingEqualizer {
|
|
|
307
492
|
*/
|
|
308
493
|
export class StreamingRetune {
|
|
309
494
|
private retune: import('./sonare.js').WasmStreamingRetune;
|
|
495
|
+
private released = false;
|
|
310
496
|
|
|
311
497
|
constructor(config: StreamingRetuneConfig = {}) {
|
|
312
498
|
const module = getSonareModule();
|
|
@@ -327,14 +513,16 @@ export class StreamingRetune {
|
|
|
327
513
|
}
|
|
328
514
|
|
|
329
515
|
/**
|
|
330
|
-
* Update
|
|
331
|
-
* {@link prepare} call
|
|
516
|
+
* Update the live controls; omitted keys keep their current value. Changing
|
|
517
|
+
* `grainSize` takes effect after the next {@link prepare} call, and an
|
|
518
|
+
* omitted `grainSize` keeps whatever was last requested — including the `0`
|
|
519
|
+
* sentinel, so a re-{@link prepare} at another sample rate re-derives it.
|
|
332
520
|
*/
|
|
333
521
|
setConfig(config: StreamingRetuneConfig): void {
|
|
334
522
|
this.retune.setConfig(config as Record<string, unknown>);
|
|
335
523
|
}
|
|
336
524
|
|
|
337
|
-
/**
|
|
525
|
+
/** The currently applied controls, with `grainSize` as the effective one. */
|
|
338
526
|
config(): Required<StreamingRetuneConfig> {
|
|
339
527
|
return this.retune.config();
|
|
340
528
|
}
|
|
@@ -344,13 +532,27 @@ export class StreamingRetune {
|
|
|
344
532
|
return this.retune.grainSize();
|
|
345
533
|
}
|
|
346
534
|
|
|
535
|
+
/** Fixed overlap-add latency in samples (one grain); 0 before prepare. */
|
|
536
|
+
latencySamples(): number {
|
|
537
|
+
return this.retune.latencySamples();
|
|
538
|
+
}
|
|
539
|
+
|
|
347
540
|
/** Process one mono block, returning the shifted samples (same length). */
|
|
348
541
|
processMono(samples: Float32Array): Float32Array {
|
|
349
542
|
return this.retune.processMono(samples);
|
|
350
543
|
}
|
|
351
544
|
|
|
352
|
-
/** Release the underlying WASM object.
|
|
545
|
+
/** Release the underlying WASM object. Idempotent, as the Node facade is. */
|
|
353
546
|
delete(): void {
|
|
547
|
+
if (this.released) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
this.released = true;
|
|
354
551
|
this.retune.delete();
|
|
355
552
|
}
|
|
553
|
+
|
|
554
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
555
|
+
destroy(): void {
|
|
556
|
+
this.delete();
|
|
557
|
+
}
|
|
356
558
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { projectModule } from './project_internal';
|
|
2
|
+
import type { TranscribeOptions, TranscribeResult } from './project_types';
|
|
3
|
+
import { assertSampleRate, assertSamples } from './validation';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Canonical request form for {@link transcribe}.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately does NOT extend `ValidateOptions`. That option's contract is
|
|
9
|
+
* that skipping the JS-side scan is safe because the native layer re-validates
|
|
10
|
+
* with an equivalent result -- and here it would not be equivalent: the
|
|
11
|
+
* transcription C ABI (`sonare_transcribe`) does re-validate the buffer,
|
|
12
|
+
* including a non-finite scan, but a rejection there surfaces as a
|
|
13
|
+
* `SonareError` `InvalidParameter`, not the `RangeError` this function
|
|
14
|
+
* documents and every other empty/non-finite/rate check on this surface
|
|
15
|
+
* raises. A `{ validate: false }` here would silently change the thrown error
|
|
16
|
+
* class instead of skipping a redundant check. The scan is also nearly free
|
|
17
|
+
* against this pipeline, which already reads every sample several times.
|
|
18
|
+
*/
|
|
19
|
+
export interface TranscribeRequest extends TranscribeOptions {
|
|
20
|
+
/** Mono source audio. Must be non-empty and all-finite. */
|
|
21
|
+
samples: Float32Array;
|
|
22
|
+
/** Sample rate of `samples` in Hz, `[8000, 384000]`. */
|
|
23
|
+
sampleRate: number;
|
|
24
|
+
/**
|
|
25
|
+
* Tempo the PPQ grid is built on, in BPM. **Omit to have it detected** from
|
|
26
|
+
* `samples`, which costs an onset/tempo pass; a detector that finds nothing
|
|
27
|
+
* usable falls back to 120 rather than failing the transcription.
|
|
28
|
+
*
|
|
29
|
+
* Supplying a tempo is not a claim about the audio — it is the coordinate
|
|
30
|
+
* system the events come back in. Twice the tempo is twice as many beats per
|
|
31
|
+
* second, so the same audio lands on twice the ppq.
|
|
32
|
+
*/
|
|
33
|
+
tempoBpm?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Transcribes mono audio into MIDI events on a constant-tempo grid.
|
|
38
|
+
*
|
|
39
|
+
* This joins parts that already exist rather than adding a detector: the note
|
|
40
|
+
* spans and their measured pitch and level come from the monophonic (pYIN) or
|
|
41
|
+
* polyphonic (multi-F0) chain, and the output is the flat
|
|
42
|
+
* {@link ProjectMidiEvent} shape {@link Project.setMidiEvents} takes, so
|
|
43
|
+
* nothing is left to convert.
|
|
44
|
+
*
|
|
45
|
+
* Finding no notes is not an error. Silence, and material the chain cannot
|
|
46
|
+
* resolve, come back with an empty `events` array and `noteCount` 0, and
|
|
47
|
+
* `tempoBpm` still reports the tempo that was used or detected.
|
|
48
|
+
*
|
|
49
|
+
* Three things are deliberately **not** done here, because the library already
|
|
50
|
+
* does each of them somewhere else and a second implementation would drift:
|
|
51
|
+
*
|
|
52
|
+
* - **Quantizing to a grid** — {@link Project.bakeMidiFx}'s `quantizePpq` /
|
|
53
|
+
* `quantizeStrength`.
|
|
54
|
+
* - **Detecting and installing a tempo map** — {@link Project.autoTempo}. The
|
|
55
|
+
* `tempoBpm` fallback here builds one constant-tempo grid for this call and
|
|
56
|
+
* installs nothing; use {@link Project.transcribeToClip} to transcribe onto a
|
|
57
|
+
* project's real map.
|
|
58
|
+
* - **Annotating key and chords** — {@link Project.annotateKeys} /
|
|
59
|
+
* {@link Project.annotateChords}.
|
|
60
|
+
*
|
|
61
|
+
* The tuning reference is likewise not measured — see
|
|
62
|
+
* {@link TranscribeOptions.referenceHz}.
|
|
63
|
+
*
|
|
64
|
+
* @throws {RangeError} on empty `samples`, a non-finite sample, or a
|
|
65
|
+
* `sampleRate` outside `[8000, 384000]`
|
|
66
|
+
* @throws {SonareError} `InvalidParameter` on an option outside its domain —
|
|
67
|
+
* a non-negative `velocityFloorDb`, a `fixedVelocity` outside `[1, 127]`, a
|
|
68
|
+
* `group` or `channel` outside `[0, 15]`, an `fmax` at or below `fmin`, or a
|
|
69
|
+
* written `0` on any field but `group` and `channel` — or `NotSupported`
|
|
70
|
+
* when the library was built without the pitch editor
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const { events, noteCount, tempoBpm } = transcribe({ samples, sampleRate, tempoBpm: 120 });
|
|
75
|
+
* const project = new Project();
|
|
76
|
+
* const { clipId } = project.addMidiClip(0, 16); // 4 bars at 4/4, in quarter notes (PPQ)
|
|
77
|
+
* project.setMidiEvents(clipId, events);
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function transcribe(request: TranscribeRequest): TranscribeResult {
|
|
81
|
+
assertSamples('transcribe', request.samples, true);
|
|
82
|
+
assertSampleRate('transcribe', request.sampleRate);
|
|
83
|
+
return projectModule().transcribe(
|
|
84
|
+
request.samples,
|
|
85
|
+
request.sampleRate,
|
|
86
|
+
request.tempoBpm,
|
|
87
|
+
request as unknown as TranscribeOptions,
|
|
88
|
+
);
|
|
89
|
+
}
|