@libraz/libsonare 1.4.1 → 1.5.1
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 +51 -20
- package/dist/index.d.ts +5416 -1
- package/dist/index.js +938 -583
- package/dist/index.js.map +1 -1
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +1083 -5227
- package/dist/worklet.js +2683 -2451
- package/dist/worklet.js.map +1 -1
- package/package.json +4 -9
- package/src/clip_page_streamer.ts +298 -0
- package/src/effects_mastering.ts +85 -1089
- package/src/effects_transform.ts +286 -0
- package/src/effects_voice_change.ts +118 -0
- package/src/feature_music.ts +5 -2
- package/src/feature_spectrogram.ts +42 -2
- package/src/features.ts +1 -0
- package/src/index.ts +23 -0
- package/src/mastering_chain.ts +200 -0
- package/src/mastering_core.ts +248 -0
- package/src/mastering_dynamics.ts +105 -0
- package/src/mastering_repair.ts +161 -0
- package/src/mixer.ts +8 -0
- package/src/mixing_oneshot.ts +54 -0
- package/src/module_state.ts +1 -2
- package/src/project.ts +71 -1712
- package/src/project_class.ts +871 -0
- package/src/project_internal.ts +333 -0
- package/src/project_synth.ts +43 -0
- package/src/project_types.ts +570 -0
- package/src/public_types.ts +6 -1221
- package/src/public_types_acoustic.ts +115 -0
- package/src/public_types_mastering.ts +333 -0
- package/src/public_types_mixing.ts +97 -0
- package/src/public_types_music.ts +352 -0
- package/src/public_types_realtime.ts +163 -0
- package/src/public_types_spectral.ts +194 -0
- package/src/realtime_engine.ts +94 -0
- package/src/sonare.js.d.ts +117 -38
- package/src/stream_analyzer.ts +3 -0
- package/src/stream_types.ts +4 -0
- package/src/worklet/engine-automation.ts +73 -0
- package/src/worklet/engine-capture-facade.ts +80 -0
- package/src/worklet/engine-clips.ts +71 -0
- package/src/worklet/engine-markers.ts +93 -0
- package/src/worklet/engine-mixer-facade.ts +186 -0
- package/src/worklet/engine-node.ts +451 -0
- package/src/worklet/engine-offline.ts +162 -0
- package/src/worklet/engine-options.ts +13 -0
- package/src/worklet/engine-parameter-facade.ts +172 -0
- package/src/worklet/engine-processor.ts +764 -0
- package/src/worklet/engine-register.ts +136 -0
- package/src/worklet/engine-strips.ts +315 -0
- package/src/worklet/engine-sync.ts +94 -0
- package/src/worklet/engine-tempo-facade.ts +141 -0
- package/src/worklet/engine.ts +998 -0
- package/src/worklet/guards.ts +14 -1
- package/src/worklet/messages.ts +60 -20
- package/src/worklet/mixer-processor.ts +368 -0
- package/src/worklet/protocol.ts +3 -0
- package/src/worklet/voice-changer-processor.ts +246 -0
- package/src/worklet.ts +20 -3549
- package/dist/sonare-rt-module.js +0 -2
- package/dist/sonare-rt.js +0 -2
- package/dist/sonare-rt.wasm +0 -0
- package/src/sonare-rt.d.ts +0 -93
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { getSonareModule } from './module_state';
|
|
2
|
+
import type {
|
|
3
|
+
HpssResult,
|
|
4
|
+
NoteStretchOptions,
|
|
5
|
+
PitchCorrectOptions,
|
|
6
|
+
SpectralEditOptions,
|
|
7
|
+
SpectralRegionOp,
|
|
8
|
+
} from './public_types';
|
|
9
|
+
import type { ValidateOptions } from './validation';
|
|
10
|
+
import { assertSampleRate, assertSamples } from './validation';
|
|
11
|
+
|
|
12
|
+
function requireModule() {
|
|
13
|
+
return getSonareModule();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Effects
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Perform Harmonic-Percussive Source Separation (HPSS).
|
|
22
|
+
*
|
|
23
|
+
* @param samples - Audio samples (mono, float32)
|
|
24
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
25
|
+
* @param kernelHarmonic - Horizontal median filter size for harmonic (default: 31)
|
|
26
|
+
* @param kernelPercussive - Vertical median filter size for percussive (default: 31)
|
|
27
|
+
* @returns Separated harmonic and percussive components
|
|
28
|
+
*/
|
|
29
|
+
export function hpss(
|
|
30
|
+
samples: Float32Array,
|
|
31
|
+
sampleRate = 22050,
|
|
32
|
+
kernelHarmonic = 31,
|
|
33
|
+
kernelPercussive = 31,
|
|
34
|
+
): HpssResult {
|
|
35
|
+
return requireModule().hpss(samples, sampleRate, kernelHarmonic, kernelPercussive);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extract harmonic component from audio.
|
|
40
|
+
*
|
|
41
|
+
* @param samples - Audio samples (mono, float32)
|
|
42
|
+
* @param sampleRate - Sample rate in Hz
|
|
43
|
+
* @returns Harmonic component
|
|
44
|
+
*/
|
|
45
|
+
export function harmonic(
|
|
46
|
+
samples: Float32Array,
|
|
47
|
+
sampleRate: number,
|
|
48
|
+
options: ValidateOptions = {},
|
|
49
|
+
): Float32Array {
|
|
50
|
+
assertSamples('harmonic', samples, options.validate !== false);
|
|
51
|
+
return requireModule().harmonic(samples, sampleRate);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extract percussive component from audio.
|
|
56
|
+
*
|
|
57
|
+
* @param samples - Audio samples (mono, float32)
|
|
58
|
+
* @param sampleRate - Sample rate in Hz
|
|
59
|
+
* @returns Percussive component
|
|
60
|
+
*/
|
|
61
|
+
export function percussive(
|
|
62
|
+
samples: Float32Array,
|
|
63
|
+
sampleRate: number,
|
|
64
|
+
options: ValidateOptions = {},
|
|
65
|
+
): Float32Array {
|
|
66
|
+
assertSamples('percussive', samples, options.validate !== false);
|
|
67
|
+
return requireModule().percussive(samples, sampleRate);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Time-stretch audio without changing pitch.
|
|
72
|
+
*
|
|
73
|
+
* @param samples - Audio samples (mono, float32)
|
|
74
|
+
* @param sampleRate - Sample rate in Hz
|
|
75
|
+
* @param rate - Time stretch rate (0.5 = double duration, 2.0 = half duration)
|
|
76
|
+
* @returns Time-stretched audio
|
|
77
|
+
*/
|
|
78
|
+
export function timeStretch(
|
|
79
|
+
samples: Float32Array,
|
|
80
|
+
sampleRate: number,
|
|
81
|
+
rate: number,
|
|
82
|
+
options: ValidateOptions = {},
|
|
83
|
+
): Float32Array {
|
|
84
|
+
assertSamples('timeStretch', samples, options.validate !== false);
|
|
85
|
+
return requireModule().timeStretch(samples, sampleRate, rate);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Pitch-shift audio without changing duration.
|
|
90
|
+
*
|
|
91
|
+
* @param samples - Audio samples (mono, float32)
|
|
92
|
+
* @param sampleRate - Sample rate in Hz
|
|
93
|
+
* @param semitones - Pitch shift in semitones (+12 = one octave up, -12 = one octave down)
|
|
94
|
+
* @returns Pitch-shifted audio
|
|
95
|
+
*/
|
|
96
|
+
export function pitchShift(
|
|
97
|
+
samples: Float32Array,
|
|
98
|
+
sampleRate: number,
|
|
99
|
+
semitones: number,
|
|
100
|
+
options: ValidateOptions = {},
|
|
101
|
+
): Float32Array {
|
|
102
|
+
assertSamples('pitchShift', samples, options.validate !== false);
|
|
103
|
+
return requireModule().pitchShift(samples, sampleRate, semitones);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Pitch-correct audio from a current MIDI note to a target MIDI note.
|
|
108
|
+
*
|
|
109
|
+
* @param samples - Audio samples (mono, float32)
|
|
110
|
+
* @param sampleRate - Sample rate in Hz
|
|
111
|
+
* @param currentMidi - Detected/current MIDI note number
|
|
112
|
+
* @param targetMidi - Desired MIDI note number
|
|
113
|
+
* @returns Pitch-corrected audio
|
|
114
|
+
*/
|
|
115
|
+
export function pitchCorrectToMidi(
|
|
116
|
+
samples: Float32Array,
|
|
117
|
+
sampleRate = 22050,
|
|
118
|
+
currentMidi = 69.0,
|
|
119
|
+
targetMidi = 69.0,
|
|
120
|
+
options: ValidateOptions = {},
|
|
121
|
+
): Float32Array {
|
|
122
|
+
assertSamples('pitchCorrectToMidi', samples, options.validate !== false);
|
|
123
|
+
return requireModule().pitchCorrectToMidi(samples, sampleRate, currentMidi, targetMidi);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Contour-following ("time-varying") pitch correction toward a MIDI target.
|
|
128
|
+
*
|
|
129
|
+
* Unlike {@link pitchCorrectToMidi} (a single constant transpose), this follows
|
|
130
|
+
* the caller-supplied per-frame `f0Hz` contour and retunes every voiced frame
|
|
131
|
+
* toward `targetMidi`, so vibrato/drift in the source is tracked rather than
|
|
132
|
+
* flattened. `voiced` (non-zero = voiced) and `voicedProb` ([0,1]) are optional;
|
|
133
|
+
* omitting them treats every frame as voiced.
|
|
134
|
+
*
|
|
135
|
+
* @param samples - Audio samples (mono, float32)
|
|
136
|
+
* @param f0Hz - Per-frame measured F0 in Hz (one entry per analysis frame)
|
|
137
|
+
* @param targetMidi - Desired MIDI note number
|
|
138
|
+
* @param sampleRate - Sample rate in Hz
|
|
139
|
+
* @param hopLength - F0 hop in samples (frame i covers sample i*hopLength)
|
|
140
|
+
* @param voiced - Optional per-frame voiced flags (non-zero = voiced)
|
|
141
|
+
* @param voicedProb - Optional per-frame voicing probability in [0, 1]
|
|
142
|
+
* @returns Pitch-corrected audio
|
|
143
|
+
*/
|
|
144
|
+
export function pitchCorrectToMidiTimevarying(
|
|
145
|
+
samples: Float32Array,
|
|
146
|
+
f0Hz: Float32Array,
|
|
147
|
+
targetMidi: number,
|
|
148
|
+
sampleRate = 22050,
|
|
149
|
+
hopLength = 512,
|
|
150
|
+
voiced?: Int32Array,
|
|
151
|
+
voicedProb?: Float32Array,
|
|
152
|
+
options: ValidateOptions = {},
|
|
153
|
+
): Float32Array {
|
|
154
|
+
assertSamples('pitchCorrectToMidiTimevarying', samples, options.validate !== false);
|
|
155
|
+
if (voiced && voiced.length !== f0Hz.length) {
|
|
156
|
+
throw new RangeError('pitchCorrectToMidiTimevarying: voiced length must match f0Hz length');
|
|
157
|
+
}
|
|
158
|
+
if (voicedProb && voicedProb.length !== f0Hz.length) {
|
|
159
|
+
throw new RangeError('pitchCorrectToMidiTimevarying: voicedProb length must match f0Hz length');
|
|
160
|
+
}
|
|
161
|
+
// The embind layer reads the companion arrays as Float32Array (voiced uses
|
|
162
|
+
// 0.0/1.0); convert here so a single native conversion path suffices.
|
|
163
|
+
const voicedF32 = voiced ? Float32Array.from(voiced) : undefined;
|
|
164
|
+
return requireModule().pitchCorrectToMidiTimevarying(
|
|
165
|
+
samples,
|
|
166
|
+
sampleRate,
|
|
167
|
+
f0Hz,
|
|
168
|
+
targetMidi,
|
|
169
|
+
hopLength,
|
|
170
|
+
voicedF32,
|
|
171
|
+
voicedProb,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Contour-following pitch correction toward a fixed MIDI note OR a musical
|
|
177
|
+
* scale, with tunable retune strength and vibrato preservation.
|
|
178
|
+
*
|
|
179
|
+
* Generalises {@link pitchCorrectToMidiTimevarying}: the same caller-supplied
|
|
180
|
+
* per-frame `f0Hz` contour drives correction, but `options.mode` selects between
|
|
181
|
+
* a fixed-MIDI target (`'midi'`, default) and scale quantisation (`'scale'`),
|
|
182
|
+
* and the retune knobs shape natural-vs-robotic correction.
|
|
183
|
+
*
|
|
184
|
+
* @param samples - Audio samples (mono, float32)
|
|
185
|
+
* @param f0Hz - Per-frame measured F0 in Hz (one entry per analysis frame)
|
|
186
|
+
* @param sampleRate - Sample rate in Hz
|
|
187
|
+
* @param hopLength - F0 hop in samples (frame i covers sample i*hopLength)
|
|
188
|
+
* @param options - Target mode + retune knobs + optional voiced/voicedProb arrays
|
|
189
|
+
* @returns Pitch-corrected audio
|
|
190
|
+
*/
|
|
191
|
+
export function pitchCorrectTimevarying(
|
|
192
|
+
samples: Float32Array,
|
|
193
|
+
f0Hz: Float32Array,
|
|
194
|
+
sampleRate = 22050,
|
|
195
|
+
hopLength = 512,
|
|
196
|
+
options: PitchCorrectOptions = {},
|
|
197
|
+
): Float32Array {
|
|
198
|
+
assertSamples('pitchCorrectTimevarying', samples, options.validate !== false);
|
|
199
|
+
if (options.voiced && options.voiced.length !== f0Hz.length) {
|
|
200
|
+
throw new RangeError('pitchCorrectTimevarying: voiced length must match f0Hz length');
|
|
201
|
+
}
|
|
202
|
+
if (options.voicedProb && options.voicedProb.length !== f0Hz.length) {
|
|
203
|
+
throw new RangeError('pitchCorrectTimevarying: voicedProb length must match f0Hz length');
|
|
204
|
+
}
|
|
205
|
+
// The embind layer reads the companion arrays as Float32Array (voiced uses
|
|
206
|
+
// 0.0/1.0); convert here so a single native conversion path suffices.
|
|
207
|
+
const nativeOptions = {
|
|
208
|
+
...options,
|
|
209
|
+
voiced: options.voiced ? Float32Array.from(options.voiced) : undefined,
|
|
210
|
+
};
|
|
211
|
+
return requireModule().pitchCorrectTimevarying(
|
|
212
|
+
samples,
|
|
213
|
+
sampleRate,
|
|
214
|
+
f0Hz,
|
|
215
|
+
hopLength,
|
|
216
|
+
nativeOptions,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Time-stretch a note region between two sample offsets without changing pitch.
|
|
222
|
+
*
|
|
223
|
+
* @param samples - Audio samples (mono, float32)
|
|
224
|
+
* @param sampleRate - Sample rate in Hz
|
|
225
|
+
* @param onsetSample - Note onset position in samples
|
|
226
|
+
* @param offsetSample - Note offset position in samples
|
|
227
|
+
* @param stretchRatio - Stretch ratio (0.5 = double duration, 2.0 = half duration)
|
|
228
|
+
* @returns Audio with the note region stretched
|
|
229
|
+
*/
|
|
230
|
+
export function noteStretch(
|
|
231
|
+
samples: Float32Array,
|
|
232
|
+
sampleRate = 22050,
|
|
233
|
+
options: NoteStretchOptions & ValidateOptions = {},
|
|
234
|
+
): Float32Array {
|
|
235
|
+
assertSamples('noteStretch', samples, options.validate !== false);
|
|
236
|
+
return requireModule().noteStretch(
|
|
237
|
+
samples,
|
|
238
|
+
sampleRate,
|
|
239
|
+
options.onsetSample ?? 0,
|
|
240
|
+
options.offsetSample ?? 0,
|
|
241
|
+
options.stretchRatio ?? 1.0,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Normalize audio to target peak level.
|
|
247
|
+
*
|
|
248
|
+
* @param samples - Audio samples (mono, float32)
|
|
249
|
+
* @param sampleRate - Sample rate in Hz
|
|
250
|
+
* @param targetDb - Target peak level in dB (default: 0 dB = full scale)
|
|
251
|
+
* @returns Normalized audio
|
|
252
|
+
*/
|
|
253
|
+
export function normalize(
|
|
254
|
+
samples: Float32Array,
|
|
255
|
+
sampleRate: number,
|
|
256
|
+
targetDb = 0.0,
|
|
257
|
+
options: ValidateOptions = {},
|
|
258
|
+
): Float32Array {
|
|
259
|
+
assertSamples('normalize', samples, options.validate !== false);
|
|
260
|
+
return requireModule().normalize(samples, sampleRate, targetDb);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Apply region-based spectral edits (gain/attenuate/mute/heal) to mono audio.
|
|
265
|
+
*
|
|
266
|
+
* Each op is a time x frequency rectangle applied in array order over a single
|
|
267
|
+
* STFT buffer, so a later op observes the result of earlier ops. The output has
|
|
268
|
+
* the same length and sample rate as the input; an empty `ops` list is an
|
|
269
|
+
* identity transform (within the iSTFT's own tolerance).
|
|
270
|
+
*
|
|
271
|
+
* @param samples - Audio samples (mono, float32)
|
|
272
|
+
* @param sampleRate - Sample rate in Hz
|
|
273
|
+
* @param ops - Region edit ops applied in order ({@link SpectralRegionOp})
|
|
274
|
+
* @param options - STFT + heal configuration ({@link SpectralEditOptions})
|
|
275
|
+
* @returns Edited audio
|
|
276
|
+
*/
|
|
277
|
+
export function spectralEdit(
|
|
278
|
+
samples: Float32Array,
|
|
279
|
+
sampleRate: number,
|
|
280
|
+
ops: SpectralRegionOp[] = [],
|
|
281
|
+
options: SpectralEditOptions & ValidateOptions = {},
|
|
282
|
+
): Float32Array {
|
|
283
|
+
assertSamples('spectralEdit', samples, options.validate !== false);
|
|
284
|
+
assertSampleRate('spectralEdit', sampleRate);
|
|
285
|
+
return requireModule().spectralEdit(samples, sampleRate, ops, options as Record<string, unknown>);
|
|
286
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { getSonareModule } from './module_state';
|
|
2
|
+
import type { RealtimeVoiceChangerConfigInput } from './public_types';
|
|
3
|
+
import { RealtimeVoiceChanger } from './streaming_mixing';
|
|
4
|
+
import type { ValidateOptions } from './validation';
|
|
5
|
+
import { assertSamples } from './validation';
|
|
6
|
+
|
|
7
|
+
function requireModule() {
|
|
8
|
+
return getSonareModule();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Options for {@link voiceChange}. All fields are optional. */
|
|
12
|
+
export interface VoiceChangeOptions extends ValidateOptions {
|
|
13
|
+
/** Pitch shift in semitones (negative = down). Default 0. */
|
|
14
|
+
pitchSemitones?: number;
|
|
15
|
+
/** Formant scale factor (>1 brightens, <1 darkens). Default 1. */
|
|
16
|
+
formantFactor?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Apply a voice change by shifting pitch and formants independently.
|
|
21
|
+
*
|
|
22
|
+
* @param samples - Audio samples (mono, float32)
|
|
23
|
+
* @param sampleRate - Sample rate in Hz
|
|
24
|
+
* @param options - Pitch/formant settings ({@link VoiceChangeOptions})
|
|
25
|
+
* @returns Voice-changed audio
|
|
26
|
+
*/
|
|
27
|
+
export function voiceChange(
|
|
28
|
+
samples: Float32Array,
|
|
29
|
+
sampleRate = 22050,
|
|
30
|
+
options: VoiceChangeOptions = {},
|
|
31
|
+
): Float32Array {
|
|
32
|
+
assertSamples('voiceChange', samples, options.validate !== false);
|
|
33
|
+
return requireModule().voiceChange(
|
|
34
|
+
samples,
|
|
35
|
+
sampleRate,
|
|
36
|
+
options.pitchSemitones ?? 0.0,
|
|
37
|
+
options.formantFactor ?? 1.0,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Options for the offline {@link voiceChangeRealtime} convenience wrapper. */
|
|
42
|
+
export interface VoiceChangeRealtimeOptions extends ValidateOptions {
|
|
43
|
+
/** Channel count (1 = mono, 2 = interleaved stereo). */
|
|
44
|
+
channels?: 1 | 2;
|
|
45
|
+
/** Block size for the internal render loop (default 512). */
|
|
46
|
+
blockSize?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function latencyCompensatedVoiceChange(
|
|
50
|
+
changer: RealtimeVoiceChanger,
|
|
51
|
+
samples: Float32Array,
|
|
52
|
+
channels: 1 | 2,
|
|
53
|
+
blockFrames: number,
|
|
54
|
+
): Float32Array {
|
|
55
|
+
const latencyFrames = Math.max(0, changer.latencySamples());
|
|
56
|
+
if (channels === 1) {
|
|
57
|
+
const total = samples.length + latencyFrames;
|
|
58
|
+
const input = new Float32Array(total);
|
|
59
|
+
input.set(samples);
|
|
60
|
+
const processed = new Float32Array(total);
|
|
61
|
+
for (let offset = 0; offset < total; offset += blockFrames) {
|
|
62
|
+
const block = input.subarray(offset, Math.min(offset + blockFrames, total));
|
|
63
|
+
processed.set(changer.processMono(block), offset);
|
|
64
|
+
}
|
|
65
|
+
return processed.slice(latencyFrames, latencyFrames + samples.length);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const frames = samples.length / 2;
|
|
69
|
+
const totalFrames = frames + latencyFrames;
|
|
70
|
+
const input = new Float32Array(totalFrames * 2);
|
|
71
|
+
input.set(samples);
|
|
72
|
+
const processed = new Float32Array(totalFrames * 2);
|
|
73
|
+
const frameStride = blockFrames * 2;
|
|
74
|
+
for (let offset = 0; offset < input.length; offset += frameStride) {
|
|
75
|
+
const block = input.subarray(offset, Math.min(offset + frameStride, input.length));
|
|
76
|
+
processed.set(changer.processInterleaved(block, 2), offset);
|
|
77
|
+
}
|
|
78
|
+
const start = latencyFrames * 2;
|
|
79
|
+
return processed.slice(start, start + samples.length);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Applies the realtime voice-changer chain to a whole buffer in one call.
|
|
84
|
+
*
|
|
85
|
+
* Constructs and prepares a {@link RealtimeVoiceChanger}, runs the block loop
|
|
86
|
+
* for the caller, then disposes it — matching the Python `voice_change_realtime`
|
|
87
|
+
* and Node `voiceChangeRealtime` convenience wrappers. For mono, `samples` is a
|
|
88
|
+
* plain mono buffer; for stereo, `samples` is interleaved (L0,R0,L1,R1,...).
|
|
89
|
+
*
|
|
90
|
+
* @param samples - Audio samples (mono, or interleaved stereo when channels=2)
|
|
91
|
+
* @param sampleRate - Sample rate in Hz (default 48000, matching Python/Node)
|
|
92
|
+
* @param preset - Voice-changer preset id or full config object
|
|
93
|
+
* @param options - Channel count and block size ({@link VoiceChangeRealtimeOptions})
|
|
94
|
+
* @returns The processed buffer (same layout/length as the input).
|
|
95
|
+
*/
|
|
96
|
+
export function voiceChangeRealtime(
|
|
97
|
+
samples: Float32Array,
|
|
98
|
+
sampleRate = 48000,
|
|
99
|
+
preset: RealtimeVoiceChangerConfigInput = 'neutral-monitor',
|
|
100
|
+
options: VoiceChangeRealtimeOptions = {},
|
|
101
|
+
): Float32Array {
|
|
102
|
+
assertSamples('voiceChangeRealtime', samples, options.validate !== false);
|
|
103
|
+
const channels = options.channels ?? 1;
|
|
104
|
+
if (channels !== 1 && channels !== 2) {
|
|
105
|
+
throw new Error('voiceChangeRealtime: channels must be 1 or 2.');
|
|
106
|
+
}
|
|
107
|
+
if (channels === 2 && samples.length % 2 !== 0) {
|
|
108
|
+
throw new Error('voiceChangeRealtime: stereo input length must be a multiple of 2.');
|
|
109
|
+
}
|
|
110
|
+
const blockSize = Math.max(1, Math.floor(options.blockSize ?? 512));
|
|
111
|
+
const changer = new RealtimeVoiceChanger(preset);
|
|
112
|
+
try {
|
|
113
|
+
changer.prepare(sampleRate, blockSize, channels);
|
|
114
|
+
return latencyCompensatedVoiceChange(changer, samples, channels, blockSize);
|
|
115
|
+
} finally {
|
|
116
|
+
changer.delete();
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/feature_music.ts
CHANGED
|
@@ -366,19 +366,22 @@ export function fourierTempogram(
|
|
|
366
366
|
* @param winLength - Window length in frames (default: 384)
|
|
367
367
|
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
368
368
|
* @param hopLength - Hop length (default: 512)
|
|
369
|
-
* @
|
|
369
|
+
* @param factors - Lag ratios to evaluate. When omitted or empty, the library
|
|
370
|
+
* default {0.5, 1, 2, 3, 4} is used.
|
|
371
|
+
* @returns Tempogram ratio features (one value per factor)
|
|
370
372
|
*/
|
|
371
373
|
export function tempogramRatio(
|
|
372
374
|
tempogramData: Float32Array,
|
|
373
375
|
winLength = 384,
|
|
374
376
|
sampleRate = 22050,
|
|
375
377
|
hopLength = 512,
|
|
378
|
+
factors?: Float32Array | number[],
|
|
376
379
|
options: GuardedOptions = {},
|
|
377
380
|
): Float32Array {
|
|
378
381
|
assertSampleRate('tempogramRatio', sampleRate);
|
|
379
382
|
assertSamples('tempogramRatio', tempogramData, options.validate !== false, 'tempogramData');
|
|
380
383
|
validatePositiveIntegers('tempogramRatio', { winLength, hopLength });
|
|
381
|
-
return requireModule().tempogramRatio(tempogramData, winLength, sampleRate, hopLength);
|
|
384
|
+
return requireModule().tempogramRatio(tempogramData, winLength, sampleRate, hopLength, factors);
|
|
382
385
|
}
|
|
383
386
|
|
|
384
387
|
/**
|
|
@@ -160,6 +160,27 @@ export function chromaCens(
|
|
|
160
160
|
return requireModule().chromaCens(samples, sampleRate, hopLength, nChroma);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Compute a constant-Q chromagram (librosa.feature.chroma_cqt).
|
|
165
|
+
*
|
|
166
|
+
* @param samples - Audio samples (mono, float32)
|
|
167
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
168
|
+
* @param hopLength - Hop length (default: 512)
|
|
169
|
+
* @param nChroma - Number of chroma bins (default: 12)
|
|
170
|
+
* @returns Chroma result
|
|
171
|
+
*/
|
|
172
|
+
export function chromaCqt(
|
|
173
|
+
samples: Float32Array,
|
|
174
|
+
sampleRate = 22050,
|
|
175
|
+
hopLength = 512,
|
|
176
|
+
nChroma = 12,
|
|
177
|
+
options: GuardedOptions = {},
|
|
178
|
+
): ChromaResult {
|
|
179
|
+
validateSpectrogramSamples('chromaCqt', samples, sampleRate, options);
|
|
180
|
+
validatePositiveIntegers('chromaCqt', { hopLength, nChroma });
|
|
181
|
+
return requireModule().chromaCqt(samples, sampleRate, hopLength, nChroma);
|
|
182
|
+
}
|
|
183
|
+
|
|
163
184
|
/**
|
|
164
185
|
* Compute low-frequency bass chroma.
|
|
165
186
|
*
|
|
@@ -237,6 +258,7 @@ export function melSpectrogram(
|
|
|
237
258
|
* @param fmin - Minimum Mel frequency in Hz (default: 0 = librosa default)
|
|
238
259
|
* @param fmax - Maximum Mel frequency in Hz (default: 0 = sampleRate / 2)
|
|
239
260
|
* @param htk - Use the HTK Mel formula instead of Slaney (default: false)
|
|
261
|
+
* @param lifter - Cepstral liftering coefficient (default: 0 = no liftering)
|
|
240
262
|
* @returns MFCC result
|
|
241
263
|
*/
|
|
242
264
|
export function mfcc(
|
|
@@ -249,12 +271,24 @@ export function mfcc(
|
|
|
249
271
|
fmin = 0,
|
|
250
272
|
fmax = 0,
|
|
251
273
|
htk = false,
|
|
274
|
+
lifter = 0,
|
|
252
275
|
options: GuardedOptions = {},
|
|
253
276
|
): MfccResult {
|
|
254
277
|
validateSpectrogramSamples('mfcc', samples, sampleRate, options);
|
|
255
278
|
validatePositiveIntegers('mfcc', { nFft, hopLength, nMels, nMfcc });
|
|
256
279
|
validateMelFrequencyRange('mfcc', fmin, fmax, sampleRate);
|
|
257
|
-
return requireModule().mfcc(
|
|
280
|
+
return requireModule().mfcc(
|
|
281
|
+
samples,
|
|
282
|
+
sampleRate,
|
|
283
|
+
nFft,
|
|
284
|
+
hopLength,
|
|
285
|
+
nMels,
|
|
286
|
+
nMfcc,
|
|
287
|
+
fmin,
|
|
288
|
+
fmax,
|
|
289
|
+
htk,
|
|
290
|
+
lifter,
|
|
291
|
+
);
|
|
258
292
|
}
|
|
259
293
|
|
|
260
294
|
// ============================================================================
|
|
@@ -433,7 +467,13 @@ export function mfccToAudio(
|
|
|
433
467
|
// ============================================================================
|
|
434
468
|
|
|
435
469
|
/**
|
|
436
|
-
* Compute chromagram (
|
|
470
|
+
* Compute STFT chromagram (librosa.feature.chroma_stft).
|
|
471
|
+
*
|
|
472
|
+
* The chroma filterbank uses a fixed tuning of 0 (concert A440). Unlike
|
|
473
|
+
* librosa.feature.chroma_stft — which estimates tuning from the signal when none
|
|
474
|
+
* is given — this does NOT auto-estimate and exposes no tuning argument, so
|
|
475
|
+
* sharp/flat (non-A440) recordings smear across pitch classes. Estimate tuning
|
|
476
|
+
* separately via {@link estimateTuning} if a non-A440 reference matters.
|
|
437
477
|
*
|
|
438
478
|
* @param samples - Audio samples (mono, float32)
|
|
439
479
|
* @param sampleRate - Sample rate in Hz (default: 22050)
|
package/src/features.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -29,6 +29,14 @@ import type {
|
|
|
29
29
|
|
|
30
30
|
export type { BrowserAudioDecodeOptions } from './audio';
|
|
31
31
|
export { Audio } from './audio';
|
|
32
|
+
export type {
|
|
33
|
+
ClipPageStreamerEngine,
|
|
34
|
+
ClipPageStreamerOptions,
|
|
35
|
+
ClipPageStreamSource,
|
|
36
|
+
OpfsClipStream,
|
|
37
|
+
OpfsClipStreamOptions,
|
|
38
|
+
} from './clip_page_streamer';
|
|
39
|
+
export { attachOpfsClipStream, ClipPageStreamer } from './clip_page_streamer';
|
|
32
40
|
export type {
|
|
33
41
|
CompressorDetector,
|
|
34
42
|
CompressorOptions,
|
|
@@ -97,6 +105,7 @@ export {
|
|
|
97
105
|
normalize,
|
|
98
106
|
noteStretch,
|
|
99
107
|
percussive,
|
|
108
|
+
pitchCorrectTimevarying,
|
|
100
109
|
pitchCorrectToMidi,
|
|
101
110
|
pitchCorrectToMidiTimevarying,
|
|
102
111
|
pitchShift,
|
|
@@ -114,6 +123,7 @@ export {
|
|
|
114
123
|
bassChroma,
|
|
115
124
|
chroma,
|
|
116
125
|
chromaCens,
|
|
126
|
+
chromaCqt,
|
|
117
127
|
cqt,
|
|
118
128
|
cyclicTempogram,
|
|
119
129
|
dbToAmplitude,
|
|
@@ -343,6 +353,7 @@ export type {
|
|
|
343
353
|
PairProcessor,
|
|
344
354
|
PanLaw,
|
|
345
355
|
PanMode,
|
|
356
|
+
PitchCorrectOptions,
|
|
346
357
|
PitchResult,
|
|
347
358
|
RealtimeVoiceChangerConfigInput,
|
|
348
359
|
RealtimeVoiceChangerPodConfig,
|
|
@@ -550,6 +561,18 @@ export function version(): string {
|
|
|
550
561
|
return module.version();
|
|
551
562
|
}
|
|
552
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Aggregate native ABI version: the per-subsystem ABI macros folded into one
|
|
566
|
+
* 32-bit value. It bumps whenever any flat C POD layout changes, so callers can
|
|
567
|
+
* detect an incompatible prebuilt binary. Matches the Node/Python `abiVersion()`.
|
|
568
|
+
*/
|
|
569
|
+
export function abiVersion(): number {
|
|
570
|
+
if (!module) {
|
|
571
|
+
throw new Error('Module not initialized. Call init() first.');
|
|
572
|
+
}
|
|
573
|
+
return module.abiVersion();
|
|
574
|
+
}
|
|
575
|
+
|
|
553
576
|
export function engineAbiVersion(): number {
|
|
554
577
|
if (!module) {
|
|
555
578
|
throw new Error('Module not initialized. Call init() first.');
|