@libraz/libsonare 1.5.2 → 1.5.3
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/README.md +18 -3
- package/dist/index.d.ts +1147 -181
- package/dist/index.js +4826 -3941
- package/dist/index.js.map +1 -1
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +29 -1
- package/dist/worklet.js +1067 -973
- package/dist/worklet.js.map +1 -1
- package/package.json +2 -1
- package/src/_chain_config.ts +46 -0
- package/src/audio.ts +24 -4
- package/src/effects_mastering.ts +25 -1
- package/src/effects_transform.ts +283 -43
- package/src/effects_voice_change.ts +46 -53
- package/src/feature_core.ts +316 -12
- package/src/feature_music.ts +357 -0
- package/src/feature_pitch.ts +61 -0
- package/src/feature_resample.ts +17 -2
- package/src/feature_spectral.ts +349 -2
- package/src/feature_spectrogram.ts +336 -0
- package/src/index.ts +130 -1
- package/src/mastering_chain.ts +305 -50
- package/src/mastering_core.ts +232 -16
- package/src/mastering_dynamics.ts +66 -10
- package/src/mastering_repair.ts +119 -7
- package/src/metering.ts +366 -81
- package/src/mixer.ts +9 -2
- package/src/mixing_oneshot.ts +25 -5
- package/src/module_state.ts +1 -1
- package/src/project_class.ts +17 -6
- package/src/project_internal.ts +10 -2
- package/src/project_types.ts +25 -2
- package/src/public_types_mastering.ts +114 -4
- package/src/public_types_spectral.ts +7 -0
- package/src/quick_analysis.ts +325 -117
- package/src/realtime_engine.ts +8 -0
- package/src/sonare.js.d.ts +31 -0
- package/src/stream_analyzer.ts +20 -1
- package/src/stream_types.ts +12 -1
- package/src/validation.ts +17 -2
- package/src/worklet/engine-clips.ts +67 -2
- package/src/worklet/engine-processor.ts +33 -5
- package/src/worklet/engine.ts +7 -3
- package/src/worklet/guards.ts +3 -0
- package/src/worklet/messages.ts +27 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libraz/libsonare",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "yarn@4.15.0",
|
|
6
6
|
"description": "Audio analysis library for music information retrieval",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"format": "biome format --write src/ tests/",
|
|
36
36
|
"test": "vitest run",
|
|
37
37
|
"test:browser": "node scripts/audio-worklet-smoke.mjs",
|
|
38
|
+
"test:browser:audit": "node scripts/audit-boundary-smoke.mjs",
|
|
38
39
|
"test:watch": "vitest",
|
|
39
40
|
"prepublishOnly": "yarn clean && cp ../../LICENSE LICENSE && yarn build && yarn lint && yarn test"
|
|
40
41
|
},
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { MasteringChainConfig } from './public_types';
|
|
2
|
+
|
|
3
|
+
type ChainSection = { [key: string]: number | boolean | ChainSection | undefined };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flattens a nested {@link MasteringChainConfig} into the dot-notation
|
|
7
|
+
* `{ "module.processor.param": value }` map the WASM core consumes. Internal
|
|
8
|
+
* helper shared by the mastering-chain / master-audio entry points; mirrors the
|
|
9
|
+
* Node facade so a nested config produces identical overrides on both surfaces.
|
|
10
|
+
*/
|
|
11
|
+
export function flattenChainConfig(config: MasteringChainConfig): Record<string, number | boolean> {
|
|
12
|
+
const out: Record<string, number | boolean> = {};
|
|
13
|
+
const walk = (node: ChainSection, prefix: string): void => {
|
|
14
|
+
for (const [key, value] of Object.entries(node)) {
|
|
15
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
16
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
17
|
+
out[path] = value;
|
|
18
|
+
} else if (value !== null && typeof value === 'object') {
|
|
19
|
+
walk(value, path);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
walk(config as ChainSection, '');
|
|
24
|
+
|
|
25
|
+
// Compatibility aliases for the original WASM-only shorthand. Normalize at
|
|
26
|
+
// this boundary so every native entry point receives the core parser's one
|
|
27
|
+
// canonical vocabulary; public types can migrate to the nested spelling
|
|
28
|
+
// without preserving a second C++ parser indefinitely.
|
|
29
|
+
const aliases: Record<string, string> = {
|
|
30
|
+
'repair.denoise': 'repair.denoise.enabled',
|
|
31
|
+
'repair.nFft': 'repair.denoise.nFft',
|
|
32
|
+
'repair.hopLength': 'repair.denoise.hopLength',
|
|
33
|
+
'repair.ddAlpha': 'repair.denoise.ddAlpha',
|
|
34
|
+
'repair.gainFloor': 'repair.denoise.gainFloor',
|
|
35
|
+
'eq.tiltDb': 'eq.tilt.tiltDb',
|
|
36
|
+
'eq.pivotHz': 'eq.tilt.pivotHz',
|
|
37
|
+
};
|
|
38
|
+
for (const [legacy, canonical] of Object.entries(aliases)) {
|
|
39
|
+
const value = out[legacy];
|
|
40
|
+
if (value !== undefined) {
|
|
41
|
+
out[canonical] = value;
|
|
42
|
+
delete out[legacy];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
package/src/audio.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
masteringChain,
|
|
8
8
|
masteringProcess,
|
|
9
9
|
normalize,
|
|
10
|
+
noteMove,
|
|
10
11
|
noteStretch,
|
|
11
12
|
percussive,
|
|
12
13
|
pitchCorrectToMidi,
|
|
@@ -332,6 +333,10 @@ export class Audio {
|
|
|
332
333
|
return noteStretch(this._samples, this._sampleRate, options);
|
|
333
334
|
}
|
|
334
335
|
|
|
336
|
+
noteMove(options: import('./public_types.js').NoteMoveOptions = {}): Float32Array {
|
|
337
|
+
return noteMove(this._samples, this._sampleRate, options);
|
|
338
|
+
}
|
|
339
|
+
|
|
335
340
|
voiceChange(options: VoiceChangeOptions = {}): Float32Array {
|
|
336
341
|
return voiceChange(this._samples, this._sampleRate, options);
|
|
337
342
|
}
|
|
@@ -344,15 +349,30 @@ export class Audio {
|
|
|
344
349
|
return mastering(this._samples, this._sampleRate, options);
|
|
345
350
|
}
|
|
346
351
|
|
|
347
|
-
masteringChain(
|
|
348
|
-
|
|
352
|
+
masteringChain(
|
|
353
|
+
config: MasteringChainConfig = {},
|
|
354
|
+
onProgress?: ProgressCallback,
|
|
355
|
+
): MasteringChainResult {
|
|
356
|
+
return masteringChain({
|
|
357
|
+
samples: this._samples,
|
|
358
|
+
sampleRate: this._sampleRate,
|
|
359
|
+
config,
|
|
360
|
+
onProgress,
|
|
361
|
+
});
|
|
349
362
|
}
|
|
350
363
|
|
|
351
364
|
masterAudio(
|
|
352
365
|
presetName: MasteringPreset = 'pop',
|
|
353
|
-
overrides:
|
|
366
|
+
overrides: MasteringChainConfig | null = null,
|
|
367
|
+
onProgress?: ProgressCallback,
|
|
354
368
|
): MasteringChainResult {
|
|
355
|
-
return masterAudio(
|
|
369
|
+
return masterAudio({
|
|
370
|
+
samples: this._samples,
|
|
371
|
+
sampleRate: this._sampleRate,
|
|
372
|
+
preset: presetName,
|
|
373
|
+
overrides: overrides ?? {},
|
|
374
|
+
onProgress,
|
|
375
|
+
});
|
|
356
376
|
}
|
|
357
377
|
|
|
358
378
|
masteringProcess(
|
package/src/effects_mastering.ts
CHANGED
|
@@ -2,6 +2,7 @@ export {
|
|
|
2
2
|
harmonic,
|
|
3
3
|
hpss,
|
|
4
4
|
normalize,
|
|
5
|
+
noteMove,
|
|
5
6
|
noteStretch,
|
|
6
7
|
percussive,
|
|
7
8
|
pitchCorrectTimevarying,
|
|
@@ -11,7 +12,12 @@ export {
|
|
|
11
12
|
spectralEdit,
|
|
12
13
|
timeStretch,
|
|
13
14
|
} from './effects_transform';
|
|
14
|
-
export type {
|
|
15
|
+
export type {
|
|
16
|
+
VoiceChangeOptions,
|
|
17
|
+
VoiceChangeRealtimeOptions,
|
|
18
|
+
VoiceChangeRealtimeRequest,
|
|
19
|
+
VoiceChangeRequest,
|
|
20
|
+
} from './effects_voice_change';
|
|
15
21
|
export { voiceChange, voiceChangeRealtime } from './effects_voice_change';
|
|
16
22
|
export {
|
|
17
23
|
masterAudio,
|
|
@@ -27,7 +33,14 @@ export {
|
|
|
27
33
|
export type {
|
|
28
34
|
MasteringChannelPolicy,
|
|
29
35
|
MasteringInsertParamInfo,
|
|
36
|
+
MasteringPairAnalyzeRequest,
|
|
37
|
+
MasteringPairProcessRequest,
|
|
30
38
|
MasteringProcessorCatalogEntry,
|
|
39
|
+
MasteringProcessRequest,
|
|
40
|
+
MasteringProcessStereoRequest,
|
|
41
|
+
MasteringSamplesParamsRequest,
|
|
42
|
+
MasteringStereoAnalyzeRequest,
|
|
43
|
+
MasteringStreamingPreviewRequest,
|
|
31
44
|
} from './mastering_core';
|
|
32
45
|
export {
|
|
33
46
|
mastering,
|
|
@@ -53,6 +66,9 @@ export type {
|
|
|
53
66
|
CompressorOptions,
|
|
54
67
|
DynamicsResult,
|
|
55
68
|
GateOptions,
|
|
69
|
+
MasteringDynamicsCompressorRequest,
|
|
70
|
+
MasteringDynamicsGateRequest,
|
|
71
|
+
MasteringDynamicsTransientShaperRequest,
|
|
56
72
|
TransientShaperOptions,
|
|
57
73
|
} from './mastering_dynamics';
|
|
58
74
|
export {
|
|
@@ -70,6 +86,13 @@ export type {
|
|
|
70
86
|
DenoiseClassicalNoiseEstimator,
|
|
71
87
|
DenoiseClassicalOptions,
|
|
72
88
|
DereverbClassicalOptions,
|
|
89
|
+
MasteringRepairDeclickRequest,
|
|
90
|
+
MasteringRepairDeclipRequest,
|
|
91
|
+
MasteringRepairDecrackleRequest,
|
|
92
|
+
MasteringRepairDehumRequest,
|
|
93
|
+
MasteringRepairDenoiseClassicalRequest,
|
|
94
|
+
MasteringRepairDereverbClassicalRequest,
|
|
95
|
+
MasteringRepairTrimSilenceRequest,
|
|
73
96
|
TrimSilenceMode,
|
|
74
97
|
TrimSilenceOptions,
|
|
75
98
|
} from './mastering_repair';
|
|
@@ -82,4 +105,5 @@ export {
|
|
|
82
105
|
masteringRepairDereverbClassical,
|
|
83
106
|
masteringRepairTrimSilence,
|
|
84
107
|
} from './mastering_repair';
|
|
108
|
+
export type { MixStereoRequest } from './mixing_oneshot';
|
|
85
109
|
export { mixingScenePresetJson, mixingScenePresetNames, mixStereo } from './mixing_oneshot';
|
package/src/effects_transform.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSonareModule } from './module_state';
|
|
2
2
|
import type {
|
|
3
3
|
HpssResult,
|
|
4
|
+
NoteMoveOptions,
|
|
4
5
|
NoteStretchOptions,
|
|
5
6
|
PitchCorrectOptions,
|
|
6
7
|
SpectralEditOptions,
|
|
@@ -13,6 +14,81 @@ function requireModule() {
|
|
|
13
14
|
return getSonareModule();
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/** Canonical request form for HPSS. */
|
|
18
|
+
export interface HpssRequest {
|
|
19
|
+
samples: Float32Array;
|
|
20
|
+
sampleRate?: number;
|
|
21
|
+
kernelHarmonic?: number;
|
|
22
|
+
kernelPercussive?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface HarmonicRequest extends ValidateOptions {
|
|
26
|
+
samples: Float32Array;
|
|
27
|
+
sampleRate?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PercussiveRequest extends ValidateOptions {
|
|
31
|
+
samples: Float32Array;
|
|
32
|
+
sampleRate?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TimeStretchRequest extends ValidateOptions {
|
|
36
|
+
samples: Float32Array;
|
|
37
|
+
sampleRate: number;
|
|
38
|
+
rate: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PitchShiftRequest extends ValidateOptions {
|
|
42
|
+
samples: Float32Array;
|
|
43
|
+
sampleRate: number;
|
|
44
|
+
semitones: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface PitchCorrectToMidiRequest extends ValidateOptions {
|
|
48
|
+
samples: Float32Array;
|
|
49
|
+
sampleRate?: number;
|
|
50
|
+
currentMidi?: number;
|
|
51
|
+
targetMidi?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PitchCorrectToMidiTimevaryingRequest extends ValidateOptions {
|
|
55
|
+
samples: Float32Array;
|
|
56
|
+
f0Hz: Float32Array;
|
|
57
|
+
targetMidi: number;
|
|
58
|
+
sampleRate?: number;
|
|
59
|
+
hopLength?: number;
|
|
60
|
+
voiced?: Int32Array;
|
|
61
|
+
voicedProb?: Float32Array;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface PitchCorrectTimevaryingRequest extends PitchCorrectOptions {
|
|
65
|
+
samples: Float32Array;
|
|
66
|
+
f0Hz: Float32Array;
|
|
67
|
+
sampleRate?: number;
|
|
68
|
+
hopLength?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface NoteStretchRequest extends NoteStretchOptions, ValidateOptions {
|
|
72
|
+
samples: Float32Array;
|
|
73
|
+
sampleRate?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface NoteMoveRequest extends NoteMoveOptions, ValidateOptions {
|
|
76
|
+
samples: Float32Array;
|
|
77
|
+
sampleRate?: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface NormalizeRequest extends ValidateOptions {
|
|
81
|
+
samples: Float32Array;
|
|
82
|
+
sampleRate: number;
|
|
83
|
+
targetDb?: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface SpectralEditRequest extends SpectralEditOptions, ValidateOptions {
|
|
87
|
+
samples: Float32Array;
|
|
88
|
+
sampleRate: number;
|
|
89
|
+
ops?: SpectralRegionOp[];
|
|
90
|
+
}
|
|
91
|
+
|
|
16
92
|
// ============================================================================
|
|
17
93
|
// Effects
|
|
18
94
|
// ============================================================================
|
|
@@ -26,13 +102,29 @@ function requireModule() {
|
|
|
26
102
|
* @param kernelPercussive - Vertical median filter size for percussive (default: 31)
|
|
27
103
|
* @returns Separated harmonic and percussive components
|
|
28
104
|
*/
|
|
105
|
+
export function hpss(request: HpssRequest): HpssResult;
|
|
29
106
|
export function hpss(
|
|
30
107
|
samples: Float32Array,
|
|
108
|
+
sampleRate?: number,
|
|
109
|
+
kernelHarmonic?: number,
|
|
110
|
+
kernelPercussive?: number,
|
|
111
|
+
): HpssResult;
|
|
112
|
+
export function hpss(
|
|
113
|
+
samples: Float32Array | HpssRequest,
|
|
31
114
|
sampleRate = 22050,
|
|
32
115
|
kernelHarmonic = 31,
|
|
33
116
|
kernelPercussive = 31,
|
|
34
117
|
): HpssResult {
|
|
35
|
-
|
|
118
|
+
const request =
|
|
119
|
+
samples instanceof Float32Array
|
|
120
|
+
? { samples, sampleRate, kernelHarmonic, kernelPercussive }
|
|
121
|
+
: samples;
|
|
122
|
+
return requireModule().hpss(
|
|
123
|
+
request.samples,
|
|
124
|
+
request.sampleRate ?? 22050,
|
|
125
|
+
request.kernelHarmonic ?? 31,
|
|
126
|
+
request.kernelPercussive ?? 31,
|
|
127
|
+
);
|
|
36
128
|
}
|
|
37
129
|
|
|
38
130
|
/**
|
|
@@ -42,13 +134,20 @@ export function hpss(
|
|
|
42
134
|
* @param sampleRate - Sample rate in Hz
|
|
43
135
|
* @returns Harmonic component
|
|
44
136
|
*/
|
|
137
|
+
export function harmonic(request: HarmonicRequest): Float32Array;
|
|
45
138
|
export function harmonic(
|
|
46
139
|
samples: Float32Array,
|
|
47
|
-
sampleRate
|
|
140
|
+
sampleRate?: number,
|
|
141
|
+
options?: ValidateOptions,
|
|
142
|
+
): Float32Array;
|
|
143
|
+
export function harmonic(
|
|
144
|
+
samples: Float32Array | HarmonicRequest,
|
|
145
|
+
sampleRate = 22050,
|
|
48
146
|
options: ValidateOptions = {},
|
|
49
147
|
): Float32Array {
|
|
50
|
-
|
|
51
|
-
|
|
148
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
149
|
+
assertSamples('harmonic', request.samples, request.validate !== false);
|
|
150
|
+
return requireModule().harmonic(request.samples, request.sampleRate ?? 22050);
|
|
52
151
|
}
|
|
53
152
|
|
|
54
153
|
/**
|
|
@@ -58,13 +157,20 @@ export function harmonic(
|
|
|
58
157
|
* @param sampleRate - Sample rate in Hz
|
|
59
158
|
* @returns Percussive component
|
|
60
159
|
*/
|
|
160
|
+
export function percussive(request: PercussiveRequest): Float32Array;
|
|
61
161
|
export function percussive(
|
|
62
162
|
samples: Float32Array,
|
|
63
|
-
sampleRate
|
|
163
|
+
sampleRate?: number,
|
|
164
|
+
options?: ValidateOptions,
|
|
165
|
+
): Float32Array;
|
|
166
|
+
export function percussive(
|
|
167
|
+
samples: Float32Array | PercussiveRequest,
|
|
168
|
+
sampleRate = 22050,
|
|
64
169
|
options: ValidateOptions = {},
|
|
65
170
|
): Float32Array {
|
|
66
|
-
|
|
67
|
-
|
|
171
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
172
|
+
assertSamples('percussive', request.samples, request.validate !== false);
|
|
173
|
+
return requireModule().percussive(request.samples, request.sampleRate ?? 22050);
|
|
68
174
|
}
|
|
69
175
|
|
|
70
176
|
/**
|
|
@@ -75,14 +181,25 @@ export function percussive(
|
|
|
75
181
|
* @param rate - Time stretch rate (0.5 = double duration, 2.0 = half duration)
|
|
76
182
|
* @returns Time-stretched audio
|
|
77
183
|
*/
|
|
184
|
+
export function timeStretch(request: TimeStretchRequest): Float32Array;
|
|
78
185
|
export function timeStretch(
|
|
79
186
|
samples: Float32Array,
|
|
80
187
|
sampleRate: number,
|
|
81
188
|
rate: number,
|
|
189
|
+
options?: ValidateOptions,
|
|
190
|
+
): Float32Array;
|
|
191
|
+
export function timeStretch(
|
|
192
|
+
samples: Float32Array | TimeStretchRequest,
|
|
193
|
+
sampleRate?: number,
|
|
194
|
+
rate?: number,
|
|
82
195
|
options: ValidateOptions = {},
|
|
83
196
|
): Float32Array {
|
|
84
|
-
|
|
85
|
-
|
|
197
|
+
const request: TimeStretchRequest =
|
|
198
|
+
samples instanceof Float32Array
|
|
199
|
+
? { samples, sampleRate: sampleRate as number, rate: rate as number, ...options }
|
|
200
|
+
: samples;
|
|
201
|
+
assertSamples('timeStretch', request.samples, request.validate !== false);
|
|
202
|
+
return requireModule().timeStretch(request.samples, request.sampleRate, request.rate);
|
|
86
203
|
}
|
|
87
204
|
|
|
88
205
|
/**
|
|
@@ -93,14 +210,25 @@ export function timeStretch(
|
|
|
93
210
|
* @param semitones - Pitch shift in semitones (+12 = one octave up, -12 = one octave down)
|
|
94
211
|
* @returns Pitch-shifted audio
|
|
95
212
|
*/
|
|
213
|
+
export function pitchShift(request: PitchShiftRequest): Float32Array;
|
|
96
214
|
export function pitchShift(
|
|
97
215
|
samples: Float32Array,
|
|
98
216
|
sampleRate: number,
|
|
99
217
|
semitones: number,
|
|
218
|
+
options?: ValidateOptions,
|
|
219
|
+
): Float32Array;
|
|
220
|
+
export function pitchShift(
|
|
221
|
+
samples: Float32Array | PitchShiftRequest,
|
|
222
|
+
sampleRate?: number,
|
|
223
|
+
semitones?: number,
|
|
100
224
|
options: ValidateOptions = {},
|
|
101
225
|
): Float32Array {
|
|
102
|
-
|
|
103
|
-
|
|
226
|
+
const request: PitchShiftRequest =
|
|
227
|
+
samples instanceof Float32Array
|
|
228
|
+
? { samples, sampleRate: sampleRate as number, semitones: semitones as number, ...options }
|
|
229
|
+
: samples;
|
|
230
|
+
assertSamples('pitchShift', request.samples, request.validate !== false);
|
|
231
|
+
return requireModule().pitchShift(request.samples, request.sampleRate, request.semitones);
|
|
104
232
|
}
|
|
105
233
|
|
|
106
234
|
/**
|
|
@@ -112,15 +240,32 @@ export function pitchShift(
|
|
|
112
240
|
* @param targetMidi - Desired MIDI note number
|
|
113
241
|
* @returns Pitch-corrected audio
|
|
114
242
|
*/
|
|
243
|
+
export function pitchCorrectToMidi(request: PitchCorrectToMidiRequest): Float32Array;
|
|
115
244
|
export function pitchCorrectToMidi(
|
|
116
245
|
samples: Float32Array,
|
|
246
|
+
sampleRate?: number,
|
|
247
|
+
currentMidi?: number,
|
|
248
|
+
targetMidi?: number,
|
|
249
|
+
options?: ValidateOptions,
|
|
250
|
+
): Float32Array;
|
|
251
|
+
export function pitchCorrectToMidi(
|
|
252
|
+
samples: Float32Array | PitchCorrectToMidiRequest,
|
|
117
253
|
sampleRate = 22050,
|
|
118
254
|
currentMidi = 69.0,
|
|
119
255
|
targetMidi = 69.0,
|
|
120
256
|
options: ValidateOptions = {},
|
|
121
257
|
): Float32Array {
|
|
122
|
-
|
|
123
|
-
|
|
258
|
+
const request =
|
|
259
|
+
samples instanceof Float32Array
|
|
260
|
+
? { samples, sampleRate, currentMidi, targetMidi, ...options }
|
|
261
|
+
: samples;
|
|
262
|
+
assertSamples('pitchCorrectToMidi', request.samples, request.validate !== false);
|
|
263
|
+
return requireModule().pitchCorrectToMidi(
|
|
264
|
+
request.samples,
|
|
265
|
+
request.sampleRate ?? 22050,
|
|
266
|
+
request.currentMidi ?? 69.0,
|
|
267
|
+
request.targetMidi ?? 69.0,
|
|
268
|
+
);
|
|
124
269
|
}
|
|
125
270
|
|
|
126
271
|
/**
|
|
@@ -141,34 +286,60 @@ export function pitchCorrectToMidi(
|
|
|
141
286
|
* @param voicedProb - Optional per-frame voicing probability in [0, 1]
|
|
142
287
|
* @returns Pitch-corrected audio
|
|
143
288
|
*/
|
|
289
|
+
export function pitchCorrectToMidiTimevarying(
|
|
290
|
+
request: PitchCorrectToMidiTimevaryingRequest,
|
|
291
|
+
): Float32Array;
|
|
144
292
|
export function pitchCorrectToMidiTimevarying(
|
|
145
293
|
samples: Float32Array,
|
|
146
294
|
f0Hz: Float32Array,
|
|
147
295
|
targetMidi: number,
|
|
296
|
+
sampleRate?: number,
|
|
297
|
+
hopLength?: number,
|
|
298
|
+
voiced?: Int32Array,
|
|
299
|
+
voicedProb?: Float32Array,
|
|
300
|
+
options?: ValidateOptions,
|
|
301
|
+
): Float32Array;
|
|
302
|
+
export function pitchCorrectToMidiTimevarying(
|
|
303
|
+
samples: Float32Array | PitchCorrectToMidiTimevaryingRequest,
|
|
304
|
+
f0Hz?: Float32Array,
|
|
305
|
+
targetMidi?: number,
|
|
148
306
|
sampleRate = 22050,
|
|
149
307
|
hopLength = 512,
|
|
150
308
|
voiced?: Int32Array,
|
|
151
309
|
voicedProb?: Float32Array,
|
|
152
310
|
options: ValidateOptions = {},
|
|
153
311
|
): Float32Array {
|
|
154
|
-
|
|
155
|
-
|
|
312
|
+
const request: PitchCorrectToMidiTimevaryingRequest =
|
|
313
|
+
samples instanceof Float32Array
|
|
314
|
+
? {
|
|
315
|
+
samples,
|
|
316
|
+
f0Hz: f0Hz as Float32Array,
|
|
317
|
+
targetMidi: targetMidi as number,
|
|
318
|
+
sampleRate,
|
|
319
|
+
hopLength,
|
|
320
|
+
voiced,
|
|
321
|
+
voicedProb,
|
|
322
|
+
...options,
|
|
323
|
+
}
|
|
324
|
+
: samples;
|
|
325
|
+
assertSamples('pitchCorrectToMidiTimevarying', request.samples, request.validate !== false);
|
|
326
|
+
if (request.voiced && request.voiced.length !== request.f0Hz.length) {
|
|
156
327
|
throw new RangeError('pitchCorrectToMidiTimevarying: voiced length must match f0Hz length');
|
|
157
328
|
}
|
|
158
|
-
if (voicedProb && voicedProb.length !== f0Hz.length) {
|
|
329
|
+
if (request.voicedProb && request.voicedProb.length !== request.f0Hz.length) {
|
|
159
330
|
throw new RangeError('pitchCorrectToMidiTimevarying: voicedProb length must match f0Hz length');
|
|
160
331
|
}
|
|
161
332
|
// The embind layer reads the companion arrays as Float32Array (voiced uses
|
|
162
333
|
// 0.0/1.0); convert here so a single native conversion path suffices.
|
|
163
|
-
const voicedF32 = voiced ? Float32Array.from(voiced) : undefined;
|
|
334
|
+
const voicedF32 = request.voiced ? Float32Array.from(request.voiced) : undefined;
|
|
164
335
|
return requireModule().pitchCorrectToMidiTimevarying(
|
|
165
|
-
samples,
|
|
166
|
-
sampleRate,
|
|
167
|
-
f0Hz,
|
|
168
|
-
targetMidi,
|
|
169
|
-
hopLength,
|
|
336
|
+
request.samples,
|
|
337
|
+
request.sampleRate ?? 22050,
|
|
338
|
+
request.f0Hz,
|
|
339
|
+
request.targetMidi,
|
|
340
|
+
request.hopLength ?? 512,
|
|
170
341
|
voicedF32,
|
|
171
|
-
voicedProb,
|
|
342
|
+
request.voicedProb,
|
|
172
343
|
);
|
|
173
344
|
}
|
|
174
345
|
|
|
@@ -188,31 +359,43 @@ export function pitchCorrectToMidiTimevarying(
|
|
|
188
359
|
* @param options - Target mode + retune knobs + optional voiced/voicedProb arrays
|
|
189
360
|
* @returns Pitch-corrected audio
|
|
190
361
|
*/
|
|
362
|
+
export function pitchCorrectTimevarying(request: PitchCorrectTimevaryingRequest): Float32Array;
|
|
191
363
|
export function pitchCorrectTimevarying(
|
|
192
364
|
samples: Float32Array,
|
|
193
365
|
f0Hz: Float32Array,
|
|
366
|
+
sampleRate?: number,
|
|
367
|
+
hopLength?: number,
|
|
368
|
+
options?: PitchCorrectOptions,
|
|
369
|
+
): Float32Array;
|
|
370
|
+
export function pitchCorrectTimevarying(
|
|
371
|
+
samples: Float32Array | PitchCorrectTimevaryingRequest,
|
|
372
|
+
f0Hz?: Float32Array,
|
|
194
373
|
sampleRate = 22050,
|
|
195
374
|
hopLength = 512,
|
|
196
375
|
options: PitchCorrectOptions = {},
|
|
197
376
|
): Float32Array {
|
|
198
|
-
|
|
199
|
-
|
|
377
|
+
const request: PitchCorrectTimevaryingRequest =
|
|
378
|
+
samples instanceof Float32Array
|
|
379
|
+
? { samples, f0Hz: f0Hz as Float32Array, sampleRate, hopLength, ...options }
|
|
380
|
+
: samples;
|
|
381
|
+
assertSamples('pitchCorrectTimevarying', request.samples, request.validate !== false);
|
|
382
|
+
if (request.voiced && request.voiced.length !== request.f0Hz.length) {
|
|
200
383
|
throw new RangeError('pitchCorrectTimevarying: voiced length must match f0Hz length');
|
|
201
384
|
}
|
|
202
|
-
if (
|
|
385
|
+
if (request.voicedProb && request.voicedProb.length !== request.f0Hz.length) {
|
|
203
386
|
throw new RangeError('pitchCorrectTimevarying: voicedProb length must match f0Hz length');
|
|
204
387
|
}
|
|
205
388
|
// The embind layer reads the companion arrays as Float32Array (voiced uses
|
|
206
389
|
// 0.0/1.0); convert here so a single native conversion path suffices.
|
|
207
390
|
const nativeOptions = {
|
|
208
|
-
...
|
|
209
|
-
voiced:
|
|
391
|
+
...request,
|
|
392
|
+
voiced: request.voiced ? Float32Array.from(request.voiced) : undefined,
|
|
210
393
|
};
|
|
211
394
|
return requireModule().pitchCorrectTimevarying(
|
|
212
|
-
samples,
|
|
213
|
-
sampleRate,
|
|
214
|
-
f0Hz,
|
|
215
|
-
hopLength,
|
|
395
|
+
request.samples,
|
|
396
|
+
request.sampleRate ?? 22050,
|
|
397
|
+
request.f0Hz,
|
|
398
|
+
request.hopLength ?? 512,
|
|
216
399
|
nativeOptions,
|
|
217
400
|
);
|
|
218
401
|
}
|
|
@@ -227,18 +410,48 @@ export function pitchCorrectTimevarying(
|
|
|
227
410
|
* @param stretchRatio - Stretch ratio (0.5 = double duration, 2.0 = half duration)
|
|
228
411
|
* @returns Audio with the note region stretched
|
|
229
412
|
*/
|
|
413
|
+
export function noteStretch(request: NoteStretchRequest): Float32Array;
|
|
230
414
|
export function noteStretch(
|
|
231
415
|
samples: Float32Array,
|
|
416
|
+
sampleRate?: number,
|
|
417
|
+
options?: NoteStretchOptions & ValidateOptions,
|
|
418
|
+
): Float32Array;
|
|
419
|
+
export function noteStretch(
|
|
420
|
+
samples: Float32Array | NoteStretchRequest,
|
|
232
421
|
sampleRate = 22050,
|
|
233
422
|
options: NoteStretchOptions & ValidateOptions = {},
|
|
234
423
|
): Float32Array {
|
|
235
|
-
|
|
424
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
425
|
+
assertSamples('noteStretch', request.samples, request.validate !== false);
|
|
236
426
|
return requireModule().noteStretch(
|
|
237
|
-
samples,
|
|
238
|
-
sampleRate,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
427
|
+
request.samples,
|
|
428
|
+
request.sampleRate ?? 22050,
|
|
429
|
+
request.onsetSample ?? 0,
|
|
430
|
+
request.offsetSample ?? 0,
|
|
431
|
+
request.stretchRatio ?? 1.0,
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Move a note region to a new onset without changing its duration. */
|
|
436
|
+
export function noteMove(request: NoteMoveRequest): Float32Array;
|
|
437
|
+
export function noteMove(
|
|
438
|
+
samples: Float32Array,
|
|
439
|
+
sampleRate?: number,
|
|
440
|
+
options?: NoteMoveOptions & ValidateOptions,
|
|
441
|
+
): Float32Array;
|
|
442
|
+
export function noteMove(
|
|
443
|
+
samples: Float32Array | NoteMoveRequest,
|
|
444
|
+
sampleRate = 22050,
|
|
445
|
+
options: NoteMoveOptions & ValidateOptions = {},
|
|
446
|
+
): Float32Array {
|
|
447
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
448
|
+
assertSamples('noteMove', request.samples, request.validate !== false);
|
|
449
|
+
return requireModule().noteMove(
|
|
450
|
+
request.samples,
|
|
451
|
+
request.sampleRate ?? 22050,
|
|
452
|
+
request.onsetSample ?? 0,
|
|
453
|
+
request.offsetSample ?? 0,
|
|
454
|
+
request.targetOnsetSample ?? 0,
|
|
242
455
|
);
|
|
243
456
|
}
|
|
244
457
|
|
|
@@ -250,14 +463,25 @@ export function noteStretch(
|
|
|
250
463
|
* @param targetDb - Target peak level in dB (default: 0 dB = full scale)
|
|
251
464
|
* @returns Normalized audio
|
|
252
465
|
*/
|
|
466
|
+
export function normalize(request: NormalizeRequest): Float32Array;
|
|
253
467
|
export function normalize(
|
|
254
468
|
samples: Float32Array,
|
|
255
469
|
sampleRate: number,
|
|
470
|
+
targetDb?: number,
|
|
471
|
+
options?: ValidateOptions,
|
|
472
|
+
): Float32Array;
|
|
473
|
+
export function normalize(
|
|
474
|
+
samples: Float32Array | NormalizeRequest,
|
|
475
|
+
sampleRate?: number,
|
|
256
476
|
targetDb = 0.0,
|
|
257
477
|
options: ValidateOptions = {},
|
|
258
478
|
): Float32Array {
|
|
259
|
-
|
|
260
|
-
|
|
479
|
+
const request: NormalizeRequest =
|
|
480
|
+
samples instanceof Float32Array
|
|
481
|
+
? { samples, sampleRate: sampleRate as number, targetDb, ...options }
|
|
482
|
+
: samples;
|
|
483
|
+
assertSamples('normalize', request.samples, request.validate !== false);
|
|
484
|
+
return requireModule().normalize(request.samples, request.sampleRate, request.targetDb ?? 0.0);
|
|
261
485
|
}
|
|
262
486
|
|
|
263
487
|
/**
|
|
@@ -274,13 +498,29 @@ export function normalize(
|
|
|
274
498
|
* @param options - STFT + heal configuration ({@link SpectralEditOptions})
|
|
275
499
|
* @returns Edited audio
|
|
276
500
|
*/
|
|
501
|
+
export function spectralEdit(request: SpectralEditRequest): Float32Array;
|
|
277
502
|
export function spectralEdit(
|
|
278
503
|
samples: Float32Array,
|
|
279
504
|
sampleRate: number,
|
|
505
|
+
ops?: SpectralRegionOp[],
|
|
506
|
+
options?: SpectralEditOptions & ValidateOptions,
|
|
507
|
+
): Float32Array;
|
|
508
|
+
export function spectralEdit(
|
|
509
|
+
samples: Float32Array | SpectralEditRequest,
|
|
510
|
+
sampleRate?: number,
|
|
280
511
|
ops: SpectralRegionOp[] = [],
|
|
281
512
|
options: SpectralEditOptions & ValidateOptions = {},
|
|
282
513
|
): Float32Array {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
514
|
+
const request: SpectralEditRequest =
|
|
515
|
+
samples instanceof Float32Array
|
|
516
|
+
? { samples, sampleRate: sampleRate as number, ops, ...options }
|
|
517
|
+
: samples;
|
|
518
|
+
assertSamples('spectralEdit', request.samples, request.validate !== false);
|
|
519
|
+
assertSampleRate('spectralEdit', request.sampleRate);
|
|
520
|
+
return requireModule().spectralEdit(
|
|
521
|
+
request.samples,
|
|
522
|
+
request.sampleRate,
|
|
523
|
+
request.ops ?? [],
|
|
524
|
+
request as unknown as Record<string, unknown>,
|
|
525
|
+
);
|
|
286
526
|
}
|