@libraz/libsonare 1.5.4 → 1.6.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/README.md +109 -20
- package/dist/analysis.d.ts +5730 -0
- package/dist/analysis.js +2464 -0
- package/dist/analysis.js.map +1 -0
- package/dist/index.d.ts +873 -63
- package/dist/index.js +1037 -265
- package/dist/index.js.map +1 -1
- package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
- package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
- package/dist/sonare-analysis.js +2 -0
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worker.d.ts +65 -0
- package/dist/worker.js +707 -0
- package/dist/worker.js.map +1 -0
- package/dist/worklet.d.ts +596 -86
- package/dist/worklet.js +1658 -235
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -11
- package/src/_chain_config.ts +5 -23
- package/src/analysis.ts +137 -0
- package/src/analysis_helpers.ts +29 -0
- package/src/audio.ts +5 -3
- package/src/clip_page_streamer.ts +62 -8
- package/src/codes.ts +51 -64
- package/src/effects_mastering.ts +1 -0
- package/src/effects_transform.ts +12 -6
- package/src/errors.ts +1 -0
- package/src/feature_core.ts +159 -3
- package/src/feature_music.ts +35 -16
- package/src/feature_pitch.ts +75 -6
- package/src/feature_spectral.ts +234 -8
- package/src/feature_spectrogram.ts +186 -5
- package/src/features.ts +16 -1
- package/src/index.ts +104 -7
- package/src/mastering_chain.ts +20 -8
- package/src/mastering_core.ts +7 -0
- package/src/mastering_dynamics.ts +7 -1
- package/src/metering.ts +51 -5
- package/src/mixer.ts +14 -1
- package/src/opfs_clip_pages.ts +26 -1
- package/src/project.ts +7 -0
- package/src/project_class.ts +74 -13
- package/src/project_internal.ts +24 -29
- package/src/project_types.ts +69 -10
- package/src/public_types.ts +68 -0
- package/src/public_types_mastering.ts +28 -0
- package/src/public_types_mixing.ts +11 -2
- package/src/public_types_music.ts +13 -0
- package/src/public_types_spectral.ts +43 -3
- package/src/quick_analysis.ts +48 -12
- package/src/realtime_engine.ts +162 -4
- package/src/realtime_voice_changer.ts +51 -77
- package/src/sonare-analysis.js.d.ts +8 -0
- package/src/sonare.js.d.ts +305 -9
- package/src/streaming_processors.ts +22 -1
- package/src/validation.ts +6 -0
- package/src/web_midi.ts +36 -4
- package/src/worker.ts +196 -0
- package/src/worker_client.ts +352 -0
- package/src/worker_protocol.ts +59 -0
- package/src/worklet/engine-automation.ts +2 -1
- package/src/worklet/engine-capture-facade.ts +13 -4
- package/src/worklet/engine-clips.ts +12 -3
- package/src/worklet/engine-node.ts +212 -4
- package/src/worklet/engine-offline.ts +7 -2
- package/src/worklet/engine-parameter-facade.ts +26 -5
- package/src/worklet/engine-processor.ts +383 -42
- package/src/worklet/engine-register.ts +8 -0
- package/src/worklet/engine-strips.ts +17 -1
- package/src/worklet/engine-sync.ts +2 -6
- package/src/worklet/engine-tempo-facade.ts +8 -6
- package/src/worklet/engine.ts +380 -13
- package/src/worklet/guards.ts +22 -1
- package/src/worklet/messages.ts +141 -9
- package/src/worklet/protocol.ts +303 -17
- package/src/worklet/voice-changer-processor.ts +12 -15
- package/src/worklet.ts +20 -1
package/src/effects_transform.ts
CHANGED
|
@@ -238,6 +238,10 @@ export function pitchShift(
|
|
|
238
238
|
/**
|
|
239
239
|
* Pitch-correct audio from a current MIDI note to a target MIDI note.
|
|
240
240
|
*
|
|
241
|
+
* Applies one constant, immediate transpose with no retune glide and preserves
|
|
242
|
+
* the input buffer length. Use {@link pitchCorrectToMidiTimevarying} for a
|
|
243
|
+
* caller-supplied pitch contour.
|
|
244
|
+
*
|
|
241
245
|
* @param samples - Audio samples (mono, float32)
|
|
242
246
|
* @param sampleRate - Sample rate in Hz
|
|
243
247
|
* @param currentMidi - Detected/current MIDI note number
|
|
@@ -279,7 +283,8 @@ export function pitchCorrectToMidi(
|
|
|
279
283
|
* the caller-supplied per-frame `f0Hz` contour and retunes every voiced frame
|
|
280
284
|
* toward `targetMidi`, so vibrato/drift in the source is tracked rather than
|
|
281
285
|
* flattened. `voiced` (non-zero = voiced) and `voicedProb` ([0,1]) are optional;
|
|
282
|
-
* omitting them treats every frame as voiced.
|
|
286
|
+
* omitting them treats every frame as voiced. An `f0Hz` NaN is accepted only
|
|
287
|
+
* when the corresponding `voiced` entry is zero, matching pYIN output.
|
|
283
288
|
*
|
|
284
289
|
* @param samples - Audio samples (mono, float32)
|
|
285
290
|
* @param f0Hz - Per-frame measured F0 in Hz (one entry per analysis frame)
|
|
@@ -354,7 +359,8 @@ export function pitchCorrectToMidiTimevarying(
|
|
|
354
359
|
* Generalises {@link pitchCorrectToMidiTimevarying}: the same caller-supplied
|
|
355
360
|
* per-frame `f0Hz` contour drives correction, but `options.mode` selects between
|
|
356
361
|
* a fixed-MIDI target (`'midi'`, default) and scale quantisation (`'scale'`),
|
|
357
|
-
* and the retune knobs shape natural-vs-robotic correction.
|
|
362
|
+
* and the retune knobs shape natural-vs-robotic correction. An `f0Hz` NaN is
|
|
363
|
+
* accepted only for a frame marked unvoiced.
|
|
358
364
|
*
|
|
359
365
|
* @param samples - Audio samples (mono, float32)
|
|
360
366
|
* @param f0Hz - Per-frame measured F0 in Hz (one entry per analysis frame)
|
|
@@ -411,7 +417,7 @@ export function pitchCorrectTimevarying(
|
|
|
411
417
|
* @param sampleRate - Sample rate in Hz
|
|
412
418
|
* @param onsetSample - Note onset position in samples
|
|
413
419
|
* @param offsetSample - Note offset position in samples
|
|
414
|
-
* @param stretchRatio - Stretch ratio (0.5 =
|
|
420
|
+
* @param stretchRatio - Stretch ratio (0.5 = half duration, 2.0 = double duration)
|
|
415
421
|
* @returns Audio with the note region stretched
|
|
416
422
|
*/
|
|
417
423
|
export function noteStretch(request: NoteStretchRequest): Float32Array;
|
|
@@ -431,7 +437,7 @@ export function noteStretch(
|
|
|
431
437
|
request.samples,
|
|
432
438
|
request.sampleRate ?? 22050,
|
|
433
439
|
request.onsetSample ?? 0,
|
|
434
|
-
request.offsetSample ??
|
|
440
|
+
request.offsetSample ?? request.samples.length,
|
|
435
441
|
request.stretchRatio ?? 1.0,
|
|
436
442
|
);
|
|
437
443
|
}
|
|
@@ -454,7 +460,7 @@ export function noteMove(
|
|
|
454
460
|
request.samples,
|
|
455
461
|
request.sampleRate ?? 22050,
|
|
456
462
|
request.onsetSample ?? 0,
|
|
457
|
-
request.offsetSample ??
|
|
463
|
+
request.offsetSample ?? request.samples.length,
|
|
458
464
|
request.targetOnsetSample ?? 0,
|
|
459
465
|
);
|
|
460
466
|
}
|
|
@@ -464,7 +470,7 @@ export function noteMove(
|
|
|
464
470
|
*
|
|
465
471
|
* @param samples - Audio samples (mono, float32)
|
|
466
472
|
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
467
|
-
* @param targetDb -
|
|
473
|
+
* @param targetDb - Finite target at or below 0 dBFS (default: 0 dB = full scale)
|
|
468
474
|
* @returns Normalized audio
|
|
469
475
|
*/
|
|
470
476
|
export function normalize(request: NormalizeRequest): Float32Array;
|
package/src/errors.ts
CHANGED
package/src/feature_core.ts
CHANGED
|
@@ -11,6 +11,85 @@ function requireModule() {
|
|
|
11
11
|
return getSonareModule();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export function tone(request?: ToneRequest): Float32Array;
|
|
15
|
+
export function tone(
|
|
16
|
+
frequency?: number,
|
|
17
|
+
sampleRate?: number,
|
|
18
|
+
duration?: number,
|
|
19
|
+
phase?: number,
|
|
20
|
+
amplitude?: number,
|
|
21
|
+
): Float32Array;
|
|
22
|
+
export function tone(
|
|
23
|
+
frequency: number | ToneRequest = 440,
|
|
24
|
+
sampleRate = 22050,
|
|
25
|
+
duration = 1,
|
|
26
|
+
phase = 0,
|
|
27
|
+
amplitude = 1,
|
|
28
|
+
): Float32Array {
|
|
29
|
+
const request =
|
|
30
|
+
typeof frequency === 'number'
|
|
31
|
+
? { frequency, sampleRate, duration, phase, amplitude }
|
|
32
|
+
: frequency;
|
|
33
|
+
return requireModule().tone(
|
|
34
|
+
request.frequency ?? 440,
|
|
35
|
+
request.sampleRate ?? 22050,
|
|
36
|
+
request.duration ?? 1,
|
|
37
|
+
request.phase ?? 0,
|
|
38
|
+
request.amplitude ?? 1,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function chirp(request?: ChirpRequest): Float32Array;
|
|
43
|
+
export function chirp(
|
|
44
|
+
fmin?: number,
|
|
45
|
+
fmax?: number,
|
|
46
|
+
sampleRate?: number,
|
|
47
|
+
duration?: number,
|
|
48
|
+
linear?: boolean,
|
|
49
|
+
): Float32Array;
|
|
50
|
+
export function chirp(
|
|
51
|
+
fmin: number | ChirpRequest = 440,
|
|
52
|
+
fmax = 880,
|
|
53
|
+
sampleRate = 22050,
|
|
54
|
+
duration = 1,
|
|
55
|
+
linear = true,
|
|
56
|
+
): Float32Array {
|
|
57
|
+
const request = typeof fmin === 'number' ? { fmin, fmax, sampleRate, duration, linear } : fmin;
|
|
58
|
+
return requireModule().chirp(
|
|
59
|
+
request.fmin ?? 440,
|
|
60
|
+
request.fmax ?? 880,
|
|
61
|
+
request.sampleRate ?? 22050,
|
|
62
|
+
request.duration ?? 1,
|
|
63
|
+
request.linear ?? true,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function clicks(request: ClicksRequest): Float32Array;
|
|
68
|
+
export function clicks(
|
|
69
|
+
times: Float32Array,
|
|
70
|
+
sampleRate?: number,
|
|
71
|
+
length?: number,
|
|
72
|
+
frequency?: number,
|
|
73
|
+
clickDuration?: number,
|
|
74
|
+
): Float32Array;
|
|
75
|
+
export function clicks(
|
|
76
|
+
times: Float32Array | ClicksRequest,
|
|
77
|
+
sampleRate = 22050,
|
|
78
|
+
length = 0,
|
|
79
|
+
frequency = 1000,
|
|
80
|
+
clickDuration = 0.1,
|
|
81
|
+
): Float32Array {
|
|
82
|
+
const request =
|
|
83
|
+
times instanceof Float32Array ? { times, sampleRate, length, frequency, clickDuration } : times;
|
|
84
|
+
return requireModule().clicks(
|
|
85
|
+
request.times,
|
|
86
|
+
request.sampleRate ?? 22050,
|
|
87
|
+
request.length ?? 0,
|
|
88
|
+
request.frequency ?? 1000,
|
|
89
|
+
request.clickDuration ?? 0.1,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
14
93
|
export interface DbConversionRequest {
|
|
15
94
|
values: Float32Array;
|
|
16
95
|
ref?: number;
|
|
@@ -30,6 +109,27 @@ export interface FrameSignalRequest {
|
|
|
30
109
|
frameLength: number;
|
|
31
110
|
hopLength: number;
|
|
32
111
|
}
|
|
112
|
+
export interface ToneRequest {
|
|
113
|
+
frequency?: number;
|
|
114
|
+
sampleRate?: number;
|
|
115
|
+
duration?: number;
|
|
116
|
+
phase?: number;
|
|
117
|
+
amplitude?: number;
|
|
118
|
+
}
|
|
119
|
+
export interface ChirpRequest {
|
|
120
|
+
fmin?: number;
|
|
121
|
+
fmax?: number;
|
|
122
|
+
sampleRate?: number;
|
|
123
|
+
duration?: number;
|
|
124
|
+
linear?: boolean;
|
|
125
|
+
}
|
|
126
|
+
export interface ClicksRequest {
|
|
127
|
+
times: Float32Array;
|
|
128
|
+
sampleRate?: number;
|
|
129
|
+
length?: number;
|
|
130
|
+
frequency?: number;
|
|
131
|
+
clickDuration?: number;
|
|
132
|
+
}
|
|
33
133
|
/** Canonical request form for pre/de-emphasis filters. */
|
|
34
134
|
export interface EmphasisRequest {
|
|
35
135
|
samples: Float32Array;
|
|
@@ -55,6 +155,10 @@ export interface FixFramesRequest {
|
|
|
55
155
|
xMax?: number;
|
|
56
156
|
pad?: boolean;
|
|
57
157
|
}
|
|
158
|
+
export interface OnsetBacktrackRequest {
|
|
159
|
+
events: Int32Array;
|
|
160
|
+
energy: Float32Array;
|
|
161
|
+
}
|
|
58
162
|
/** Canonical request form for peak selection. */
|
|
59
163
|
export interface PeakPickRequest {
|
|
60
164
|
values: Float32Array;
|
|
@@ -81,6 +185,14 @@ export interface PcenRequest {
|
|
|
81
185
|
values: Float32Array;
|
|
82
186
|
nBins: number;
|
|
83
187
|
nFrames: number;
|
|
188
|
+
sampleRate?: number;
|
|
189
|
+
hopLength?: number;
|
|
190
|
+
timeConstant?: number;
|
|
191
|
+
gain?: number;
|
|
192
|
+
bias?: number;
|
|
193
|
+
power?: number;
|
|
194
|
+
eps?: number;
|
|
195
|
+
/** @deprecated Put PCEN fields directly on the request object. */
|
|
84
196
|
options?: Record<string, number>;
|
|
85
197
|
}
|
|
86
198
|
export interface TempogramRequest {
|
|
@@ -89,6 +201,8 @@ export interface TempogramRequest {
|
|
|
89
201
|
hopLength?: number;
|
|
90
202
|
winLength?: number;
|
|
91
203
|
mode?: TempogramMode;
|
|
204
|
+
center?: boolean;
|
|
205
|
+
norm?: boolean;
|
|
92
206
|
}
|
|
93
207
|
export interface CyclicTempogramRequest {
|
|
94
208
|
onsetEnvelope: Float32Array;
|
|
@@ -383,6 +497,18 @@ export function fixFrames(
|
|
|
383
497
|
return requireModule().fixFrames(frames, xMin, xMax, pad);
|
|
384
498
|
}
|
|
385
499
|
|
|
500
|
+
export function onsetBacktrack(request: OnsetBacktrackRequest): Int32Array;
|
|
501
|
+
export function onsetBacktrack(events: Int32Array, energy: Float32Array): Int32Array;
|
|
502
|
+
export function onsetBacktrack(
|
|
503
|
+
events: Int32Array | OnsetBacktrackRequest,
|
|
504
|
+
energy?: Float32Array,
|
|
505
|
+
): Int32Array {
|
|
506
|
+
if (!(events instanceof Int32Array)) {
|
|
507
|
+
return onsetBacktrack(events.events, events.energy);
|
|
508
|
+
}
|
|
509
|
+
return requireModule().onsetBacktrack(events, energy as Float32Array);
|
|
510
|
+
}
|
|
511
|
+
|
|
386
512
|
export function peakPick(request: PeakPickRequest): Int32Array;
|
|
387
513
|
export function peakPick(
|
|
388
514
|
values: Float32Array,
|
|
@@ -449,7 +575,17 @@ export function pcen(
|
|
|
449
575
|
): Float32Array {
|
|
450
576
|
if (!(values instanceof Float32Array)) {
|
|
451
577
|
const r = values;
|
|
452
|
-
|
|
578
|
+
const {
|
|
579
|
+
values: requestValues,
|
|
580
|
+
nBins: requestBins,
|
|
581
|
+
nFrames: requestFrames,
|
|
582
|
+
options: legacyOptions,
|
|
583
|
+
...flatOptions
|
|
584
|
+
} = r;
|
|
585
|
+
return pcen(requestValues, requestBins, requestFrames, {
|
|
586
|
+
...legacyOptions,
|
|
587
|
+
...flatOptions,
|
|
588
|
+
});
|
|
453
589
|
}
|
|
454
590
|
return requireModule().pcen(values, nBins, nFrames, options);
|
|
455
591
|
}
|
|
@@ -474,6 +610,8 @@ export function tempogram(
|
|
|
474
610
|
hopLength?: number,
|
|
475
611
|
winLength?: number,
|
|
476
612
|
mode?: TempogramMode,
|
|
613
|
+
center?: boolean,
|
|
614
|
+
norm?: boolean,
|
|
477
615
|
): WasmTempogramResult;
|
|
478
616
|
export function tempogram(
|
|
479
617
|
onsetEnvelope: Float32Array | TempogramRequest,
|
|
@@ -481,12 +619,30 @@ export function tempogram(
|
|
|
481
619
|
hopLength = 512,
|
|
482
620
|
winLength = 384,
|
|
483
621
|
mode: TempogramMode = 'autocorrelation',
|
|
622
|
+
center = true,
|
|
623
|
+
norm = true,
|
|
484
624
|
): WasmTempogramResult {
|
|
485
625
|
if (!(onsetEnvelope instanceof Float32Array)) {
|
|
486
626
|
const r = onsetEnvelope;
|
|
487
|
-
return tempogram(
|
|
627
|
+
return tempogram(
|
|
628
|
+
r.onsetEnvelope,
|
|
629
|
+
r.sampleRate,
|
|
630
|
+
r.hopLength,
|
|
631
|
+
r.winLength,
|
|
632
|
+
r.mode,
|
|
633
|
+
r.center,
|
|
634
|
+
r.norm,
|
|
635
|
+
);
|
|
488
636
|
}
|
|
489
|
-
return requireModule().tempogram(
|
|
637
|
+
return requireModule().tempogram(
|
|
638
|
+
onsetEnvelope,
|
|
639
|
+
sampleRate,
|
|
640
|
+
hopLength,
|
|
641
|
+
winLength,
|
|
642
|
+
mode,
|
|
643
|
+
center,
|
|
644
|
+
norm,
|
|
645
|
+
);
|
|
490
646
|
}
|
|
491
647
|
|
|
492
648
|
export function cyclicTempogram(request: CyclicTempogramRequest): WasmCyclicTempogramResult;
|
package/src/feature_music.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface FourierTempogramRequest extends GuardedOptions {
|
|
|
58
58
|
sampleRate?: number;
|
|
59
59
|
hopLength?: number;
|
|
60
60
|
winLength?: number;
|
|
61
|
+
center?: boolean;
|
|
62
|
+
norm?: boolean;
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
export interface TempogramRatioRequest extends GuardedOptions {
|
|
@@ -98,6 +100,9 @@ export interface LufsRequest extends ValidateOptions {
|
|
|
98
100
|
export interface NnlsChromaRequest extends GuardedOptions {
|
|
99
101
|
samples: Float32Array;
|
|
100
102
|
sampleRate?: number;
|
|
103
|
+
enableStftBlend?: boolean;
|
|
104
|
+
stftBlendWeight?: number;
|
|
105
|
+
stftBlendNFft?: number;
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
function validateMusicSamples(
|
|
@@ -140,18 +145,24 @@ export function nnlsChroma(request: NnlsChromaRequest): WasmNnlsChromaResult;
|
|
|
140
145
|
export function nnlsChroma(
|
|
141
146
|
samples: Float32Array,
|
|
142
147
|
sampleRate?: number,
|
|
143
|
-
options?:
|
|
148
|
+
options?: Omit<NnlsChromaRequest, 'samples' | 'sampleRate'>,
|
|
144
149
|
): WasmNnlsChromaResult;
|
|
145
150
|
export function nnlsChroma(
|
|
146
151
|
samples: Float32Array | NnlsChromaRequest,
|
|
147
152
|
sampleRate = 22050,
|
|
148
|
-
options:
|
|
153
|
+
options: Omit<NnlsChromaRequest, 'samples' | 'sampleRate'> = {},
|
|
149
154
|
): WasmNnlsChromaResult {
|
|
150
155
|
if (!(samples instanceof Float32Array)) {
|
|
151
156
|
return nnlsChroma(samples.samples, samples.sampleRate, samples);
|
|
152
157
|
}
|
|
153
158
|
validateMusicSamples('nnlsChroma', samples, sampleRate, options);
|
|
154
|
-
return requireModule().nnlsChroma(
|
|
159
|
+
return requireModule().nnlsChroma(
|
|
160
|
+
samples,
|
|
161
|
+
sampleRate,
|
|
162
|
+
options.enableStftBlend ?? true,
|
|
163
|
+
options.stftBlendWeight ?? 0.55,
|
|
164
|
+
options.stftBlendNFft ?? 4096,
|
|
165
|
+
);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
/**
|
|
@@ -307,7 +318,8 @@ export function hybridCqt(
|
|
|
307
318
|
* @param fmin - Minimum frequency in Hz (default: 32.70319566257483, C1)
|
|
308
319
|
* @param nBins - Number of frequency bins (default: 84)
|
|
309
320
|
* @param binsPerOctave - Bins per octave (default: 12)
|
|
310
|
-
* @param gamma - Bandwidth offset;
|
|
321
|
+
* @param gamma - Bandwidth offset; negative selects the automatic ERB-derived
|
|
322
|
+
* value, while 0 is equivalent to CQT (default: -1)
|
|
311
323
|
* @returns VQT magnitude result (same shape as CQT)
|
|
312
324
|
*/
|
|
313
325
|
export function vqt(request: VqtRequest): CqtResult;
|
|
@@ -328,7 +340,7 @@ export function vqt(
|
|
|
328
340
|
fmin = 32.70319566257483,
|
|
329
341
|
nBins = 84,
|
|
330
342
|
binsPerOctave = 12,
|
|
331
|
-
gamma =
|
|
343
|
+
gamma = -1,
|
|
332
344
|
options: GuardedOptions = {},
|
|
333
345
|
): CqtResult {
|
|
334
346
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -348,9 +360,6 @@ export function vqt(
|
|
|
348
360
|
validatePositiveIntegers('vqt', { hopLength, nBins, binsPerOctave });
|
|
349
361
|
validateFrequencyBounds('vqt', fmin);
|
|
350
362
|
assertFiniteScalar('vqt', gamma, 'gamma');
|
|
351
|
-
if (gamma < 0) {
|
|
352
|
-
throw new RangeError('vqt: gamma must be non-negative');
|
|
353
|
-
}
|
|
354
363
|
return requireModule().vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);
|
|
355
364
|
}
|
|
356
365
|
|
|
@@ -465,7 +474,7 @@ export function vqtToAudio(
|
|
|
465
474
|
hopLength = 512,
|
|
466
475
|
fmin = 32.70319566257483,
|
|
467
476
|
binsPerOctave = 12,
|
|
468
|
-
gamma =
|
|
477
|
+
gamma = -1,
|
|
469
478
|
nIter = 32,
|
|
470
479
|
options: GuardedOptions = {},
|
|
471
480
|
): Float32Array {
|
|
@@ -497,9 +506,6 @@ export function vqtToAudio(
|
|
|
497
506
|
options,
|
|
498
507
|
);
|
|
499
508
|
assertFiniteScalar('vqtToAudio', gamma, 'gamma');
|
|
500
|
-
if (gamma < 0) {
|
|
501
|
-
throw new RangeError('vqtToAudio: gamma must be non-negative');
|
|
502
|
-
}
|
|
503
509
|
return requireModule().vqtToAudio(
|
|
504
510
|
magnitude,
|
|
505
511
|
nBins,
|
|
@@ -544,8 +550,8 @@ export function analyzeSections(
|
|
|
544
550
|
hopLength: options.hopLength ?? 512,
|
|
545
551
|
});
|
|
546
552
|
assertFiniteScalar('analyzeSections', options.minSectionSec ?? 4.0, 'minSectionSec');
|
|
547
|
-
if ((options.minSectionSec ?? 4.0)
|
|
548
|
-
throw new RangeError('analyzeSections: minSectionSec must be
|
|
553
|
+
if ((options.minSectionSec ?? 4.0) < 0) {
|
|
554
|
+
throw new RangeError('analyzeSections: minSectionSec must be non-negative');
|
|
549
555
|
}
|
|
550
556
|
// The embind value marshalling returns an array whose constructor is not this
|
|
551
557
|
// realm's Array; chaining .map() onto it propagates that constructor via
|
|
@@ -580,7 +586,7 @@ export interface MelodyOptions {
|
|
|
580
586
|
*/
|
|
581
587
|
usePyin?: boolean;
|
|
582
588
|
/**
|
|
583
|
-
* When {@link usePyin} is `true`,
|
|
589
|
+
* When {@link usePyin} is `true`, zero-pad by `frameLength / 2` so frame
|
|
584
590
|
* `i` is centered at `i * hopLength` (matches `librosa.pyin(center=True)`).
|
|
585
591
|
* Ignored by the plain-YIN path. Defaults to `true`.
|
|
586
592
|
*/
|
|
@@ -756,6 +762,8 @@ export function fourierTempogram(
|
|
|
756
762
|
sampleRate?: number,
|
|
757
763
|
hopLength?: number,
|
|
758
764
|
winLength?: number,
|
|
765
|
+
center?: boolean,
|
|
766
|
+
norm?: boolean,
|
|
759
767
|
options?: GuardedOptions,
|
|
760
768
|
): WasmFourierTempogramResult;
|
|
761
769
|
export function fourierTempogram(
|
|
@@ -763,6 +771,8 @@ export function fourierTempogram(
|
|
|
763
771
|
sampleRate = 22050,
|
|
764
772
|
hopLength = 512,
|
|
765
773
|
winLength = 384,
|
|
774
|
+
center = true,
|
|
775
|
+
norm = true,
|
|
766
776
|
options: GuardedOptions = {},
|
|
767
777
|
): WasmFourierTempogramResult {
|
|
768
778
|
if (!(onsetEnvelope instanceof Float32Array)) {
|
|
@@ -772,13 +782,22 @@ export function fourierTempogram(
|
|
|
772
782
|
request.sampleRate,
|
|
773
783
|
request.hopLength,
|
|
774
784
|
request.winLength,
|
|
785
|
+
request.center,
|
|
786
|
+
request.norm,
|
|
775
787
|
request,
|
|
776
788
|
);
|
|
777
789
|
}
|
|
778
790
|
assertSampleRate('fourierTempogram', sampleRate);
|
|
779
791
|
assertSamples('fourierTempogram', onsetEnvelope, options.validate !== false, 'onsetEnvelope');
|
|
780
792
|
validatePositiveIntegers('fourierTempogram', { hopLength, winLength });
|
|
781
|
-
return requireModule().fourierTempogram(
|
|
793
|
+
return requireModule().fourierTempogram(
|
|
794
|
+
onsetEnvelope,
|
|
795
|
+
sampleRate,
|
|
796
|
+
hopLength,
|
|
797
|
+
winLength,
|
|
798
|
+
center,
|
|
799
|
+
norm,
|
|
800
|
+
);
|
|
782
801
|
}
|
|
783
802
|
|
|
784
803
|
/**
|
package/src/feature_pitch.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSonareModule } from './module_state';
|
|
2
|
-
import type { PitchResult } from './public_types';
|
|
2
|
+
import type { NoteSegment, PiptrackResult, PitchResult } from './public_types';
|
|
3
3
|
|
|
4
4
|
function requireModule() {
|
|
5
5
|
return getSonareModule();
|
|
@@ -18,8 +18,8 @@ function requireModule() {
|
|
|
18
18
|
* @param hopLength - Hop length (default: 512)
|
|
19
19
|
* @param fmin - Minimum frequency in Hz (default: 65)
|
|
20
20
|
* @param fmax - Maximum frequency in Hz (default: 2093)
|
|
21
|
-
* @param threshold - YIN threshold (default: 0.
|
|
22
|
-
* @param fillNa -
|
|
21
|
+
* @param threshold - YIN threshold (default: 0.1)
|
|
22
|
+
* @param fillNa - Retained for compatibility; YIN always returns a finite per-frame estimate.
|
|
23
23
|
* @returns Pitch detection result
|
|
24
24
|
*/
|
|
25
25
|
export interface PitchYinRequest {
|
|
@@ -33,6 +33,51 @@ export interface PitchYinRequest {
|
|
|
33
33
|
fillNa?: boolean;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export interface PiptrackRequest {
|
|
37
|
+
samples: Float32Array;
|
|
38
|
+
sampleRate?: number;
|
|
39
|
+
nFft?: number;
|
|
40
|
+
hopLength?: number;
|
|
41
|
+
fmin?: number;
|
|
42
|
+
fmax?: number;
|
|
43
|
+
threshold?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Per-bin spectral pitch candidates and peak magnitudes (librosa.piptrack). */
|
|
47
|
+
export function piptrack(request: PiptrackRequest): PiptrackResult;
|
|
48
|
+
export function piptrack(
|
|
49
|
+
samples: Float32Array,
|
|
50
|
+
sampleRate?: number,
|
|
51
|
+
nFft?: number,
|
|
52
|
+
hopLength?: number,
|
|
53
|
+
fmin?: number,
|
|
54
|
+
fmax?: number,
|
|
55
|
+
threshold?: number,
|
|
56
|
+
): PiptrackResult;
|
|
57
|
+
export function piptrack(
|
|
58
|
+
samples: Float32Array | PiptrackRequest,
|
|
59
|
+
sampleRate = 22050,
|
|
60
|
+
nFft = 2048,
|
|
61
|
+
hopLength = 512,
|
|
62
|
+
fmin = 150,
|
|
63
|
+
fmax = 4000,
|
|
64
|
+
threshold = 0.1,
|
|
65
|
+
): PiptrackResult {
|
|
66
|
+
if (!(samples instanceof Float32Array)) {
|
|
67
|
+
const request = samples;
|
|
68
|
+
return piptrack(
|
|
69
|
+
request.samples,
|
|
70
|
+
request.sampleRate,
|
|
71
|
+
request.nFft,
|
|
72
|
+
request.hopLength,
|
|
73
|
+
request.fmin,
|
|
74
|
+
request.fmax,
|
|
75
|
+
request.threshold,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return requireModule().piptrack(samples, sampleRate, nFft, hopLength, fmin, fmax, threshold);
|
|
79
|
+
}
|
|
80
|
+
|
|
36
81
|
export function pitchYin(request: PitchYinRequest): PitchResult;
|
|
37
82
|
export function pitchYin(
|
|
38
83
|
samples: Float32Array,
|
|
@@ -51,7 +96,7 @@ export function pitchYin(
|
|
|
51
96
|
hopLength = 512,
|
|
52
97
|
fmin = 65.0,
|
|
53
98
|
fmax = 2093.0,
|
|
54
|
-
threshold = 0.
|
|
99
|
+
threshold = 0.1,
|
|
55
100
|
fillNa = false,
|
|
56
101
|
): PitchResult {
|
|
57
102
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -88,7 +133,7 @@ export function pitchYin(
|
|
|
88
133
|
* @param hopLength - Hop length (default: 512)
|
|
89
134
|
* @param fmin - Minimum frequency in Hz (default: 65)
|
|
90
135
|
* @param fmax - Maximum frequency in Hz (default: 2093)
|
|
91
|
-
* @param threshold - YIN threshold (default: 0.
|
|
136
|
+
* @param threshold - YIN threshold (default: 0.1)
|
|
92
137
|
* @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)
|
|
93
138
|
* @returns Pitch detection result
|
|
94
139
|
*/
|
|
@@ -112,7 +157,7 @@ export function pitchPyin(
|
|
|
112
157
|
hopLength = 512,
|
|
113
158
|
fmin = 65.0,
|
|
114
159
|
fmax = 2093.0,
|
|
115
|
-
threshold = 0.
|
|
160
|
+
threshold = 0.1,
|
|
116
161
|
fillNa = false,
|
|
117
162
|
): PitchResult {
|
|
118
163
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -139,3 +184,27 @@ export function pitchPyin(
|
|
|
139
184
|
fillNa,
|
|
140
185
|
);
|
|
141
186
|
}
|
|
187
|
+
|
|
188
|
+
/** Parameters for segmenting an F0 track into stable monophonic notes. */
|
|
189
|
+
export interface NoteSegmentsRequest {
|
|
190
|
+
f0Hz: Float32Array;
|
|
191
|
+
voicedProb: Float32Array;
|
|
192
|
+
frameRate: number;
|
|
193
|
+
segmentationThresholdCents?: number;
|
|
194
|
+
minNoteMs?: number;
|
|
195
|
+
referenceHz?: number;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Segment a caller-supplied monophonic F0 track into stable note regions.
|
|
200
|
+
*
|
|
201
|
+
* `f0Hz` and `voicedProb` must have the same non-zero length. Zero-Hz frames
|
|
202
|
+
* and probabilities below 0.5 are treated as unvoiced.
|
|
203
|
+
*/
|
|
204
|
+
export function noteSegments(request: NoteSegmentsRequest): NoteSegment[] {
|
|
205
|
+
return requireModule().noteSegments(request.f0Hz, request.voicedProb, request.frameRate, {
|
|
206
|
+
segmentationThresholdCents: request.segmentationThresholdCents,
|
|
207
|
+
minNoteMs: request.minNoteMs,
|
|
208
|
+
referenceHz: request.referenceHz,
|
|
209
|
+
});
|
|
210
|
+
}
|