@libraz/libsonare 1.5.2 → 1.5.4
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 +69 -725
- package/dist/index.d.ts +1155 -183
- package/dist/index.js +4924 -3935
- 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 +1141 -970
- package/dist/worklet.js.map +1 -1
- package/package.json +15 -3
- package/src/_chain_config.ts +46 -0
- package/src/audio.ts +24 -4
- package/src/codes.ts +27 -4
- package/src/effects_mastering.ts +25 -1
- package/src/effects_transform.ts +292 -46
- package/src/effects_voice_change.ts +46 -53
- package/src/feature_core.ts +316 -12
- package/src/feature_music.ts +371 -4
- 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 +370 -83
- 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 +30 -6
- package/src/project_internal.ts +12 -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/realtime_voice_changer.ts +79 -3
- package/src/sonare.js.d.ts +35 -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/src/feature_music.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ErrorCode, SonareError } from './errors';
|
|
1
2
|
import { getSonareModule } from './module_state';
|
|
2
3
|
import type {
|
|
3
4
|
AnalyzeSectionsOptions,
|
|
@@ -25,6 +26,80 @@ type GuardedOptions = ValidateOptions;
|
|
|
25
26
|
type AnalyzeSectionsGuardedOptions = AnalyzeSectionsOptions & ValidateOptions;
|
|
26
27
|
type MelodyGuardedOptions = MelodyOptions & ValidateOptions;
|
|
27
28
|
|
|
29
|
+
/** Canonical request form shared by the Constant-Q transform variants. */
|
|
30
|
+
export interface CqtRequest extends GuardedOptions {
|
|
31
|
+
samples: Float32Array;
|
|
32
|
+
sampleRate?: number;
|
|
33
|
+
hopLength?: number;
|
|
34
|
+
fmin?: number;
|
|
35
|
+
nBins?: number;
|
|
36
|
+
binsPerOctave?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Canonical request form for {@link vqt}. */
|
|
40
|
+
export interface VqtRequest extends CqtRequest {
|
|
41
|
+
gamma?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface OnsetEnvelopeRequest extends GuardedOptions {
|
|
45
|
+
samples: Float32Array;
|
|
46
|
+
sampleRate?: number;
|
|
47
|
+
nFft?: number;
|
|
48
|
+
hopLength?: number;
|
|
49
|
+
nMels?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface OnsetStrengthMultiRequest extends OnsetEnvelopeRequest {
|
|
53
|
+
nBands?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface FourierTempogramRequest extends GuardedOptions {
|
|
57
|
+
onsetEnvelope: Float32Array;
|
|
58
|
+
sampleRate?: number;
|
|
59
|
+
hopLength?: number;
|
|
60
|
+
winLength?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface TempogramRatioRequest extends GuardedOptions {
|
|
64
|
+
tempogramData: Float32Array;
|
|
65
|
+
winLength?: number;
|
|
66
|
+
sampleRate?: number;
|
|
67
|
+
hopLength?: number;
|
|
68
|
+
factors?: Float32Array | number[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface CqtToAudioRequest extends GuardedOptions {
|
|
72
|
+
magnitude: Float32Array;
|
|
73
|
+
nBins: number;
|
|
74
|
+
nFrames: number;
|
|
75
|
+
sampleRate?: number;
|
|
76
|
+
hopLength?: number;
|
|
77
|
+
fmin?: number;
|
|
78
|
+
binsPerOctave?: number;
|
|
79
|
+
nIter?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface VqtToAudioRequest extends CqtToAudioRequest {
|
|
83
|
+
gamma?: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface AnalyzeSectionsRequest extends AnalyzeSectionsGuardedOptions {
|
|
87
|
+
samples: Float32Array;
|
|
88
|
+
sampleRate?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface AnalyzeMelodyRequest extends MelodyGuardedOptions {
|
|
91
|
+
samples: Float32Array;
|
|
92
|
+
sampleRate?: number;
|
|
93
|
+
}
|
|
94
|
+
export interface LufsRequest extends ValidateOptions {
|
|
95
|
+
samples: Float32Array;
|
|
96
|
+
sampleRate?: number;
|
|
97
|
+
}
|
|
98
|
+
export interface NnlsChromaRequest extends GuardedOptions {
|
|
99
|
+
samples: Float32Array;
|
|
100
|
+
sampleRate?: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
28
103
|
function validateMusicSamples(
|
|
29
104
|
fnName: string,
|
|
30
105
|
samples: Float32Array,
|
|
@@ -61,11 +136,20 @@ function validateFrequencyBounds(fnName: string, fmin: number, fmax?: number): v
|
|
|
61
136
|
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
62
137
|
* @returns NNLS chroma result
|
|
63
138
|
*/
|
|
139
|
+
export function nnlsChroma(request: NnlsChromaRequest): WasmNnlsChromaResult;
|
|
64
140
|
export function nnlsChroma(
|
|
65
141
|
samples: Float32Array,
|
|
142
|
+
sampleRate?: number,
|
|
143
|
+
options?: GuardedOptions,
|
|
144
|
+
): WasmNnlsChromaResult;
|
|
145
|
+
export function nnlsChroma(
|
|
146
|
+
samples: Float32Array | NnlsChromaRequest,
|
|
66
147
|
sampleRate = 22050,
|
|
67
148
|
options: GuardedOptions = {},
|
|
68
149
|
): WasmNnlsChromaResult {
|
|
150
|
+
if (!(samples instanceof Float32Array)) {
|
|
151
|
+
return nnlsChroma(samples.samples, samples.sampleRate, samples);
|
|
152
|
+
}
|
|
69
153
|
validateMusicSamples('nnlsChroma', samples, sampleRate, options);
|
|
70
154
|
return requireModule().nnlsChroma(samples, sampleRate);
|
|
71
155
|
}
|
|
@@ -81,8 +165,18 @@ export function nnlsChroma(
|
|
|
81
165
|
* @param binsPerOctave - Bins per octave (default: 12)
|
|
82
166
|
* @returns CQT magnitude result
|
|
83
167
|
*/
|
|
168
|
+
export function cqt(request: CqtRequest): CqtResult;
|
|
84
169
|
export function cqt(
|
|
85
170
|
samples: Float32Array,
|
|
171
|
+
sampleRate?: number,
|
|
172
|
+
hopLength?: number,
|
|
173
|
+
fmin?: number,
|
|
174
|
+
nBins?: number,
|
|
175
|
+
binsPerOctave?: number,
|
|
176
|
+
options?: GuardedOptions,
|
|
177
|
+
): CqtResult;
|
|
178
|
+
export function cqt(
|
|
179
|
+
samples: Float32Array | CqtRequest,
|
|
86
180
|
sampleRate = 22050,
|
|
87
181
|
hopLength = 512,
|
|
88
182
|
fmin = 32.70319566257483,
|
|
@@ -90,6 +184,18 @@ export function cqt(
|
|
|
90
184
|
binsPerOctave = 12,
|
|
91
185
|
options: GuardedOptions = {},
|
|
92
186
|
): CqtResult {
|
|
187
|
+
if (!(samples instanceof Float32Array)) {
|
|
188
|
+
const request = samples;
|
|
189
|
+
return cqt(
|
|
190
|
+
request.samples,
|
|
191
|
+
request.sampleRate,
|
|
192
|
+
request.hopLength,
|
|
193
|
+
request.fmin,
|
|
194
|
+
request.nBins,
|
|
195
|
+
request.binsPerOctave,
|
|
196
|
+
request,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
93
199
|
validateMusicSamples('cqt', samples, sampleRate, options);
|
|
94
200
|
validatePositiveIntegers('cqt', { hopLength, nBins, binsPerOctave });
|
|
95
201
|
validateFrequencyBounds('cqt', fmin);
|
|
@@ -107,8 +213,18 @@ export function cqt(
|
|
|
107
213
|
* @param binsPerOctave - Bins per octave (default: 12)
|
|
108
214
|
* @returns CQT magnitude result
|
|
109
215
|
*/
|
|
216
|
+
export function pseudoCqt(request: CqtRequest): CqtResult;
|
|
110
217
|
export function pseudoCqt(
|
|
111
218
|
samples: Float32Array,
|
|
219
|
+
sampleRate?: number,
|
|
220
|
+
hopLength?: number,
|
|
221
|
+
fmin?: number,
|
|
222
|
+
nBins?: number,
|
|
223
|
+
binsPerOctave?: number,
|
|
224
|
+
options?: GuardedOptions,
|
|
225
|
+
): CqtResult;
|
|
226
|
+
export function pseudoCqt(
|
|
227
|
+
samples: Float32Array | CqtRequest,
|
|
112
228
|
sampleRate = 22050,
|
|
113
229
|
hopLength = 512,
|
|
114
230
|
fmin = 32.70319566257483,
|
|
@@ -116,6 +232,18 @@ export function pseudoCqt(
|
|
|
116
232
|
binsPerOctave = 12,
|
|
117
233
|
options: GuardedOptions = {},
|
|
118
234
|
): CqtResult {
|
|
235
|
+
if (!(samples instanceof Float32Array)) {
|
|
236
|
+
const request = samples;
|
|
237
|
+
return pseudoCqt(
|
|
238
|
+
request.samples,
|
|
239
|
+
request.sampleRate,
|
|
240
|
+
request.hopLength,
|
|
241
|
+
request.fmin,
|
|
242
|
+
request.nBins,
|
|
243
|
+
request.binsPerOctave,
|
|
244
|
+
request,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
119
247
|
validateMusicSamples('pseudoCqt', samples, sampleRate, options);
|
|
120
248
|
validatePositiveIntegers('pseudoCqt', { hopLength, nBins, binsPerOctave });
|
|
121
249
|
validateFrequencyBounds('pseudoCqt', fmin);
|
|
@@ -133,8 +261,18 @@ export function pseudoCqt(
|
|
|
133
261
|
* @param binsPerOctave - Bins per octave (default: 12)
|
|
134
262
|
* @returns CQT magnitude result
|
|
135
263
|
*/
|
|
264
|
+
export function hybridCqt(request: CqtRequest): CqtResult;
|
|
136
265
|
export function hybridCqt(
|
|
137
266
|
samples: Float32Array,
|
|
267
|
+
sampleRate?: number,
|
|
268
|
+
hopLength?: number,
|
|
269
|
+
fmin?: number,
|
|
270
|
+
nBins?: number,
|
|
271
|
+
binsPerOctave?: number,
|
|
272
|
+
options?: GuardedOptions,
|
|
273
|
+
): CqtResult;
|
|
274
|
+
export function hybridCqt(
|
|
275
|
+
samples: Float32Array | CqtRequest,
|
|
138
276
|
sampleRate = 22050,
|
|
139
277
|
hopLength = 512,
|
|
140
278
|
fmin = 32.70319566257483,
|
|
@@ -142,6 +280,18 @@ export function hybridCqt(
|
|
|
142
280
|
binsPerOctave = 12,
|
|
143
281
|
options: GuardedOptions = {},
|
|
144
282
|
): CqtResult {
|
|
283
|
+
if (!(samples instanceof Float32Array)) {
|
|
284
|
+
const request = samples;
|
|
285
|
+
return hybridCqt(
|
|
286
|
+
request.samples,
|
|
287
|
+
request.sampleRate,
|
|
288
|
+
request.hopLength,
|
|
289
|
+
request.fmin,
|
|
290
|
+
request.nBins,
|
|
291
|
+
request.binsPerOctave,
|
|
292
|
+
request,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
145
295
|
validateMusicSamples('hybridCqt', samples, sampleRate, options);
|
|
146
296
|
validatePositiveIntegers('hybridCqt', { hopLength, nBins, binsPerOctave });
|
|
147
297
|
validateFrequencyBounds('hybridCqt', fmin);
|
|
@@ -160,8 +310,19 @@ export function hybridCqt(
|
|
|
160
310
|
* @param gamma - Bandwidth offset; 0 is equivalent to CQT (default: 0)
|
|
161
311
|
* @returns VQT magnitude result (same shape as CQT)
|
|
162
312
|
*/
|
|
313
|
+
export function vqt(request: VqtRequest): CqtResult;
|
|
163
314
|
export function vqt(
|
|
164
315
|
samples: Float32Array,
|
|
316
|
+
sampleRate?: number,
|
|
317
|
+
hopLength?: number,
|
|
318
|
+
fmin?: number,
|
|
319
|
+
nBins?: number,
|
|
320
|
+
binsPerOctave?: number,
|
|
321
|
+
gamma?: number,
|
|
322
|
+
options?: GuardedOptions,
|
|
323
|
+
): CqtResult;
|
|
324
|
+
export function vqt(
|
|
325
|
+
samples: Float32Array | VqtRequest,
|
|
165
326
|
sampleRate = 22050,
|
|
166
327
|
hopLength = 512,
|
|
167
328
|
fmin = 32.70319566257483,
|
|
@@ -170,6 +331,19 @@ export function vqt(
|
|
|
170
331
|
gamma = 0,
|
|
171
332
|
options: GuardedOptions = {},
|
|
172
333
|
): CqtResult {
|
|
334
|
+
if (!(samples instanceof Float32Array)) {
|
|
335
|
+
const request = samples;
|
|
336
|
+
return vqt(
|
|
337
|
+
request.samples,
|
|
338
|
+
request.sampleRate,
|
|
339
|
+
request.hopLength,
|
|
340
|
+
request.fmin,
|
|
341
|
+
request.nBins,
|
|
342
|
+
request.binsPerOctave,
|
|
343
|
+
request.gamma,
|
|
344
|
+
request,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
173
347
|
validateMusicSamples('vqt', samples, sampleRate, options);
|
|
174
348
|
validatePositiveIntegers('vqt', { hopLength, nBins, binsPerOctave });
|
|
175
349
|
validateFrequencyBounds('vqt', fmin);
|
|
@@ -208,10 +382,22 @@ function validateCqtInverse(
|
|
|
208
382
|
}
|
|
209
383
|
|
|
210
384
|
/** Reconstruct mono audio from row-major CQT magnitude via Griffin-Lim. */
|
|
385
|
+
export function cqtToAudio(request: CqtToAudioRequest): Float32Array;
|
|
211
386
|
export function cqtToAudio(
|
|
212
387
|
magnitude: Float32Array,
|
|
213
388
|
nBins: number,
|
|
214
389
|
nFrames: number,
|
|
390
|
+
sampleRate?: number,
|
|
391
|
+
hopLength?: number,
|
|
392
|
+
fmin?: number,
|
|
393
|
+
binsPerOctave?: number,
|
|
394
|
+
nIter?: number,
|
|
395
|
+
options?: GuardedOptions,
|
|
396
|
+
): Float32Array;
|
|
397
|
+
export function cqtToAudio(
|
|
398
|
+
magnitude: Float32Array | CqtToAudioRequest,
|
|
399
|
+
nBins = 0,
|
|
400
|
+
nFrames = 0,
|
|
215
401
|
sampleRate = 22050,
|
|
216
402
|
hopLength = 512,
|
|
217
403
|
fmin = 32.70319566257483,
|
|
@@ -219,6 +405,20 @@ export function cqtToAudio(
|
|
|
219
405
|
nIter = 32,
|
|
220
406
|
options: GuardedOptions = {},
|
|
221
407
|
): Float32Array {
|
|
408
|
+
if (!(magnitude instanceof Float32Array)) {
|
|
409
|
+
const request = magnitude;
|
|
410
|
+
return cqtToAudio(
|
|
411
|
+
request.magnitude,
|
|
412
|
+
request.nBins,
|
|
413
|
+
request.nFrames,
|
|
414
|
+
request.sampleRate,
|
|
415
|
+
request.hopLength,
|
|
416
|
+
request.fmin,
|
|
417
|
+
request.binsPerOctave,
|
|
418
|
+
request.nIter,
|
|
419
|
+
request,
|
|
420
|
+
);
|
|
421
|
+
}
|
|
222
422
|
validateCqtInverse(
|
|
223
423
|
'cqtToAudio',
|
|
224
424
|
magnitude,
|
|
@@ -244,10 +444,23 @@ export function cqtToAudio(
|
|
|
244
444
|
}
|
|
245
445
|
|
|
246
446
|
/** Reconstruct mono audio from row-major VQT magnitude via Griffin-Lim. */
|
|
447
|
+
export function vqtToAudio(request: VqtToAudioRequest): Float32Array;
|
|
247
448
|
export function vqtToAudio(
|
|
248
449
|
magnitude: Float32Array,
|
|
249
450
|
nBins: number,
|
|
250
451
|
nFrames: number,
|
|
452
|
+
sampleRate?: number,
|
|
453
|
+
hopLength?: number,
|
|
454
|
+
fmin?: number,
|
|
455
|
+
binsPerOctave?: number,
|
|
456
|
+
gamma?: number,
|
|
457
|
+
nIter?: number,
|
|
458
|
+
options?: GuardedOptions,
|
|
459
|
+
): Float32Array;
|
|
460
|
+
export function vqtToAudio(
|
|
461
|
+
magnitude: Float32Array | VqtToAudioRequest,
|
|
462
|
+
nBins = 0,
|
|
463
|
+
nFrames = 0,
|
|
251
464
|
sampleRate = 22050,
|
|
252
465
|
hopLength = 512,
|
|
253
466
|
fmin = 32.70319566257483,
|
|
@@ -256,6 +469,21 @@ export function vqtToAudio(
|
|
|
256
469
|
nIter = 32,
|
|
257
470
|
options: GuardedOptions = {},
|
|
258
471
|
): Float32Array {
|
|
472
|
+
if (!(magnitude instanceof Float32Array)) {
|
|
473
|
+
const request = magnitude;
|
|
474
|
+
return vqtToAudio(
|
|
475
|
+
request.magnitude,
|
|
476
|
+
request.nBins,
|
|
477
|
+
request.nFrames,
|
|
478
|
+
request.sampleRate,
|
|
479
|
+
request.hopLength,
|
|
480
|
+
request.fmin,
|
|
481
|
+
request.binsPerOctave,
|
|
482
|
+
request.gamma,
|
|
483
|
+
request.nIter,
|
|
484
|
+
request,
|
|
485
|
+
);
|
|
486
|
+
}
|
|
259
487
|
validateCqtInverse(
|
|
260
488
|
'vqtToAudio',
|
|
261
489
|
magnitude,
|
|
@@ -295,11 +523,21 @@ export function vqtToAudio(
|
|
|
295
523
|
* @param minSectionSec - Minimum section duration in seconds (default: 4.0)
|
|
296
524
|
* @returns Array of detected sections
|
|
297
525
|
*/
|
|
526
|
+
export function analyzeSections(request: AnalyzeSectionsRequest): Section[];
|
|
298
527
|
export function analyzeSections(
|
|
299
528
|
samples: Float32Array,
|
|
529
|
+
sampleRate?: number,
|
|
530
|
+
options?: AnalyzeSectionsGuardedOptions,
|
|
531
|
+
): Section[];
|
|
532
|
+
export function analyzeSections(
|
|
533
|
+
samples: Float32Array | AnalyzeSectionsRequest,
|
|
300
534
|
sampleRate = 22050,
|
|
301
535
|
options: AnalyzeSectionsGuardedOptions = {},
|
|
302
536
|
): Section[] {
|
|
537
|
+
if (!(samples instanceof Float32Array)) {
|
|
538
|
+
const r = samples;
|
|
539
|
+
return analyzeSections(r.samples, r.sampleRate, r);
|
|
540
|
+
}
|
|
303
541
|
validateMusicSamples('analyzeSections', samples, sampleRate, options);
|
|
304
542
|
validatePositiveIntegers('analyzeSections', {
|
|
305
543
|
nFft: options.nFft ?? 2048,
|
|
@@ -357,20 +595,35 @@ export interface MelodyOptions {
|
|
|
357
595
|
* @param options - Tracker + tuning options ({@link MelodyOptions})
|
|
358
596
|
* @returns Melody contour with per-frame pitch points and summary stats
|
|
359
597
|
*/
|
|
598
|
+
export function analyzeMelody(request: AnalyzeMelodyRequest): MelodyResult;
|
|
360
599
|
export function analyzeMelody(
|
|
361
600
|
samples: Float32Array,
|
|
601
|
+
sampleRate?: number,
|
|
602
|
+
options?: MelodyGuardedOptions,
|
|
603
|
+
): MelodyResult;
|
|
604
|
+
export function analyzeMelody(
|
|
605
|
+
samples: Float32Array | AnalyzeMelodyRequest,
|
|
362
606
|
sampleRate = 22050,
|
|
363
607
|
options: MelodyGuardedOptions = {},
|
|
364
608
|
): MelodyResult {
|
|
609
|
+
if (!(samples instanceof Float32Array)) {
|
|
610
|
+
const r = samples;
|
|
611
|
+
return analyzeMelody(r.samples, r.sampleRate, r);
|
|
612
|
+
}
|
|
365
613
|
validateMusicSamples('analyzeMelody', samples, sampleRate, options);
|
|
366
614
|
const fmin = options.fmin ?? 65.0;
|
|
367
615
|
const fmax = options.fmax ?? 2093.0;
|
|
368
616
|
validateFrequencyBounds('analyzeMelody', fmin, fmax);
|
|
369
617
|
// The melody tracker's fmin is a YIN pitch floor: 0 is meaningless, and the
|
|
370
|
-
// flat C ABI (sonare_analyze_melody) rejects it
|
|
371
|
-
// guards fmin >= 0, so enforce strict positivity
|
|
618
|
+
// flat C ABI (sonare_analyze_melody) rejects it with InvalidParameter.
|
|
619
|
+
// validateFrequencyBounds only guards fmin >= 0, so enforce strict positivity
|
|
620
|
+
// here and report the same branded error the Node/Python surfaces raise.
|
|
372
621
|
if (fmin <= 0) {
|
|
373
|
-
throw new
|
|
622
|
+
throw new SonareError(
|
|
623
|
+
ErrorCode.InvalidParameter,
|
|
624
|
+
'InvalidParameter',
|
|
625
|
+
'analyzeMelody: fmin must be positive',
|
|
626
|
+
);
|
|
374
627
|
}
|
|
375
628
|
validatePositiveIntegers('analyzeMelody', {
|
|
376
629
|
frameLength: options.frameLength ?? 2048,
|
|
@@ -379,7 +632,11 @@ export function analyzeMelody(
|
|
|
379
632
|
const threshold = options.threshold ?? 0.1;
|
|
380
633
|
assertFiniteScalar('analyzeMelody', threshold, 'threshold');
|
|
381
634
|
if (threshold <= 0) {
|
|
382
|
-
throw new
|
|
635
|
+
throw new SonareError(
|
|
636
|
+
ErrorCode.InvalidParameter,
|
|
637
|
+
'InvalidParameter',
|
|
638
|
+
'analyzeMelody: threshold must be positive',
|
|
639
|
+
);
|
|
383
640
|
}
|
|
384
641
|
return requireModule().analyzeMelody(
|
|
385
642
|
samples,
|
|
@@ -404,14 +661,34 @@ export function analyzeMelody(
|
|
|
404
661
|
* @param nMels - Number of Mel bands (default: 128)
|
|
405
662
|
* @returns Onset envelope for each frame
|
|
406
663
|
*/
|
|
664
|
+
export function onsetEnvelope(request: OnsetEnvelopeRequest): Float32Array;
|
|
407
665
|
export function onsetEnvelope(
|
|
408
666
|
samples: Float32Array,
|
|
667
|
+
sampleRate?: number,
|
|
668
|
+
nFft?: number,
|
|
669
|
+
hopLength?: number,
|
|
670
|
+
nMels?: number,
|
|
671
|
+
options?: GuardedOptions,
|
|
672
|
+
): Float32Array;
|
|
673
|
+
export function onsetEnvelope(
|
|
674
|
+
samples: Float32Array | OnsetEnvelopeRequest,
|
|
409
675
|
sampleRate = 22050,
|
|
410
676
|
nFft = 2048,
|
|
411
677
|
hopLength = 512,
|
|
412
678
|
nMels = 128,
|
|
413
679
|
options: GuardedOptions = {},
|
|
414
680
|
): Float32Array {
|
|
681
|
+
if (!(samples instanceof Float32Array)) {
|
|
682
|
+
const request = samples;
|
|
683
|
+
return onsetEnvelope(
|
|
684
|
+
request.samples,
|
|
685
|
+
request.sampleRate,
|
|
686
|
+
request.nFft,
|
|
687
|
+
request.hopLength,
|
|
688
|
+
request.nMels,
|
|
689
|
+
request,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
415
692
|
validateMusicSamples('onsetEnvelope', samples, sampleRate, options);
|
|
416
693
|
validatePositiveIntegers('onsetEnvelope', { nFft, hopLength, nMels });
|
|
417
694
|
return requireModule().onsetEnvelope(samples, sampleRate, nFft, hopLength, nMels);
|
|
@@ -428,8 +705,18 @@ export function onsetEnvelope(
|
|
|
428
705
|
* @param nBands - Number of onset bands (default: 3)
|
|
429
706
|
* @returns Multi-band onset matrix
|
|
430
707
|
*/
|
|
708
|
+
export function onsetStrengthMulti(request: OnsetStrengthMultiRequest): OnsetStrengthMultiResult;
|
|
431
709
|
export function onsetStrengthMulti(
|
|
432
710
|
samples: Float32Array,
|
|
711
|
+
sampleRate?: number,
|
|
712
|
+
nFft?: number,
|
|
713
|
+
hopLength?: number,
|
|
714
|
+
nMels?: number,
|
|
715
|
+
nBands?: number,
|
|
716
|
+
options?: GuardedOptions,
|
|
717
|
+
): OnsetStrengthMultiResult;
|
|
718
|
+
export function onsetStrengthMulti(
|
|
719
|
+
samples: Float32Array | OnsetStrengthMultiRequest,
|
|
433
720
|
sampleRate = 22050,
|
|
434
721
|
nFft = 2048,
|
|
435
722
|
hopLength = 512,
|
|
@@ -437,6 +724,18 @@ export function onsetStrengthMulti(
|
|
|
437
724
|
nBands = 3,
|
|
438
725
|
options: GuardedOptions = {},
|
|
439
726
|
): OnsetStrengthMultiResult {
|
|
727
|
+
if (!(samples instanceof Float32Array)) {
|
|
728
|
+
const request = samples;
|
|
729
|
+
return onsetStrengthMulti(
|
|
730
|
+
request.samples,
|
|
731
|
+
request.sampleRate,
|
|
732
|
+
request.nFft,
|
|
733
|
+
request.hopLength,
|
|
734
|
+
request.nMels,
|
|
735
|
+
request.nBands,
|
|
736
|
+
request,
|
|
737
|
+
);
|
|
738
|
+
}
|
|
440
739
|
validateMusicSamples('onsetStrengthMulti', samples, sampleRate, options);
|
|
441
740
|
validatePositiveIntegers('onsetStrengthMulti', { nFft, hopLength, nMels, nBands });
|
|
442
741
|
return requireModule().onsetStrengthMulti(samples, sampleRate, nFft, hopLength, nMels, nBands);
|
|
@@ -451,13 +750,31 @@ export function onsetStrengthMulti(
|
|
|
451
750
|
* @param winLength - Window length in frames (default: 384)
|
|
452
751
|
* @returns Fourier tempogram result
|
|
453
752
|
*/
|
|
753
|
+
export function fourierTempogram(request: FourierTempogramRequest): WasmFourierTempogramResult;
|
|
454
754
|
export function fourierTempogram(
|
|
455
755
|
onsetEnvelope: Float32Array,
|
|
756
|
+
sampleRate?: number,
|
|
757
|
+
hopLength?: number,
|
|
758
|
+
winLength?: number,
|
|
759
|
+
options?: GuardedOptions,
|
|
760
|
+
): WasmFourierTempogramResult;
|
|
761
|
+
export function fourierTempogram(
|
|
762
|
+
onsetEnvelope: Float32Array | FourierTempogramRequest,
|
|
456
763
|
sampleRate = 22050,
|
|
457
764
|
hopLength = 512,
|
|
458
765
|
winLength = 384,
|
|
459
766
|
options: GuardedOptions = {},
|
|
460
767
|
): WasmFourierTempogramResult {
|
|
768
|
+
if (!(onsetEnvelope instanceof Float32Array)) {
|
|
769
|
+
const request = onsetEnvelope;
|
|
770
|
+
return fourierTempogram(
|
|
771
|
+
request.onsetEnvelope,
|
|
772
|
+
request.sampleRate,
|
|
773
|
+
request.hopLength,
|
|
774
|
+
request.winLength,
|
|
775
|
+
request,
|
|
776
|
+
);
|
|
777
|
+
}
|
|
461
778
|
assertSampleRate('fourierTempogram', sampleRate);
|
|
462
779
|
assertSamples('fourierTempogram', onsetEnvelope, options.validate !== false, 'onsetEnvelope');
|
|
463
780
|
validatePositiveIntegers('fourierTempogram', { hopLength, winLength });
|
|
@@ -475,14 +792,34 @@ export function fourierTempogram(
|
|
|
475
792
|
* default {0.5, 1, 2, 3, 4} is used.
|
|
476
793
|
* @returns Tempogram ratio features (one value per factor)
|
|
477
794
|
*/
|
|
795
|
+
export function tempogramRatio(request: TempogramRatioRequest): Float32Array;
|
|
478
796
|
export function tempogramRatio(
|
|
479
797
|
tempogramData: Float32Array,
|
|
798
|
+
winLength?: number,
|
|
799
|
+
sampleRate?: number,
|
|
800
|
+
hopLength?: number,
|
|
801
|
+
factors?: Float32Array | number[],
|
|
802
|
+
options?: GuardedOptions,
|
|
803
|
+
): Float32Array;
|
|
804
|
+
export function tempogramRatio(
|
|
805
|
+
tempogramData: Float32Array | TempogramRatioRequest,
|
|
480
806
|
winLength = 384,
|
|
481
807
|
sampleRate = 22050,
|
|
482
808
|
hopLength = 512,
|
|
483
809
|
factors?: Float32Array | number[],
|
|
484
810
|
options: GuardedOptions = {},
|
|
485
811
|
): Float32Array {
|
|
812
|
+
if (!(tempogramData instanceof Float32Array)) {
|
|
813
|
+
const request = tempogramData;
|
|
814
|
+
return tempogramRatio(
|
|
815
|
+
request.tempogramData,
|
|
816
|
+
request.winLength,
|
|
817
|
+
request.sampleRate,
|
|
818
|
+
request.hopLength,
|
|
819
|
+
request.factors,
|
|
820
|
+
request,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
486
823
|
assertSampleRate('tempogramRatio', sampleRate);
|
|
487
824
|
assertSamples('tempogramRatio', tempogramData, options.validate !== false, 'tempogramData');
|
|
488
825
|
validatePositiveIntegers('tempogramRatio', { winLength, hopLength });
|
|
@@ -498,11 +835,21 @@ export function tempogramRatio(
|
|
|
498
835
|
* dependent and a wrong rate yields wrong loudness.
|
|
499
836
|
* @returns Loudness measurement result
|
|
500
837
|
*/
|
|
838
|
+
export function lufs(request: LufsRequest): LufsResult;
|
|
501
839
|
export function lufs(
|
|
502
840
|
samples: Float32Array,
|
|
841
|
+
sampleRate?: number,
|
|
842
|
+
options?: ValidateOptions,
|
|
843
|
+
): LufsResult;
|
|
844
|
+
export function lufs(
|
|
845
|
+
samples: Float32Array | LufsRequest,
|
|
503
846
|
sampleRate = 22050,
|
|
504
847
|
options: ValidateOptions = {},
|
|
505
848
|
): LufsResult {
|
|
849
|
+
if (!(samples instanceof Float32Array)) {
|
|
850
|
+
const r = samples;
|
|
851
|
+
return lufs(r.samples, r.sampleRate, r);
|
|
852
|
+
}
|
|
506
853
|
assertSampleRate('lufs', sampleRate);
|
|
507
854
|
assertSamples('lufs', samples, options.validate !== false);
|
|
508
855
|
return requireModule().lufs(samples, sampleRate);
|
|
@@ -516,11 +863,21 @@ export function lufs(
|
|
|
516
863
|
* K-weighting is sample-rate dependent; pass the buffer's actual rate.
|
|
517
864
|
* @returns Momentary LUFS values over time
|
|
518
865
|
*/
|
|
866
|
+
export function momentaryLufs(request: LufsRequest): Float32Array;
|
|
519
867
|
export function momentaryLufs(
|
|
520
868
|
samples: Float32Array,
|
|
869
|
+
sampleRate?: number,
|
|
870
|
+
options?: ValidateOptions,
|
|
871
|
+
): Float32Array;
|
|
872
|
+
export function momentaryLufs(
|
|
873
|
+
samples: Float32Array | LufsRequest,
|
|
521
874
|
sampleRate = 22050,
|
|
522
875
|
options: ValidateOptions = {},
|
|
523
876
|
): Float32Array {
|
|
877
|
+
if (!(samples instanceof Float32Array)) {
|
|
878
|
+
const r = samples;
|
|
879
|
+
return momentaryLufs(r.samples, r.sampleRate, r);
|
|
880
|
+
}
|
|
524
881
|
assertSampleRate('momentaryLufs', sampleRate);
|
|
525
882
|
assertSamples('momentaryLufs', samples, options.validate !== false);
|
|
526
883
|
return requireModule().momentaryLufs(samples, sampleRate);
|
|
@@ -534,11 +891,21 @@ export function momentaryLufs(
|
|
|
534
891
|
* K-weighting is sample-rate dependent; pass the buffer's actual rate.
|
|
535
892
|
* @returns Short-term LUFS values over time
|
|
536
893
|
*/
|
|
894
|
+
export function shortTermLufs(request: LufsRequest): Float32Array;
|
|
537
895
|
export function shortTermLufs(
|
|
538
896
|
samples: Float32Array,
|
|
897
|
+
sampleRate?: number,
|
|
898
|
+
options?: ValidateOptions,
|
|
899
|
+
): Float32Array;
|
|
900
|
+
export function shortTermLufs(
|
|
901
|
+
samples: Float32Array | LufsRequest,
|
|
539
902
|
sampleRate = 22050,
|
|
540
903
|
options: ValidateOptions = {},
|
|
541
904
|
): Float32Array {
|
|
905
|
+
if (!(samples instanceof Float32Array)) {
|
|
906
|
+
const r = samples;
|
|
907
|
+
return shortTermLufs(r.samples, r.sampleRate, r);
|
|
908
|
+
}
|
|
542
909
|
assertSampleRate('shortTermLufs', sampleRate);
|
|
543
910
|
assertSamples('shortTermLufs', samples, options.validate !== false);
|
|
544
911
|
return requireModule().shortTermLufs(samples, sampleRate);
|
package/src/feature_pitch.ts
CHANGED
|
@@ -22,8 +22,30 @@ function requireModule() {
|
|
|
22
22
|
* @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)
|
|
23
23
|
* @returns Pitch detection result
|
|
24
24
|
*/
|
|
25
|
+
export interface PitchYinRequest {
|
|
26
|
+
samples: Float32Array;
|
|
27
|
+
sampleRate?: number;
|
|
28
|
+
frameLength?: number;
|
|
29
|
+
hopLength?: number;
|
|
30
|
+
fmin?: number;
|
|
31
|
+
fmax?: number;
|
|
32
|
+
threshold?: number;
|
|
33
|
+
fillNa?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function pitchYin(request: PitchYinRequest): PitchResult;
|
|
25
37
|
export function pitchYin(
|
|
26
38
|
samples: Float32Array,
|
|
39
|
+
sampleRate?: number,
|
|
40
|
+
frameLength?: number,
|
|
41
|
+
hopLength?: number,
|
|
42
|
+
fmin?: number,
|
|
43
|
+
fmax?: number,
|
|
44
|
+
threshold?: number,
|
|
45
|
+
fillNa?: boolean,
|
|
46
|
+
): PitchResult;
|
|
47
|
+
export function pitchYin(
|
|
48
|
+
samples: Float32Array | PitchYinRequest,
|
|
27
49
|
sampleRate = 22050,
|
|
28
50
|
frameLength = 2048,
|
|
29
51
|
hopLength = 512,
|
|
@@ -32,6 +54,19 @@ export function pitchYin(
|
|
|
32
54
|
threshold = 0.3,
|
|
33
55
|
fillNa = false,
|
|
34
56
|
): PitchResult {
|
|
57
|
+
if (!(samples instanceof Float32Array)) {
|
|
58
|
+
const request = samples;
|
|
59
|
+
return pitchYin(
|
|
60
|
+
request.samples,
|
|
61
|
+
request.sampleRate,
|
|
62
|
+
request.frameLength,
|
|
63
|
+
request.hopLength,
|
|
64
|
+
request.fmin,
|
|
65
|
+
request.fmax,
|
|
66
|
+
request.threshold,
|
|
67
|
+
request.fillNa,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
35
70
|
return requireModule().pitchYin(
|
|
36
71
|
samples,
|
|
37
72
|
sampleRate,
|
|
@@ -57,8 +92,21 @@ export function pitchYin(
|
|
|
57
92
|
* @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)
|
|
58
93
|
* @returns Pitch detection result
|
|
59
94
|
*/
|
|
95
|
+
export interface PitchPyinRequest extends PitchYinRequest {}
|
|
96
|
+
|
|
97
|
+
export function pitchPyin(request: PitchPyinRequest): PitchResult;
|
|
60
98
|
export function pitchPyin(
|
|
61
99
|
samples: Float32Array,
|
|
100
|
+
sampleRate?: number,
|
|
101
|
+
frameLength?: number,
|
|
102
|
+
hopLength?: number,
|
|
103
|
+
fmin?: number,
|
|
104
|
+
fmax?: number,
|
|
105
|
+
threshold?: number,
|
|
106
|
+
fillNa?: boolean,
|
|
107
|
+
): PitchResult;
|
|
108
|
+
export function pitchPyin(
|
|
109
|
+
samples: Float32Array | PitchPyinRequest,
|
|
62
110
|
sampleRate = 22050,
|
|
63
111
|
frameLength = 2048,
|
|
64
112
|
hopLength = 512,
|
|
@@ -67,6 +115,19 @@ export function pitchPyin(
|
|
|
67
115
|
threshold = 0.3,
|
|
68
116
|
fillNa = false,
|
|
69
117
|
): PitchResult {
|
|
118
|
+
if (!(samples instanceof Float32Array)) {
|
|
119
|
+
const request = samples;
|
|
120
|
+
return pitchPyin(
|
|
121
|
+
request.samples,
|
|
122
|
+
request.sampleRate,
|
|
123
|
+
request.frameLength,
|
|
124
|
+
request.hopLength,
|
|
125
|
+
request.fmin,
|
|
126
|
+
request.fmax,
|
|
127
|
+
request.threshold,
|
|
128
|
+
request.fillNa,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
70
131
|
return requireModule().pitchPyin(
|
|
71
132
|
samples,
|
|
72
133
|
sampleRate,
|