@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/module_state.ts
CHANGED
|
@@ -22,7 +22,7 @@ interface NativeExceptionInfo {
|
|
|
22
22
|
* Returns null when the thrown value is neither (a genuine JS error), so the
|
|
23
23
|
* caller rethrows it unchanged.
|
|
24
24
|
*/
|
|
25
|
-
function nativeExceptionPtr(error: unknown): number | null {
|
|
25
|
+
export function nativeExceptionPtr(error: unknown): number | null {
|
|
26
26
|
if (typeof error === 'number') {
|
|
27
27
|
return error;
|
|
28
28
|
}
|
|
@@ -38,7 +38,16 @@ function nativeExceptionPtr(error: unknown): number | null {
|
|
|
38
38
|
/**
|
|
39
39
|
* Turn a thrown native exception pointer into a {@link SonareError}. The bound
|
|
40
40
|
* `sonareExceptionInfo` decodes the pointer back into { code, codeName,
|
|
41
|
-
* message }
|
|
41
|
+
* message }, then `sonareReleaseException` drops the reference emscripten's
|
|
42
|
+
* `__cxa_throw` took before rethrowing the pointer into JS.
|
|
43
|
+
*
|
|
44
|
+
* The release is mandatory, not an optimization: no C++ frame catches the
|
|
45
|
+
* exception, so that reference is the only one and nothing else ever drops it.
|
|
46
|
+
* Every rejected input would otherwise leak its exception object for the
|
|
47
|
+
* lifetime of the module — and rejection-as-control-flow (re-validating markers
|
|
48
|
+
* or clips on each edit, a scrub handle seeking every pointer move) is a
|
|
49
|
+
* documented usage of this API. It runs in a `finally` so a decode failure
|
|
50
|
+
* still frees, and after decoding because freeing invalidates the message.
|
|
42
51
|
*/
|
|
43
52
|
function makeSonareError(raw: SonareModule, thrown: number): SonareError {
|
|
44
53
|
let code: number = ErrorCode.Unknown;
|
|
@@ -55,6 +64,13 @@ function makeSonareError(raw: SonareModule, thrown: number): SonareError {
|
|
|
55
64
|
}
|
|
56
65
|
} catch {
|
|
57
66
|
// Fall back to the generic message if decoding fails.
|
|
67
|
+
} finally {
|
|
68
|
+
try {
|
|
69
|
+
raw.sonareReleaseException(thrown);
|
|
70
|
+
} catch {
|
|
71
|
+
// A module built before the release binding existed still yields an error
|
|
72
|
+
// object; it just keeps leaking, which is what this replaces.
|
|
73
|
+
}
|
|
58
74
|
}
|
|
59
75
|
return new SonareError(code, codeName, message);
|
|
60
76
|
}
|
|
@@ -85,6 +101,12 @@ function wrapModuleErrors(raw: SonareModule): SonareModule {
|
|
|
85
101
|
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof Promise) {
|
|
86
102
|
return value;
|
|
87
103
|
}
|
|
104
|
+
// Plain result data carries no native methods, and a Proxy cannot be
|
|
105
|
+
// structured-cloned, so wrapping it would block postMessage to a worker.
|
|
106
|
+
const proto = Object.getPrototypeOf(value);
|
|
107
|
+
if (Array.isArray(value) || proto === Object.prototype || proto === null) {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
88
110
|
const objectValue = value as object;
|
|
89
111
|
const cached = objectCache.get(objectValue);
|
|
90
112
|
if (cached) {
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { getSonareModule } from './module_state';
|
|
2
|
+
import type {
|
|
3
|
+
PlaybackDiagnostics,
|
|
4
|
+
PlaybackRendererConfig,
|
|
5
|
+
PlaybackRendererOptions,
|
|
6
|
+
RenderPlaybackRequest,
|
|
7
|
+
RenderPlaybackResult,
|
|
8
|
+
} from './public_types_playback';
|
|
9
|
+
import type { WasmHrtfSet, WasmPlaybackLoudnessMeter, WasmPlaybackRenderer } from './sonare.js';
|
|
10
|
+
|
|
11
|
+
function configJsonText(config: PlaybackRendererConfig | string): string {
|
|
12
|
+
return typeof config === 'string' ? config : JSON.stringify(config);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Reads {@link HrtfSet}'s private `native`: TypeScript's `private` is a
|
|
17
|
+
* compile-time rule, so one reviewed read here beats widening the class with a
|
|
18
|
+
* handle accessor no caller wants.
|
|
19
|
+
*/
|
|
20
|
+
function nativeHrtf(hrtf: HrtfSet | undefined): WasmHrtfSet | null {
|
|
21
|
+
if (hrtf === undefined) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (!(hrtf instanceof HrtfSet)) {
|
|
25
|
+
throw new TypeError('hrtf must be an HrtfSet');
|
|
26
|
+
}
|
|
27
|
+
return (hrtf as unknown as { native: WasmHrtfSet }).native;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* An HRTF set (SHRF v1): the direct-sound impulse responses and inter-aural
|
|
32
|
+
* time delays a headphones-target {@link PlaybackRenderer} convolves each
|
|
33
|
+
* virtual speaker's signal with.
|
|
34
|
+
*
|
|
35
|
+
* This build embeds no HRTF data. The package ships the default set as the
|
|
36
|
+
* asset `@libraz/libsonare/hrtf/default.shrf`; fetch or read it and pass the
|
|
37
|
+
* bytes to {@link HrtfSet.fromBytes}. A renderer built from a set keeps its
|
|
38
|
+
* own copy, so deleting the set right after construction is safe.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const bytes = new Uint8Array(await (await fetch(hrtfUrl)).arrayBuffer());
|
|
43
|
+
* const hrtf = HrtfSet.fromBytes(bytes);
|
|
44
|
+
* const renderer = new PlaybackRenderer({ config: {}, hrtf, sampleRate: 48000 });
|
|
45
|
+
* hrtf.delete();
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export class HrtfSet {
|
|
49
|
+
private native: WasmHrtfSet;
|
|
50
|
+
private released = false;
|
|
51
|
+
|
|
52
|
+
private constructor(native: WasmHrtfSet) {
|
|
53
|
+
this.native = native;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Builds an HRTF set from SHRF v1 bytes; malformed data throws. */
|
|
57
|
+
static fromBytes(bytes: Uint8Array): HrtfSet {
|
|
58
|
+
return new HrtfSet(getSonareModule().createHrtfSet(bytes));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Releases the native handle. Idempotent, as the Node facade is. */
|
|
62
|
+
delete(): void {
|
|
63
|
+
if (this.released) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.released = true;
|
|
67
|
+
this.native.delete();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
71
|
+
destroy(): void {
|
|
72
|
+
this.delete();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Renders decoded movie audio (mono / stereo / 5.1 / 7.1 PCM) to headphones
|
|
78
|
+
* (binaural, head tracking, room model) or to stereo / 5.1 / 7.1 speakers
|
|
79
|
+
* (upmix, loudness alignment, night-mode DRC, dialogue level, speaker
|
|
80
|
+
* calibration, bass management).
|
|
81
|
+
*
|
|
82
|
+
* `config` follows `schemas/playback-renderer-config.schema.json`; see
|
|
83
|
+
* {@link PlaybackRendererConfig}. A headphones target requires `hrtf`.
|
|
84
|
+
*
|
|
85
|
+
* `processPlanar` copies every plane through the embind boundary, so it suits
|
|
86
|
+
* the main thread; like `processInterleaved`, a non-finite sample is replaced
|
|
87
|
+
* with 0 and counted rather than refused. The AudioWorklet path is
|
|
88
|
+
* `SonarePlaybackWorkletProcessor` in the worklet bundle.
|
|
89
|
+
*/
|
|
90
|
+
export class PlaybackRenderer {
|
|
91
|
+
private native: WasmPlaybackRenderer;
|
|
92
|
+
private released = false;
|
|
93
|
+
|
|
94
|
+
constructor(options: PlaybackRendererOptions) {
|
|
95
|
+
this.native = getSonareModule().createPlaybackRenderer(
|
|
96
|
+
configJsonText(options.config),
|
|
97
|
+
nativeHrtf(options.hrtf),
|
|
98
|
+
options.sampleRate ?? 48000,
|
|
99
|
+
options.maxBlockSize ?? 1024,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Renders one planar block; every plane must carry the same frame count, at
|
|
105
|
+
* most `maxBlockSize` (0 is a no-op). With a fixed input layout the plane
|
|
106
|
+
* count must equal {@link inputChannels}; with `input.layout: "auto"` it
|
|
107
|
+
* must be 1, 2, 6 or 8, and a change switches the input layout without
|
|
108
|
+
* changing the latency. Non-finite input samples are replaced with 0 and
|
|
109
|
+
* counted ({@link nonFiniteDiscardCount}).
|
|
110
|
+
*/
|
|
111
|
+
processPlanar(planes: Float32Array[]): Float32Array[] {
|
|
112
|
+
return this.native.processPlanar(planes);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Interleaved variant of {@link processPlanar}. Non-finite input samples are replaced with 0 and counted. */
|
|
116
|
+
processInterleaved(samples: Float32Array, inChannels: number): Float32Array {
|
|
117
|
+
return this.native.processInterleaved(samples, inChannels);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Applies a complete configuration document; a changed prepare key throws. */
|
|
121
|
+
setConfig(config: PlaybackRendererConfig | string): void {
|
|
122
|
+
this.native.setConfig(configJsonText(config));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The current complete configuration document. */
|
|
126
|
+
config(): PlaybackRendererConfig {
|
|
127
|
+
return JSON.parse(this.native.configJson()) as PlaybackRendererConfig;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Publishes the listener head orientation in degrees: right-handed,
|
|
132
|
+
* positive yaw turns the head right, positive pitch looks up, positive roll
|
|
133
|
+
* lowers the right ear. Ignored by a speakers target; a non-finite angle is
|
|
134
|
+
* ignored.
|
|
135
|
+
*/
|
|
136
|
+
setHeadOrientation(yawDeg: number, pitchDeg = 0, rollDeg = 0): void {
|
|
137
|
+
this.native.setHeadOrientation(yawDeg, pitchDeg, rollDeg);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Clears DSP state (filters, FIFOs, dynamics, convolution history, pending
|
|
142
|
+
* input-layout drains). Configuration and head pose are kept. Call it after
|
|
143
|
+
* a seek, from the thread that processes.
|
|
144
|
+
*/
|
|
145
|
+
reset(): void {
|
|
146
|
+
this.native.reset();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Renderer latency in samples (headphones: near ear). Depends only on the
|
|
151
|
+
* target, the sample rate and distance compensation, never on realtime keys
|
|
152
|
+
* or the input layout.
|
|
153
|
+
*/
|
|
154
|
+
latencySamples(): number {
|
|
155
|
+
return this.native.latencySamples();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Channel count of the active input layout. With `input.layout: "auto"`
|
|
160
|
+
* this follows the channel count of the most recent non-empty process call
|
|
161
|
+
* (2 before the first call).
|
|
162
|
+
*/
|
|
163
|
+
inputChannels(): number {
|
|
164
|
+
return this.native.inputChannels();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Channel count of the output target. */
|
|
168
|
+
outputChannels(): number {
|
|
169
|
+
return this.native.outputChannels();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Inactive stages, per-stage latency, clamps, the active input layout, and
|
|
174
|
+
* the layout-switch / truncated-drain counters, as a plain object.
|
|
175
|
+
*/
|
|
176
|
+
diagnostics(): PlaybackDiagnostics {
|
|
177
|
+
return JSON.parse(this.native.diagnosticsJson()) as PlaybackDiagnostics;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Non-finite input samples replaced with 0 since construction. */
|
|
181
|
+
nonFiniteDiscardCount(): number {
|
|
182
|
+
return this.native.nonFiniteDiscardCount();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Releases the native handle. Idempotent, as the Node facade is. */
|
|
186
|
+
delete(): void {
|
|
187
|
+
if (this.released) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.released = true;
|
|
191
|
+
this.native.delete();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
195
|
+
destroy(): void {
|
|
196
|
+
this.delete();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Integrated-loudness meter for multichannel program material (BS.1770
|
|
202
|
+
* channel weights by channel count: 1, 2, 6 or 8), for measuring
|
|
203
|
+
* `loudness.program_lufs` ahead of a {@link PlaybackRenderer}.
|
|
204
|
+
*/
|
|
205
|
+
export class PlaybackLoudnessMeter {
|
|
206
|
+
private native: WasmPlaybackLoudnessMeter;
|
|
207
|
+
private released = false;
|
|
208
|
+
|
|
209
|
+
constructor(channels: number, sampleRate: number) {
|
|
210
|
+
this.native = getSonareModule().createPlaybackLoudnessMeter(channels, sampleRate);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Feeds interleaved frames of any length. */
|
|
214
|
+
pushInterleaved(samples: Float32Array): void {
|
|
215
|
+
this.native.pushInterleaved(samples);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Integrated loudness of everything pushed so far, in LUFS. */
|
|
219
|
+
integratedLufs(): number {
|
|
220
|
+
return this.native.integratedLufs();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Releases the native handle. Idempotent, as the Node facade is. */
|
|
224
|
+
delete(): void {
|
|
225
|
+
if (this.released) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.released = true;
|
|
229
|
+
this.native.delete();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
233
|
+
destroy(): void {
|
|
234
|
+
this.delete();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Offline one-shot playback render: builds a renderer internally, feeds the
|
|
240
|
+
* whole interleaved signal through it, and removes the renderer's own latency
|
|
241
|
+
* so the output aligns with the input frame for frame.
|
|
242
|
+
* An empty or non-finite `samples` is refused with an `InvalidParameter` error.
|
|
243
|
+
*/
|
|
244
|
+
export function renderPlayback(request: RenderPlaybackRequest): RenderPlaybackResult {
|
|
245
|
+
return getSonareModule().renderPlayback(
|
|
246
|
+
request.samples,
|
|
247
|
+
request.channels,
|
|
248
|
+
request.sampleRate,
|
|
249
|
+
configJsonText(request.config),
|
|
250
|
+
nativeHrtf(request.hrtf),
|
|
251
|
+
);
|
|
252
|
+
}
|
package/src/polyphony.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { ErrorCode, SonareError } from './errors';
|
|
2
|
+
import { getSonareModule } from './module_state';
|
|
3
|
+
import type {
|
|
4
|
+
NoteEditInput,
|
|
5
|
+
NoteObject,
|
|
6
|
+
PolyphonicAnalysisOptions,
|
|
7
|
+
PolyphonicRenderOptions,
|
|
8
|
+
} from './public_types';
|
|
9
|
+
import type { WasmPolyphonicAnalysis } from './sonare.js';
|
|
10
|
+
import type { ValidateOptions } from './validation';
|
|
11
|
+
import { assertSampleRate, assertSamples } from './validation';
|
|
12
|
+
|
|
13
|
+
/** Canonical request form for {@link analyzePolyphonic}. */
|
|
14
|
+
export interface AnalyzePolyphonicRequest extends PolyphonicAnalysisOptions, ValidateOptions {
|
|
15
|
+
samples: Float32Array;
|
|
16
|
+
/**
|
|
17
|
+
* Sample rate in Hz. Required: every duration in the chain — the ridge
|
|
18
|
+
* minimum, the note minimum, the per-frame windows — is converted to samples
|
|
19
|
+
* with this rate, so a wrong value silently analyses differently.
|
|
20
|
+
*/
|
|
21
|
+
sampleRate: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A polyphonic analysis, held as a handle so one note of a chord can be edited
|
|
26
|
+
* and the result re-rendered without analysing the audio again.
|
|
27
|
+
*
|
|
28
|
+
* Created by {@link analyzePolyphonic}. **Release it with {@link destroy} as soon
|
|
29
|
+
* as you are done with it**: the handle owns the source's complex spectrogram plus
|
|
30
|
+
* the claimed bins of every note, which is the input over again plus the claims.
|
|
31
|
+
* That is the price of re-rendering an edit for free, and it is why this is a
|
|
32
|
+
* handle and not a result object — the measurement never crosses into JS.
|
|
33
|
+
*
|
|
34
|
+
* What crosses is what a host acts on: the notes ({@link notes}), each note's
|
|
35
|
+
* pending edit ({@link setNoteEdit}), the per-frame voice count
|
|
36
|
+
* ({@link polyphony}), and per note a pitch, a level and a salience curve. The
|
|
37
|
+
* spectrogram, the masks and the per-bin weights do not, and no method reports a
|
|
38
|
+
* per-bin figure.
|
|
39
|
+
*
|
|
40
|
+
* Using the analysis after it has been released throws `InvalidState` rather than
|
|
41
|
+
* reaching a freed native object.
|
|
42
|
+
*/
|
|
43
|
+
export class PolyphonicAnalysis {
|
|
44
|
+
private native: WasmPolyphonicAnalysis | null;
|
|
45
|
+
|
|
46
|
+
/** Analyses the request's audio. {@link analyzePolyphonic} is the same call. */
|
|
47
|
+
constructor(request: AnalyzePolyphonicRequest) {
|
|
48
|
+
assertSamples('analyzePolyphonic', request.samples, request.validate !== false);
|
|
49
|
+
assertSampleRate('analyzePolyphonic', request.sampleRate);
|
|
50
|
+
const module = getSonareModule();
|
|
51
|
+
this.native = module.createPolyphonicAnalysis(
|
|
52
|
+
request.samples,
|
|
53
|
+
request.sampleRate,
|
|
54
|
+
request as unknown as Record<string, unknown>,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private handle(): WasmPolyphonicAnalysis {
|
|
59
|
+
if (this.native === null) {
|
|
60
|
+
throw new SonareError(
|
|
61
|
+
ErrorCode.InvalidState,
|
|
62
|
+
'InvalidState',
|
|
63
|
+
'PolyphonicAnalysis has been released',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return this.native;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Number of notes, which is also the number of claim sets. */
|
|
70
|
+
get noteCount(): number {
|
|
71
|
+
return this.handle().noteCount;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Number of STFT frames the analysis ran over. */
|
|
75
|
+
get frameCount(): number {
|
|
76
|
+
return this.handle().frameCount;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Every note, in the order their claim sets are held in — the same
|
|
81
|
+
* {@link NoteObject} shape `extractNotes` returns, so a host that edits through
|
|
82
|
+
* both doors sees one note.
|
|
83
|
+
*
|
|
84
|
+
* Each note carries its sample span, its frame span (in the analysis's own
|
|
85
|
+
* framing), its median pitch, its steadiness, its per-frame `amplitude` and its
|
|
86
|
+
* pending edit. The curves a note does not carry inline have their own accessors:
|
|
87
|
+
* {@link noteF0}, {@link noteSalience}, and {@link noteEnvelope} for the points
|
|
88
|
+
* last set through {@link setNoteEdit}. `amplitude` is {@link noteAmplitude}'s
|
|
89
|
+
* curve, read once per note.
|
|
90
|
+
*/
|
|
91
|
+
notes(): NoteObject[] {
|
|
92
|
+
return this.handle().notes();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Replaces one note's pending edit.
|
|
97
|
+
*
|
|
98
|
+
* The only thing a host writes. Everything else on a note is a measurement, and
|
|
99
|
+
* the order is the pairing with the claim sets, so neither is settable.
|
|
100
|
+
*
|
|
101
|
+
* An omitted field is the identity, so `{}` restores the identity edit. The
|
|
102
|
+
* envelope is `edit.amplitudeEnvelope`, which the handle copies, and its points
|
|
103
|
+
* are per-frame linear gains over the note's span on top of `gainDb` — stretched
|
|
104
|
+
* over whatever length the note renders at, so one entry is a constant gain and
|
|
105
|
+
* the count need not match the note's frame count. Every value must be finite
|
|
106
|
+
* and non-negative, which {@link render} is where it is checked, so one refusal
|
|
107
|
+
* names one place.
|
|
108
|
+
*
|
|
109
|
+
* @param note - Index below {@link noteCount}
|
|
110
|
+
* @throws {SonareError} `InvalidParameter` when `note` is out of range
|
|
111
|
+
*/
|
|
112
|
+
setNoteEdit(note: number, edit: NoteEditInput): void {
|
|
113
|
+
this.handle().setNoteEdit(note, edit);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Voices estimated per frame, before tracking dropped anything — one entry per
|
|
118
|
+
* frame from frame 0.
|
|
119
|
+
*
|
|
120
|
+
* What the estimation saw rather than what survived: a frame reported as three
|
|
121
|
+
* voices with two notes spanning it is the difference between the two stages,
|
|
122
|
+
* which is the figure a host deciding what to edit wants.
|
|
123
|
+
*/
|
|
124
|
+
polyphony(): Int32Array {
|
|
125
|
+
return this.handle().polyphony();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* One note's F0 in Hz, per frame over its own span.
|
|
130
|
+
*
|
|
131
|
+
* `frameEnd - frameStart` entries, so the value at index `i` belongs to frame
|
|
132
|
+
* `frameStart + i`. This is the curve the monophonic door makes a caller pass
|
|
133
|
+
* back in; here the handle already holds it, so a curve edit needs nothing from
|
|
134
|
+
* the caller.
|
|
135
|
+
*
|
|
136
|
+
* @throws {SonareError} `InvalidParameter` when `note` is out of range
|
|
137
|
+
*/
|
|
138
|
+
noteF0(note: number): Float32Array {
|
|
139
|
+
return this.handle().noteF0(note);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** One note's linear RMS, per frame over its own span. Indexed as {@link noteF0}. */
|
|
143
|
+
noteAmplitude(note: number): Float32Array {
|
|
144
|
+
return this.handle().noteAmplitude(note);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* One note's salience, per frame over its own span. Indexed as {@link noteF0},
|
|
149
|
+
* and the one curve here that is not the note's own: it is the tracked ridge's,
|
|
150
|
+
* so a frame of the note the ridge does not reach reads 0.
|
|
151
|
+
*
|
|
152
|
+
* Salience is what the estimation scored the candidate at, so it says how well
|
|
153
|
+
* the material supported this note rather than how loud the note is —
|
|
154
|
+
* {@link noteAmplitude} is the loud.
|
|
155
|
+
*/
|
|
156
|
+
noteSalience(note: number): Float32Array {
|
|
157
|
+
return this.handle().noteSalience(note);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The stretch fitted for each note, one entry per note in {@link notes}' order.
|
|
162
|
+
*
|
|
163
|
+
* Empty when `estimateInharmonicity` was not set, so an empty array means the
|
|
164
|
+
* fit was never asked for. A non-negative entry is a fitted stretch; **exactly
|
|
165
|
+
* `-1` is the refusal**, and a refused note's claims were placed at the
|
|
166
|
+
* `inharmonicity` the request declared instead.
|
|
167
|
+
*
|
|
168
|
+
* **`0` is a fitted result and means the harmonic series**, which is why the
|
|
169
|
+
* refusal is reported at all: the declared stretch also defaults to 0, so the
|
|
170
|
+
* effective value alone cannot separate a fit that reached the material from one
|
|
171
|
+
* that did not. The fit refuses a chord at the default framing, so the
|
|
172
|
+
* distinction is the usual case rather than an edge one.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const analysis = analyzePolyphonic({ samples, sampleRate, estimateInharmonicity: true });
|
|
177
|
+
* const fitted = analysis.noteInharmonicity();
|
|
178
|
+
* const reached = [...fitted].filter((stretch) => stretch >= 0).length;
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
noteInharmonicity(): Float32Array {
|
|
182
|
+
return this.handle().noteInharmonicity();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One note's amplitude envelope points, as last set — the same array
|
|
187
|
+
* `notes()[note].edit.amplitudeEnvelope` carries.
|
|
188
|
+
*
|
|
189
|
+
* Indexed from 0 rather than over the note's span: an envelope is a set of gain
|
|
190
|
+
* points stretched over whatever length the note renders at, not a per-frame
|
|
191
|
+
* signal. The only one of the four curves that is not a measurement, and empty on
|
|
192
|
+
* a note carrying no envelope.
|
|
193
|
+
*/
|
|
194
|
+
noteEnvelope(note: number): Float32Array {
|
|
195
|
+
return this.handle().noteEnvelope(note);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Renders the analysis back to audio with whatever edits its notes carry, at the
|
|
200
|
+
* source's length.
|
|
201
|
+
*
|
|
202
|
+
* Each note's claimed share is inverted, edited, and added to the residual — the
|
|
203
|
+
* part of the input no note claimed. With every edit identity the result is the
|
|
204
|
+
* analysis's own round trip, not the source bit for bit, the STFT round trip's
|
|
205
|
+
* error being neither added to nor removed here.
|
|
206
|
+
*
|
|
207
|
+
* The render is additive per note with no cross-note term, so an unedited note's
|
|
208
|
+
* contribution is identical between two renders. That is also the limit: a host
|
|
209
|
+
* cannot tell from two renders whether a claim set divided the energy correctly.
|
|
210
|
+
*
|
|
211
|
+
* @throws {SonareError} `InvalidParameter` on an option or an edit field the
|
|
212
|
+
* renderer rejects — a non-positive stretch ratio, a negative or non-finite
|
|
213
|
+
* envelope point, or a vibrato or drift edit on a note carrying no usable
|
|
214
|
+
* pitch curve
|
|
215
|
+
*/
|
|
216
|
+
render(options: PolyphonicRenderOptions = {}): Float32Array {
|
|
217
|
+
return this.handle().render(options as Record<string, unknown>);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Releases the underlying WASM object and everything it holds. Idempotent,
|
|
222
|
+
* as the Node facade is; any other method called after this one still
|
|
223
|
+
* throws `InvalidState` rather than reaching a freed native object.
|
|
224
|
+
*/
|
|
225
|
+
delete(): void {
|
|
226
|
+
if (this.native === null) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const native = this.native;
|
|
230
|
+
this.native = null;
|
|
231
|
+
native.delete();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */
|
|
235
|
+
destroy(): void {
|
|
236
|
+
this.delete();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Analyses audio into editable notes and returns a handle to the analysis.
|
|
242
|
+
*
|
|
243
|
+
* One pass: one STFT, the multi-F0 extraction over it, a claim set per tracked
|
|
244
|
+
* ridge, the apportionment of the bins two notes stand on, and the measured fields
|
|
245
|
+
* of each note. Every note comes back with the identity edit, so rendering the
|
|
246
|
+
* result unchanged reproduces the analysis's own round trip.
|
|
247
|
+
*
|
|
248
|
+
* An analysis finding no notes is not an error. Silence, or material the register
|
|
249
|
+
* of the framing cannot resolve, tracks no ridge; rendering that is the residual
|
|
250
|
+
* alone, which is the whole round trip.
|
|
251
|
+
*
|
|
252
|
+
* **This is for spans, not for whole songs.** The handle holds the source's
|
|
253
|
+
* complex spectrogram plus every note's claimed bins — roughly 1,440 MiB for five
|
|
254
|
+
* minutes of audio against a 2 GiB linear-memory cap — so analyse the passage you
|
|
255
|
+
* are editing and {@link PolyphonicAnalysis.destroy} it when done.
|
|
256
|
+
*
|
|
257
|
+
* @throws {RangeError} on empty samples, a non-finite sample, or a `sampleRate`
|
|
258
|
+
* outside `[8000, 384000]`
|
|
259
|
+
* @throws {SonareError} `InvalidParameter` on a config value the chain rejects,
|
|
260
|
+
* or on audio too short for two STFT frames at the configured `nFft`/`hopLength`
|
|
261
|
+
* (roughly one `hopLength`, ~512 samples at the default)
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* const analysis = analyzePolyphonic({ samples, sampleRate, maxPolyphony: 3 });
|
|
266
|
+
* try {
|
|
267
|
+
* // Transpose the lowest note of the chord up a semitone.
|
|
268
|
+
* const notes = analysis.notes();
|
|
269
|
+
* const lowest = notes.reduce((a, b) => (a.medianHz <= b.medianHz ? a : b));
|
|
270
|
+
* analysis.setNoteEdit(notes.indexOf(lowest), { pitchShiftSemitones: 1 });
|
|
271
|
+
* const edited = analysis.render();
|
|
272
|
+
* } finally {
|
|
273
|
+
* analysis.destroy();
|
|
274
|
+
* }
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
export function analyzePolyphonic(request: AnalyzePolyphonicRequest): PolyphonicAnalysis {
|
|
278
|
+
return new PolyphonicAnalysis(request);
|
|
279
|
+
}
|
package/src/project.ts
CHANGED
|
@@ -1,15 +1,66 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
Articulation,
|
|
3
|
+
BuiltinSynthBinding,
|
|
4
|
+
BuiltinSynthConfig,
|
|
5
|
+
BuiltinSynthWaveform,
|
|
6
|
+
ControllerAxis,
|
|
7
|
+
ControllerBinding,
|
|
8
|
+
ControllerInput,
|
|
9
|
+
MpeDimension,
|
|
10
|
+
NoteTracking,
|
|
11
|
+
SampleDesc,
|
|
12
|
+
SampleDescLoopMode,
|
|
13
|
+
SampleKeyTrack,
|
|
14
|
+
SampleLoopMode,
|
|
15
|
+
SampleZoneDesc,
|
|
16
|
+
Sf2InstrumentConfig,
|
|
17
|
+
Sf2ProgramStatus,
|
|
18
|
+
SourceBackend,
|
|
19
|
+
SynthBodyType,
|
|
20
|
+
SynthEngineMode,
|
|
21
|
+
SynthEnumTables,
|
|
22
|
+
SynthFilterModel,
|
|
23
|
+
SynthFilterOutput,
|
|
24
|
+
SynthModDestination,
|
|
25
|
+
SynthModRouting,
|
|
26
|
+
SynthModSource,
|
|
27
|
+
SynthOscWaveform,
|
|
28
|
+
SynthPatch,
|
|
29
|
+
SynthRetrigger,
|
|
30
|
+
} from './instrument_types';
|
|
31
|
+
export {
|
|
32
|
+
ARTICULATIONS,
|
|
33
|
+
BUILTIN_SYNTH_WAVEFORMS,
|
|
34
|
+
CONTROLLER_AXES,
|
|
35
|
+
CONTROLLER_INPUTS,
|
|
36
|
+
MPE_DIMENSIONS,
|
|
37
|
+
NOTE_TRACKINGS,
|
|
38
|
+
SAMPLE_KEY_TRACKS,
|
|
39
|
+
SAMPLE_LOOP_MODES,
|
|
40
|
+
SYNTH_BODY_TYPES,
|
|
41
|
+
SYNTH_ENGINE_MODES,
|
|
42
|
+
SYNTH_FILTER_MODELS,
|
|
43
|
+
SYNTH_FILTER_OUTPUTS,
|
|
44
|
+
SYNTH_MOD_DESTINATIONS,
|
|
45
|
+
SYNTH_MOD_SOURCES,
|
|
46
|
+
SYNTH_OSC_WAVEFORMS,
|
|
47
|
+
SYNTH_RETRIGGERS,
|
|
48
|
+
} from './instrument_types';
|
|
1
49
|
export { Project } from './project_class';
|
|
2
50
|
export {
|
|
51
|
+
controllerProfileNames,
|
|
3
52
|
projectAbiVersion,
|
|
4
53
|
synthEnumTables,
|
|
54
|
+
synthGsDrumKitIsVoicedApart,
|
|
55
|
+
synthGsDrumKitName,
|
|
56
|
+
synthGsVariationIsVoicedApart,
|
|
5
57
|
synthPatchRoundTripForTest,
|
|
6
58
|
synthPresetNames,
|
|
7
59
|
synthPresetPatch,
|
|
8
60
|
} from './project_synth';
|
|
9
61
|
export type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
BuiltinSynthWaveform,
|
|
62
|
+
AlignTakeToReferenceRequest,
|
|
63
|
+
AlignTakeToReferenceResult,
|
|
13
64
|
ExternalSeparatedStem,
|
|
14
65
|
ExternalSeparatedStemImportRequest,
|
|
15
66
|
ExternalSeparatedStemImportResult,
|
|
@@ -47,41 +98,27 @@ export type {
|
|
|
47
98
|
ProjectMidiRouteResult,
|
|
48
99
|
ProjectNotePairValidation,
|
|
49
100
|
ProjectSource,
|
|
101
|
+
ProjectTempoCandidate,
|
|
102
|
+
ProjectTempoOptions,
|
|
50
103
|
ProjectTempoSegment,
|
|
51
104
|
ProjectTimeSignatureSegment,
|
|
52
105
|
ProjectTrack,
|
|
53
106
|
ProjectTrackDesc,
|
|
54
107
|
ProjectTrackKind,
|
|
108
|
+
ProjectTranscribeRequest,
|
|
55
109
|
ProjectWarpAnchor,
|
|
56
110
|
ProjectWarpMapDesc,
|
|
57
111
|
ProjectWarpMode,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
SynthBodyType,
|
|
62
|
-
SynthEngineMode,
|
|
63
|
-
SynthEnumTables,
|
|
64
|
-
SynthFilterModel,
|
|
65
|
-
SynthFilterOutput,
|
|
66
|
-
SynthModDestination,
|
|
67
|
-
SynthModRouting,
|
|
68
|
-
SynthModSource,
|
|
69
|
-
SynthOscWaveform,
|
|
70
|
-
SynthPatch,
|
|
112
|
+
TakeAlignment,
|
|
113
|
+
TranscribeOptions,
|
|
114
|
+
TranscribeResult,
|
|
71
115
|
} from './project_types';
|
|
72
116
|
export {
|
|
73
117
|
AutomationTargetKind,
|
|
74
|
-
BUILTIN_SYNTH_WAVEFORMS,
|
|
75
118
|
EXPECTED_PROJECT_ABI_VERSION,
|
|
76
119
|
MarkerKind,
|
|
77
120
|
PROJECT_AUTOMATION_TARGET_OPAQUE,
|
|
78
121
|
PROJECT_AUTOMATION_TARGET_TRACK_FADER_DB,
|
|
79
122
|
PROJECT_AUTOMATION_TARGET_TRACK_PAN,
|
|
80
|
-
SYNTH_BODY_TYPES,
|
|
81
|
-
SYNTH_ENGINE_MODES,
|
|
82
|
-
SYNTH_FILTER_MODELS,
|
|
83
|
-
SYNTH_FILTER_OUTPUTS,
|
|
84
|
-
SYNTH_MOD_DESTINATIONS,
|
|
85
|
-
SYNTH_MOD_SOURCES,
|
|
86
|
-
SYNTH_OSC_WAVEFORMS,
|
|
87
123
|
} from './project_types';
|
|
124
|
+
export { SampleBank } from './sample_bank';
|