@libraz/libsonare 1.5.4 → 1.5.5
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/dist/index.d.ts +116 -30
- package/dist/index.js +133 -56
- package/dist/index.js.map +1 -1
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +23 -1
- package/dist/worklet.js +27 -3
- package/dist/worklet.js.map +1 -1
- package/package.json +1 -1
- package/src/_chain_config.ts +0 -20
- package/src/analysis_helpers.ts +23 -0
- package/src/audio.ts +5 -3
- package/src/effects_transform.ts +12 -6
- package/src/feature_core.ts +19 -1
- package/src/feature_music.ts +18 -14
- package/src/feature_pitch.ts +5 -5
- package/src/feature_spectral.ts +3 -3
- package/src/feature_spectrogram.ts +27 -5
- package/src/index.ts +3 -0
- package/src/mastering_core.ts +2 -0
- package/src/metering.ts +45 -4
- package/src/mixer.ts +14 -1
- package/src/project_class.ts +12 -5
- package/src/public_types_mastering.ts +6 -0
- package/src/public_types_music.ts +4 -0
- package/src/public_types_spectral.ts +10 -3
- package/src/quick_analysis.ts +23 -5
- package/src/realtime_engine.ts +9 -1
- package/src/sonare.js.d.ts +29 -3
- package/src/streaming_processors.ts +7 -1
- package/src/validation.ts +6 -0
- package/src/worklet/messages.ts +16 -2
- package/src/worklet/protocol.ts +2 -0
package/package.json
CHANGED
package/src/_chain_config.ts
CHANGED
|
@@ -22,25 +22,5 @@ export function flattenChainConfig(config: MasteringChainConfig): Record<string,
|
|
|
22
22
|
};
|
|
23
23
|
walk(config as ChainSection, '');
|
|
24
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
25
|
return out;
|
|
46
26
|
}
|
package/src/analysis_helpers.ts
CHANGED
|
@@ -16,6 +16,25 @@ import type {
|
|
|
16
16
|
WasmKeyCandidateResult,
|
|
17
17
|
} from './sonare.js';
|
|
18
18
|
|
|
19
|
+
const PITCH_CLASS_NAMES = [
|
|
20
|
+
'C',
|
|
21
|
+
'C#',
|
|
22
|
+
'D',
|
|
23
|
+
'D#',
|
|
24
|
+
'E',
|
|
25
|
+
'F',
|
|
26
|
+
'F#',
|
|
27
|
+
'G',
|
|
28
|
+
'G#',
|
|
29
|
+
'A',
|
|
30
|
+
'A#',
|
|
31
|
+
'B',
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
function pitchClassName(value: number): string {
|
|
35
|
+
return PITCH_CLASS_NAMES[value] ?? 'C';
|
|
36
|
+
}
|
|
37
|
+
|
|
19
38
|
export function convertKeyCandidate(wasm: WasmKeyCandidateResult): KeyCandidate {
|
|
20
39
|
return {
|
|
21
40
|
key: {
|
|
@@ -89,6 +108,8 @@ export function convertChordAnalysisResult(wasm: WasmChordAnalysisResult): Chord
|
|
|
89
108
|
chords: wasm.chords.map((c) => ({
|
|
90
109
|
root: c.root as PitchClass,
|
|
91
110
|
bass: c.bass as PitchClass,
|
|
111
|
+
rootName: pitchClassName(c.root),
|
|
112
|
+
bassName: pitchClassName(c.bass),
|
|
92
113
|
quality: c.quality as ChordQuality,
|
|
93
114
|
start: c.start,
|
|
94
115
|
end: c.end,
|
|
@@ -129,6 +150,8 @@ export function convertAnalysisResult(wasm: WasmAnalysisResult): AnalysisResult
|
|
|
129
150
|
chords: wasm.chords.map((c) => ({
|
|
130
151
|
root: c.root as PitchClass,
|
|
131
152
|
bass: c.bass as PitchClass,
|
|
153
|
+
rootName: pitchClassName(c.root),
|
|
154
|
+
bassName: pitchClassName(c.bass),
|
|
132
155
|
quality: c.quality as ChordQuality,
|
|
133
156
|
start: c.start,
|
|
134
157
|
end: c.end,
|
package/src/audio.ts
CHANGED
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
detectOnsets,
|
|
77
77
|
} from './quick_analysis';
|
|
78
78
|
import type { ProgressCallback, WasmNnlsChromaResult } from './sonare.js';
|
|
79
|
+
import { validateAudioBuffer } from './validation';
|
|
79
80
|
|
|
80
81
|
// ============================================================================
|
|
81
82
|
// Audio Class
|
|
@@ -177,7 +178,8 @@ export class Audio {
|
|
|
177
178
|
* Node/Python surfaces).
|
|
178
179
|
*/
|
|
179
180
|
static fromBuffer(samples: Float32Array, sampleRate = 48000): Audio {
|
|
180
|
-
|
|
181
|
+
validateAudioBuffer(samples, sampleRate);
|
|
182
|
+
return new Audio(samples.slice(), sampleRate);
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
/**
|
|
@@ -472,7 +474,7 @@ export class Audio {
|
|
|
472
474
|
hopLength = 512,
|
|
473
475
|
fmin = 65.0,
|
|
474
476
|
fmax = 2093.0,
|
|
475
|
-
threshold = 0.
|
|
477
|
+
threshold = 0.1,
|
|
476
478
|
fillNa = false,
|
|
477
479
|
): PitchResult {
|
|
478
480
|
return pitchYin(
|
|
@@ -492,7 +494,7 @@ export class Audio {
|
|
|
492
494
|
hopLength = 512,
|
|
493
495
|
fmin = 65.0,
|
|
494
496
|
fmax = 2093.0,
|
|
495
|
-
threshold = 0.
|
|
497
|
+
threshold = 0.1,
|
|
496
498
|
fillNa = false,
|
|
497
499
|
): PitchResult {
|
|
498
500
|
return pitchPyin(
|
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/feature_core.ts
CHANGED
|
@@ -81,6 +81,14 @@ export interface PcenRequest {
|
|
|
81
81
|
values: Float32Array;
|
|
82
82
|
nBins: number;
|
|
83
83
|
nFrames: number;
|
|
84
|
+
sampleRate?: number;
|
|
85
|
+
hopLength?: number;
|
|
86
|
+
timeConstant?: number;
|
|
87
|
+
gain?: number;
|
|
88
|
+
bias?: number;
|
|
89
|
+
power?: number;
|
|
90
|
+
eps?: number;
|
|
91
|
+
/** @deprecated Put PCEN fields directly on the request object. */
|
|
84
92
|
options?: Record<string, number>;
|
|
85
93
|
}
|
|
86
94
|
export interface TempogramRequest {
|
|
@@ -449,7 +457,17 @@ export function pcen(
|
|
|
449
457
|
): Float32Array {
|
|
450
458
|
if (!(values instanceof Float32Array)) {
|
|
451
459
|
const r = values;
|
|
452
|
-
|
|
460
|
+
const {
|
|
461
|
+
values: requestValues,
|
|
462
|
+
nBins: requestBins,
|
|
463
|
+
nFrames: requestFrames,
|
|
464
|
+
options: legacyOptions,
|
|
465
|
+
...flatOptions
|
|
466
|
+
} = r;
|
|
467
|
+
return pcen(requestValues, requestBins, requestFrames, {
|
|
468
|
+
...legacyOptions,
|
|
469
|
+
...flatOptions,
|
|
470
|
+
});
|
|
453
471
|
}
|
|
454
472
|
return requireModule().pcen(values, nBins, nFrames, options);
|
|
455
473
|
}
|
package/src/feature_music.ts
CHANGED
|
@@ -98,6 +98,9 @@ export interface LufsRequest extends ValidateOptions {
|
|
|
98
98
|
export interface NnlsChromaRequest extends GuardedOptions {
|
|
99
99
|
samples: Float32Array;
|
|
100
100
|
sampleRate?: number;
|
|
101
|
+
enableStftBlend?: boolean;
|
|
102
|
+
stftBlendWeight?: number;
|
|
103
|
+
stftBlendNFft?: number;
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
function validateMusicSamples(
|
|
@@ -140,18 +143,24 @@ export function nnlsChroma(request: NnlsChromaRequest): WasmNnlsChromaResult;
|
|
|
140
143
|
export function nnlsChroma(
|
|
141
144
|
samples: Float32Array,
|
|
142
145
|
sampleRate?: number,
|
|
143
|
-
options?:
|
|
146
|
+
options?: Omit<NnlsChromaRequest, 'samples' | 'sampleRate'>,
|
|
144
147
|
): WasmNnlsChromaResult;
|
|
145
148
|
export function nnlsChroma(
|
|
146
149
|
samples: Float32Array | NnlsChromaRequest,
|
|
147
150
|
sampleRate = 22050,
|
|
148
|
-
options:
|
|
151
|
+
options: Omit<NnlsChromaRequest, 'samples' | 'sampleRate'> = {},
|
|
149
152
|
): WasmNnlsChromaResult {
|
|
150
153
|
if (!(samples instanceof Float32Array)) {
|
|
151
154
|
return nnlsChroma(samples.samples, samples.sampleRate, samples);
|
|
152
155
|
}
|
|
153
156
|
validateMusicSamples('nnlsChroma', samples, sampleRate, options);
|
|
154
|
-
return requireModule().nnlsChroma(
|
|
157
|
+
return requireModule().nnlsChroma(
|
|
158
|
+
samples,
|
|
159
|
+
sampleRate,
|
|
160
|
+
options.enableStftBlend ?? true,
|
|
161
|
+
options.stftBlendWeight ?? 0.55,
|
|
162
|
+
options.stftBlendNFft ?? 4096,
|
|
163
|
+
);
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
/**
|
|
@@ -307,7 +316,8 @@ export function hybridCqt(
|
|
|
307
316
|
* @param fmin - Minimum frequency in Hz (default: 32.70319566257483, C1)
|
|
308
317
|
* @param nBins - Number of frequency bins (default: 84)
|
|
309
318
|
* @param binsPerOctave - Bins per octave (default: 12)
|
|
310
|
-
* @param gamma - Bandwidth offset;
|
|
319
|
+
* @param gamma - Bandwidth offset; negative selects the automatic ERB-derived
|
|
320
|
+
* value, while 0 is equivalent to CQT (default: -1)
|
|
311
321
|
* @returns VQT magnitude result (same shape as CQT)
|
|
312
322
|
*/
|
|
313
323
|
export function vqt(request: VqtRequest): CqtResult;
|
|
@@ -328,7 +338,7 @@ export function vqt(
|
|
|
328
338
|
fmin = 32.70319566257483,
|
|
329
339
|
nBins = 84,
|
|
330
340
|
binsPerOctave = 12,
|
|
331
|
-
gamma =
|
|
341
|
+
gamma = -1,
|
|
332
342
|
options: GuardedOptions = {},
|
|
333
343
|
): CqtResult {
|
|
334
344
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -348,9 +358,6 @@ export function vqt(
|
|
|
348
358
|
validatePositiveIntegers('vqt', { hopLength, nBins, binsPerOctave });
|
|
349
359
|
validateFrequencyBounds('vqt', fmin);
|
|
350
360
|
assertFiniteScalar('vqt', gamma, 'gamma');
|
|
351
|
-
if (gamma < 0) {
|
|
352
|
-
throw new RangeError('vqt: gamma must be non-negative');
|
|
353
|
-
}
|
|
354
361
|
return requireModule().vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);
|
|
355
362
|
}
|
|
356
363
|
|
|
@@ -465,7 +472,7 @@ export function vqtToAudio(
|
|
|
465
472
|
hopLength = 512,
|
|
466
473
|
fmin = 32.70319566257483,
|
|
467
474
|
binsPerOctave = 12,
|
|
468
|
-
gamma =
|
|
475
|
+
gamma = -1,
|
|
469
476
|
nIter = 32,
|
|
470
477
|
options: GuardedOptions = {},
|
|
471
478
|
): Float32Array {
|
|
@@ -497,9 +504,6 @@ export function vqtToAudio(
|
|
|
497
504
|
options,
|
|
498
505
|
);
|
|
499
506
|
assertFiniteScalar('vqtToAudio', gamma, 'gamma');
|
|
500
|
-
if (gamma < 0) {
|
|
501
|
-
throw new RangeError('vqtToAudio: gamma must be non-negative');
|
|
502
|
-
}
|
|
503
507
|
return requireModule().vqtToAudio(
|
|
504
508
|
magnitude,
|
|
505
509
|
nBins,
|
|
@@ -544,8 +548,8 @@ export function analyzeSections(
|
|
|
544
548
|
hopLength: options.hopLength ?? 512,
|
|
545
549
|
});
|
|
546
550
|
assertFiniteScalar('analyzeSections', options.minSectionSec ?? 4.0, 'minSectionSec');
|
|
547
|
-
if ((options.minSectionSec ?? 4.0)
|
|
548
|
-
throw new RangeError('analyzeSections: minSectionSec must be
|
|
551
|
+
if ((options.minSectionSec ?? 4.0) < 0) {
|
|
552
|
+
throw new RangeError('analyzeSections: minSectionSec must be non-negative');
|
|
549
553
|
}
|
|
550
554
|
// The embind value marshalling returns an array whose constructor is not this
|
|
551
555
|
// realm's Array; chaining .map() onto it propagates that constructor via
|
package/src/feature_pitch.ts
CHANGED
|
@@ -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 {
|
|
@@ -51,7 +51,7 @@ export function pitchYin(
|
|
|
51
51
|
hopLength = 512,
|
|
52
52
|
fmin = 65.0,
|
|
53
53
|
fmax = 2093.0,
|
|
54
|
-
threshold = 0.
|
|
54
|
+
threshold = 0.1,
|
|
55
55
|
fillNa = false,
|
|
56
56
|
): PitchResult {
|
|
57
57
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -88,7 +88,7 @@ export function pitchYin(
|
|
|
88
88
|
* @param hopLength - Hop length (default: 512)
|
|
89
89
|
* @param fmin - Minimum frequency in Hz (default: 65)
|
|
90
90
|
* @param fmax - Maximum frequency in Hz (default: 2093)
|
|
91
|
-
* @param threshold - YIN threshold (default: 0.
|
|
91
|
+
* @param threshold - YIN threshold (default: 0.1)
|
|
92
92
|
* @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)
|
|
93
93
|
* @returns Pitch detection result
|
|
94
94
|
*/
|
|
@@ -112,7 +112,7 @@ export function pitchPyin(
|
|
|
112
112
|
hopLength = 512,
|
|
113
113
|
fmin = 65.0,
|
|
114
114
|
fmax = 2093.0,
|
|
115
|
-
threshold = 0.
|
|
115
|
+
threshold = 0.1,
|
|
116
116
|
fillNa = false,
|
|
117
117
|
): PitchResult {
|
|
118
118
|
if (!(samples instanceof Float32Array)) {
|
package/src/feature_spectral.ts
CHANGED
|
@@ -438,21 +438,21 @@ export function remix(
|
|
|
438
438
|
export function phaseVocoder(request: PhaseVocoderRequest): Float32Array;
|
|
439
439
|
export function phaseVocoder(
|
|
440
440
|
samples: Float32Array,
|
|
441
|
+
sampleRate: number,
|
|
441
442
|
rate: number,
|
|
442
|
-
sampleRate?: number,
|
|
443
443
|
nFft?: number,
|
|
444
444
|
hopLength?: number,
|
|
445
445
|
): Float32Array;
|
|
446
446
|
export function phaseVocoder(
|
|
447
447
|
samples: Float32Array | PhaseVocoderRequest,
|
|
448
|
-
rate = 1,
|
|
449
448
|
sampleRate = 22050,
|
|
449
|
+
rate = 1,
|
|
450
450
|
nFft = 2048,
|
|
451
451
|
hopLength = 512,
|
|
452
452
|
): Float32Array {
|
|
453
453
|
if (!(samples instanceof Float32Array)) {
|
|
454
454
|
const r = samples;
|
|
455
|
-
return phaseVocoder(r.samples, r.
|
|
455
|
+
return phaseVocoder(r.samples, r.sampleRate ?? 22050, r.rate, r.nFft, r.hopLength);
|
|
456
456
|
}
|
|
457
457
|
return requireModule().phaseVocoder(samples, sampleRate, rate, nFft, hopLength);
|
|
458
458
|
}
|
|
@@ -34,6 +34,7 @@ export interface ChromaSpectrogramRequest extends GuardedOptions {
|
|
|
34
34
|
sampleRate?: number;
|
|
35
35
|
hopLength?: number;
|
|
36
36
|
nChroma?: number;
|
|
37
|
+
binsPerOctave?: number;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface MelSpectrogramRequest extends SpectrogramRequest {
|
|
@@ -53,6 +54,8 @@ export interface MfccToMelRequest extends GuardedOptions {
|
|
|
53
54
|
nMfcc: number;
|
|
54
55
|
nFrames: number;
|
|
55
56
|
nMels?: number;
|
|
57
|
+
/** Lifter used by the forward MFCC transform; zero means no liftering. */
|
|
58
|
+
lifter?: number;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
/** Canonical request form for reconstruction from a Mel power spectrogram. */
|
|
@@ -262,6 +265,7 @@ export function chromaCens(
|
|
|
262
265
|
sampleRate?: number,
|
|
263
266
|
hopLength?: number,
|
|
264
267
|
nChroma?: number,
|
|
268
|
+
binsPerOctave?: number,
|
|
265
269
|
options?: GuardedOptions,
|
|
266
270
|
): ChromaResult;
|
|
267
271
|
export function chromaCens(
|
|
@@ -269,6 +273,7 @@ export function chromaCens(
|
|
|
269
273
|
sampleRate = 22050,
|
|
270
274
|
hopLength = 512,
|
|
271
275
|
nChroma = 12,
|
|
276
|
+
binsPerOctave = 36,
|
|
272
277
|
options: GuardedOptions = {},
|
|
273
278
|
): ChromaResult {
|
|
274
279
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -278,12 +283,16 @@ export function chromaCens(
|
|
|
278
283
|
request.sampleRate,
|
|
279
284
|
request.hopLength,
|
|
280
285
|
request.nChroma,
|
|
286
|
+
request.binsPerOctave,
|
|
281
287
|
request,
|
|
282
288
|
);
|
|
283
289
|
}
|
|
284
290
|
validateSpectrogramSamples('chromaCens', samples, sampleRate, options);
|
|
285
|
-
validatePositiveIntegers('chromaCens', { hopLength, nChroma });
|
|
286
|
-
|
|
291
|
+
validatePositiveIntegers('chromaCens', { hopLength, nChroma, binsPerOctave });
|
|
292
|
+
if (binsPerOctave % nChroma !== 0) {
|
|
293
|
+
throw new RangeError('chromaCens: binsPerOctave must be a multiple of nChroma');
|
|
294
|
+
}
|
|
295
|
+
return requireModule().chromaCens(samples, sampleRate, hopLength, nChroma, binsPerOctave);
|
|
287
296
|
}
|
|
288
297
|
|
|
289
298
|
/**
|
|
@@ -301,6 +310,7 @@ export function chromaCqt(
|
|
|
301
310
|
sampleRate?: number,
|
|
302
311
|
hopLength?: number,
|
|
303
312
|
nChroma?: number,
|
|
313
|
+
binsPerOctave?: number,
|
|
304
314
|
options?: GuardedOptions,
|
|
305
315
|
): ChromaResult;
|
|
306
316
|
export function chromaCqt(
|
|
@@ -308,6 +318,7 @@ export function chromaCqt(
|
|
|
308
318
|
sampleRate = 22050,
|
|
309
319
|
hopLength = 512,
|
|
310
320
|
nChroma = 12,
|
|
321
|
+
binsPerOctave = 36,
|
|
311
322
|
options: GuardedOptions = {},
|
|
312
323
|
): ChromaResult {
|
|
313
324
|
if (!(samples instanceof Float32Array)) {
|
|
@@ -317,12 +328,16 @@ export function chromaCqt(
|
|
|
317
328
|
request.sampleRate,
|
|
318
329
|
request.hopLength,
|
|
319
330
|
request.nChroma,
|
|
331
|
+
request.binsPerOctave,
|
|
320
332
|
request,
|
|
321
333
|
);
|
|
322
334
|
}
|
|
323
335
|
validateSpectrogramSamples('chromaCqt', samples, sampleRate, options);
|
|
324
|
-
validatePositiveIntegers('chromaCqt', { hopLength, nChroma });
|
|
325
|
-
|
|
336
|
+
validatePositiveIntegers('chromaCqt', { hopLength, nChroma, binsPerOctave });
|
|
337
|
+
if (binsPerOctave % nChroma !== 0) {
|
|
338
|
+
throw new RangeError('chromaCqt: binsPerOctave must be a multiple of nChroma');
|
|
339
|
+
}
|
|
340
|
+
return requireModule().chromaCqt(samples, sampleRate, hopLength, nChroma, binsPerOctave);
|
|
326
341
|
}
|
|
327
342
|
|
|
328
343
|
/**
|
|
@@ -664,6 +679,7 @@ export function mfccToMel(
|
|
|
664
679
|
nMfcc: number,
|
|
665
680
|
nFrames: number,
|
|
666
681
|
nMels?: number,
|
|
682
|
+
lifter?: number,
|
|
667
683
|
options?: GuardedOptions,
|
|
668
684
|
): MelPowerResult;
|
|
669
685
|
export function mfccToMel(
|
|
@@ -671,6 +687,7 @@ export function mfccToMel(
|
|
|
671
687
|
nMfcc = 0,
|
|
672
688
|
nFrames = 0,
|
|
673
689
|
nMels = 128,
|
|
690
|
+
lifter = 0,
|
|
674
691
|
options: GuardedOptions = {},
|
|
675
692
|
): MelPowerResult {
|
|
676
693
|
if (!(mfccCoefficients instanceof Float32Array)) {
|
|
@@ -680,6 +697,7 @@ export function mfccToMel(
|
|
|
680
697
|
request.nMfcc,
|
|
681
698
|
request.nFrames,
|
|
682
699
|
request.nMels,
|
|
700
|
+
request.lifter,
|
|
683
701
|
request,
|
|
684
702
|
);
|
|
685
703
|
}
|
|
@@ -693,7 +711,7 @@ export function mfccToMel(
|
|
|
693
711
|
options,
|
|
694
712
|
);
|
|
695
713
|
validatePositiveIntegers('mfccToMel', { nMels });
|
|
696
|
-
return requireModule().mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels);
|
|
714
|
+
return requireModule().mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels, lifter);
|
|
697
715
|
}
|
|
698
716
|
|
|
699
717
|
/**
|
|
@@ -726,6 +744,7 @@ export function mfccToAudio(
|
|
|
726
744
|
fmax?: number,
|
|
727
745
|
nIter?: number,
|
|
728
746
|
htk?: boolean,
|
|
747
|
+
lifter?: number,
|
|
729
748
|
options?: GuardedOptions,
|
|
730
749
|
): Float32Array;
|
|
731
750
|
export function mfccToAudio(
|
|
@@ -740,6 +759,7 @@ export function mfccToAudio(
|
|
|
740
759
|
fmax = 0,
|
|
741
760
|
nIter = 32,
|
|
742
761
|
htk = false,
|
|
762
|
+
lifter = 0,
|
|
743
763
|
options: GuardedOptions = {},
|
|
744
764
|
): Float32Array {
|
|
745
765
|
if (!(mfccCoefficients instanceof Float32Array)) {
|
|
@@ -756,6 +776,7 @@ export function mfccToAudio(
|
|
|
756
776
|
request.fmax,
|
|
757
777
|
request.nIter,
|
|
758
778
|
request.htk,
|
|
779
|
+
request.lifter,
|
|
759
780
|
request,
|
|
760
781
|
);
|
|
761
782
|
}
|
|
@@ -783,6 +804,7 @@ export function mfccToAudio(
|
|
|
783
804
|
fmax,
|
|
784
805
|
nIter,
|
|
785
806
|
htk,
|
|
807
|
+
lifter,
|
|
786
808
|
);
|
|
787
809
|
}
|
|
788
810
|
|
package/src/index.ts
CHANGED
|
@@ -312,6 +312,7 @@ export type {
|
|
|
312
312
|
MeteringDynamicRangeOptions,
|
|
313
313
|
MeteringDynamicRangeRequest,
|
|
314
314
|
MeteringSamplesRequest,
|
|
315
|
+
MeteringSilenceRatioRequest,
|
|
315
316
|
MeteringSpectrumFrameRequest,
|
|
316
317
|
MeteringSpectrumRequest,
|
|
317
318
|
MeteringStereoDecimatedRequest,
|
|
@@ -336,6 +337,7 @@ export {
|
|
|
336
337
|
meteringPhaseScope,
|
|
337
338
|
meteringPhaseScopeDecimated,
|
|
338
339
|
meteringRmsDb,
|
|
340
|
+
meteringSilenceRatio,
|
|
339
341
|
meteringSpectrum,
|
|
340
342
|
meteringSpectrumFrame,
|
|
341
343
|
meteringStereoCorrelation,
|
|
@@ -522,6 +524,7 @@ export type {
|
|
|
522
524
|
DetectKeyRequest,
|
|
523
525
|
DynamicsAnalysisResult,
|
|
524
526
|
EstimateRoomRequest,
|
|
527
|
+
MusicAnalyzeRequest,
|
|
525
528
|
RhythmAnalysisResult,
|
|
526
529
|
RoomMorphRequest,
|
|
527
530
|
SamplesRequest,
|
package/src/mastering_core.ts
CHANGED
package/src/metering.ts
CHANGED
|
@@ -128,6 +128,43 @@ export function meteringRmsDb(
|
|
|
128
128
|
return requireModule().meteringRmsDb(request.samples, request.sampleRate ?? 22050);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
export interface MeteringSilenceRatioRequest extends MeteringSamplesRequest {
|
|
132
|
+
thresholdDb?: number;
|
|
133
|
+
frameLength?: number;
|
|
134
|
+
hopLength?: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function meteringSilenceRatio(request: MeteringSilenceRatioRequest): number;
|
|
138
|
+
export function meteringSilenceRatio(
|
|
139
|
+
samples: Float32Array,
|
|
140
|
+
sampleRate?: number,
|
|
141
|
+
thresholdDb?: number,
|
|
142
|
+
frameLength?: number,
|
|
143
|
+
hopLength?: number,
|
|
144
|
+
options?: ValidateOptions,
|
|
145
|
+
): number;
|
|
146
|
+
export function meteringSilenceRatio(
|
|
147
|
+
samples: Float32Array | MeteringSilenceRatioRequest,
|
|
148
|
+
sampleRate = 22050,
|
|
149
|
+
thresholdDb = -45,
|
|
150
|
+
frameLength = 1024,
|
|
151
|
+
hopLength = 256,
|
|
152
|
+
options: ValidateOptions = {},
|
|
153
|
+
): number {
|
|
154
|
+
const request =
|
|
155
|
+
samples instanceof Float32Array
|
|
156
|
+
? { samples, sampleRate, thresholdDb, frameLength, hopLength, ...options }
|
|
157
|
+
: samples;
|
|
158
|
+
assertSamples('meteringSilenceRatio', request.samples, request.validate !== false);
|
|
159
|
+
return requireModule().meteringSilenceRatio(
|
|
160
|
+
request.samples,
|
|
161
|
+
request.sampleRate ?? 22050,
|
|
162
|
+
request.thresholdDb ?? -45,
|
|
163
|
+
request.frameLength ?? 1024,
|
|
164
|
+
request.hopLength ?? 256,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
131
168
|
export function meteringCrestFactorDb(request: MeteringSamplesRequest): number;
|
|
132
169
|
export function meteringCrestFactorDb(
|
|
133
170
|
samples: Float32Array,
|
|
@@ -206,16 +243,20 @@ export function meteringDetectClipping(
|
|
|
206
243
|
): ClippingReport {
|
|
207
244
|
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
208
245
|
assertSamples('meteringDetectClipping', request.samples, request.validate !== false);
|
|
246
|
+
const minRegionSamples = request.minRegionSamples ?? 1;
|
|
247
|
+
if (!Number.isInteger(minRegionSamples) || minRegionSamples < 0) {
|
|
248
|
+
throw new RangeError('meteringDetectClipping: minRegionSamples must be a non-negative integer');
|
|
249
|
+
}
|
|
209
250
|
return requireModule().meteringDetectClipping(
|
|
210
251
|
request.samples,
|
|
211
252
|
request.sampleRate ?? 22050,
|
|
212
253
|
request.threshold ?? 0.999,
|
|
213
|
-
|
|
254
|
+
minRegionSamples,
|
|
214
255
|
);
|
|
215
256
|
}
|
|
216
257
|
|
|
217
258
|
/**
|
|
218
|
-
* Sliding-window dynamic range. Pass 0 for window/hop to use the library
|
|
259
|
+
* Sliding-window dynamic range for mono audio. Pass 0 for window/hop to use the library
|
|
219
260
|
* default (window=3 s, hop=1 s). The percentiles use a NEGATIVE sentinel for
|
|
220
261
|
* "use the library default" (low=0.10, high=0.95) because 0 is a literal 0th
|
|
221
262
|
* percentile; omitted percentiles therefore default to -1.
|
|
@@ -354,7 +395,7 @@ export interface WaveformPeaksReport {
|
|
|
354
395
|
samplesPerBucket: number;
|
|
355
396
|
}
|
|
356
397
|
|
|
357
|
-
/**
|
|
398
|
+
/** Uncentered correlation (cosine similarity) in [-1, 1] between equal-length channels. */
|
|
358
399
|
export function meteringStereoCorrelation(request: MeteringStereoRequest): number;
|
|
359
400
|
export function meteringStereoCorrelation(
|
|
360
401
|
left: Float32Array,
|
|
@@ -561,7 +602,7 @@ export function meteringPhaseScopeDecimated(
|
|
|
561
602
|
}
|
|
562
603
|
|
|
563
604
|
/**
|
|
564
|
-
* Welch-averaged magnitude / power / dB spectrum over the WHOLE signal (split
|
|
605
|
+
* Welch-averaged magnitude / power / dB spectrum over the WHOLE mono signal (split
|
|
565
606
|
* into Hann-windowed, 50%-overlapping `nFft`-length frames whose power spectra
|
|
566
607
|
* are averaged). For a true single-frame snapshot, use
|
|
567
608
|
* {@link meteringSpectrumFrame}.
|
package/src/mixer.ts
CHANGED
|
@@ -74,7 +74,10 @@ export class Mixer {
|
|
|
74
74
|
return new Mixer(module.createMixerFromSceneJson(json, sampleRate, blockSize), blockSize);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Rebuild and compile the routing graph without resetting its absolute
|
|
79
|
+
* automation sample position or queued strip automation.
|
|
80
|
+
*/
|
|
78
81
|
compile(): void {
|
|
79
82
|
this.mixer.compile();
|
|
80
83
|
}
|
|
@@ -261,6 +264,11 @@ export class Mixer {
|
|
|
261
264
|
this.mixer.setVcaGroupGainDb(id, gainDb);
|
|
262
265
|
}
|
|
263
266
|
|
|
267
|
+
/** Replace an existing VCA group's strip membership. */
|
|
268
|
+
setVcaGroupMembers(id: string, members: string[]): void {
|
|
269
|
+
this.mixer.setVcaGroupMembers(id, members);
|
|
270
|
+
}
|
|
271
|
+
|
|
264
272
|
/** Remove a VCA group by id. */
|
|
265
273
|
removeVcaGroup(id: string): void {
|
|
266
274
|
this.mixer.removeVcaGroup(id);
|
|
@@ -427,6 +435,11 @@ export class Mixer {
|
|
|
427
435
|
return this.mixer.meterTap(stripIndex, meterTapCode(tap));
|
|
428
436
|
}
|
|
429
437
|
|
|
438
|
+
/** Read the post-insert meter for a compiled bus, including master. */
|
|
439
|
+
busMeter(busId: string): MixMeterSnapshot {
|
|
440
|
+
return this.mixer.busMeter(busId);
|
|
441
|
+
}
|
|
442
|
+
|
|
430
443
|
/**
|
|
431
444
|
* Schedule sample-accurate fader automation on a strip.
|
|
432
445
|
*
|