@libraz/libsonare 1.2.3 → 1.3.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 +38 -1
- package/dist/index.d.ts +1 -2842
- package/dist/index.js +3602 -1934
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +1 -1
- package/dist/sonare-rt.js +1 -1
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +4816 -483
- package/dist/worklet.js +747 -440
- package/dist/worklet.js.map +1 -1
- package/package.json +2 -1
- package/src/analysis_helpers.ts +152 -0
- package/src/audio.ts +493 -0
- package/src/codes.ts +56 -0
- package/src/effects_mastering.ts +964 -0
- package/src/feature_core.ts +248 -0
- package/src/feature_music.ts +419 -0
- package/src/feature_pitch.ts +80 -0
- package/src/feature_resample.ts +21 -0
- package/src/feature_spectral.ts +330 -0
- package/src/feature_spectrogram.ts +454 -0
- package/src/features.ts +84 -0
- package/src/index.ts +341 -4963
- package/src/live_audio.ts +45 -0
- package/src/metering.ts +380 -0
- package/src/mixer.ts +523 -0
- package/src/module_state.ts +14 -0
- package/src/opfs_clip_pages.ts +188 -0
- package/src/project.ts +1614 -0
- package/src/public_types.ts +177 -2
- package/src/quick_analysis.ts +508 -0
- package/src/realtime_engine.ts +667 -0
- package/src/realtime_voice_changer.ts +275 -0
- package/src/scale.ts +42 -0
- package/src/sonare.js.d.ts +302 -4
- package/src/stream_analyzer.ts +275 -0
- package/src/stream_types.ts +26 -1
- package/src/streaming_mixing.ts +18 -0
- package/src/streaming_processors.ts +335 -0
- package/src/validation.ts +82 -0
- package/src/web_midi.ts +367 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chordChromaMethodValue,
|
|
3
|
+
convertAnalysisResult,
|
|
4
|
+
convertChordAnalysisResult,
|
|
5
|
+
convertKeyCandidate,
|
|
6
|
+
keyModeValues,
|
|
7
|
+
keyProfileValue,
|
|
8
|
+
} from './analysis_helpers';
|
|
9
|
+
import { getSonareModule } from './module_state';
|
|
10
|
+
import type {
|
|
11
|
+
AcousticOptions,
|
|
12
|
+
AcousticResult,
|
|
13
|
+
AnalysisResult,
|
|
14
|
+
AnalyzeBpmOptions,
|
|
15
|
+
AnalyzeDynamicsOptions,
|
|
16
|
+
AnalyzeRhythmOptions,
|
|
17
|
+
AnalyzeTimbreOptions,
|
|
18
|
+
ChordAnalysisResult,
|
|
19
|
+
ChordDetectionOptions,
|
|
20
|
+
Key,
|
|
21
|
+
KeyCandidate,
|
|
22
|
+
KeyDetectionOptions,
|
|
23
|
+
RirResult,
|
|
24
|
+
RirSynthOptions,
|
|
25
|
+
RoomEstimateOptions,
|
|
26
|
+
RoomEstimateResult,
|
|
27
|
+
RoomMorphOptions,
|
|
28
|
+
} from './public_types';
|
|
29
|
+
import { Mode, PitchClass } from './public_types';
|
|
30
|
+
import type { ProgressCallback, WasmAcousticResult } from './sonare.js';
|
|
31
|
+
import type { ValidateOptions } from './validation';
|
|
32
|
+
import { assertNonNegativeInteger, assertSampleRate, assertSamples } from './validation';
|
|
33
|
+
|
|
34
|
+
function requireModule() {
|
|
35
|
+
return getSonareModule();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type GuardedOptions = ValidateOptions;
|
|
39
|
+
|
|
40
|
+
function validateAnalysisInput(
|
|
41
|
+
fnName: string,
|
|
42
|
+
samples: Float32Array,
|
|
43
|
+
sampleRate: number,
|
|
44
|
+
options: GuardedOptions = {},
|
|
45
|
+
): void {
|
|
46
|
+
assertSampleRate(fnName, sampleRate);
|
|
47
|
+
assertSamples(fnName, samples, options.validate !== false);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Quick API (High-level Analysis)
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Detect BPM from audio samples.
|
|
56
|
+
*
|
|
57
|
+
* @param samples - Audio samples (mono, float32)
|
|
58
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
59
|
+
* @returns Detected BPM
|
|
60
|
+
*/
|
|
61
|
+
export function detectBpm(
|
|
62
|
+
samples: Float32Array,
|
|
63
|
+
sampleRate = 22050,
|
|
64
|
+
options: GuardedOptions = {},
|
|
65
|
+
): number {
|
|
66
|
+
validateAnalysisInput('detectBpm', samples, sampleRate, options);
|
|
67
|
+
return requireModule().detectBpm(samples, sampleRate);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Detect musical key from audio samples.
|
|
72
|
+
*
|
|
73
|
+
* @param samples - Audio samples (mono, float32)
|
|
74
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
75
|
+
* @returns Detected key
|
|
76
|
+
*/
|
|
77
|
+
export function detectKey(
|
|
78
|
+
samples: Float32Array,
|
|
79
|
+
sampleRate = 22050,
|
|
80
|
+
options: KeyDetectionOptions = {},
|
|
81
|
+
): Key {
|
|
82
|
+
validateAnalysisInput('detectKey', samples, sampleRate, options);
|
|
83
|
+
const result = requireModule()._detectKeyWithOptions(
|
|
84
|
+
samples,
|
|
85
|
+
sampleRate,
|
|
86
|
+
options.nFft ?? 4096,
|
|
87
|
+
options.hopLength ?? 512,
|
|
88
|
+
options.useHpss ?? false,
|
|
89
|
+
options.loudnessWeighted ?? false,
|
|
90
|
+
options.highPassHz ?? 0,
|
|
91
|
+
keyModeValues(options.modes),
|
|
92
|
+
keyProfileValue(options.profile),
|
|
93
|
+
options.genreHint ?? '',
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
root: result.root as PitchClass,
|
|
97
|
+
mode: result.mode as Mode,
|
|
98
|
+
confidence: result.confidence,
|
|
99
|
+
name: result.name,
|
|
100
|
+
shortName: result.shortName,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function detectKeyCandidates(
|
|
105
|
+
samples: Float32Array,
|
|
106
|
+
sampleRate = 22050,
|
|
107
|
+
options: KeyDetectionOptions = {},
|
|
108
|
+
): KeyCandidate[] {
|
|
109
|
+
validateAnalysisInput('detectKeyCandidates', samples, sampleRate, options);
|
|
110
|
+
return requireModule()
|
|
111
|
+
._detectKeyCandidates(
|
|
112
|
+
samples,
|
|
113
|
+
sampleRate,
|
|
114
|
+
options.nFft ?? 4096,
|
|
115
|
+
options.hopLength ?? 512,
|
|
116
|
+
options.useHpss ?? false,
|
|
117
|
+
options.loudnessWeighted ?? false,
|
|
118
|
+
options.highPassHz ?? 0,
|
|
119
|
+
keyModeValues(options.modes),
|
|
120
|
+
keyProfileValue(options.profile),
|
|
121
|
+
options.genreHint ?? '',
|
|
122
|
+
)
|
|
123
|
+
.map(convertKeyCandidate);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Detect onset times from audio samples.
|
|
128
|
+
*
|
|
129
|
+
* @param samples - Audio samples (mono, float32)
|
|
130
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
131
|
+
* @returns Array of onset times in seconds
|
|
132
|
+
*/
|
|
133
|
+
export function detectOnsets(
|
|
134
|
+
samples: Float32Array,
|
|
135
|
+
sampleRate = 22050,
|
|
136
|
+
options: GuardedOptions = {},
|
|
137
|
+
): Float32Array {
|
|
138
|
+
validateAnalysisInput('detectOnsets', samples, sampleRate, options);
|
|
139
|
+
return requireModule().detectOnsets(samples, sampleRate);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Detect beat times from audio samples.
|
|
144
|
+
*
|
|
145
|
+
* @param samples - Audio samples (mono, float32)
|
|
146
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
147
|
+
* @returns Array of beat times in seconds
|
|
148
|
+
*/
|
|
149
|
+
export function detectBeats(
|
|
150
|
+
samples: Float32Array,
|
|
151
|
+
sampleRate = 22050,
|
|
152
|
+
options: GuardedOptions = {},
|
|
153
|
+
): Float32Array {
|
|
154
|
+
validateAnalysisInput('detectBeats', samples, sampleRate, options);
|
|
155
|
+
return requireModule().detectBeats(samples, sampleRate);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Detect downbeat times from audio samples.
|
|
160
|
+
*
|
|
161
|
+
* @param samples - Audio samples (mono, float32)
|
|
162
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
163
|
+
* @returns Array of downbeat times in seconds
|
|
164
|
+
*/
|
|
165
|
+
export function detectDownbeats(
|
|
166
|
+
samples: Float32Array,
|
|
167
|
+
sampleRate = 22050,
|
|
168
|
+
options: GuardedOptions = {},
|
|
169
|
+
): Float32Array {
|
|
170
|
+
validateAnalysisInput('detectDownbeats', samples, sampleRate, options);
|
|
171
|
+
return requireModule().detectDownbeats(samples, sampleRate);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Detect chords from audio samples.
|
|
176
|
+
*
|
|
177
|
+
* @param samples - Audio samples (mono, float32)
|
|
178
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
179
|
+
* @param options - Optional chord detection settings
|
|
180
|
+
* @returns Detected chord segments
|
|
181
|
+
*/
|
|
182
|
+
export function detectChords(
|
|
183
|
+
samples: Float32Array,
|
|
184
|
+
sampleRate = 22050,
|
|
185
|
+
options: ChordDetectionOptions = {},
|
|
186
|
+
): ChordAnalysisResult {
|
|
187
|
+
validateAnalysisInput('detectChords', samples, sampleRate, options);
|
|
188
|
+
const result = requireModule().detectChords(
|
|
189
|
+
samples,
|
|
190
|
+
sampleRate,
|
|
191
|
+
options.minDuration ?? 0.3,
|
|
192
|
+
options.smoothingWindow ?? 2.0,
|
|
193
|
+
options.threshold ?? 0.5,
|
|
194
|
+
options.useTriadsOnly ?? false,
|
|
195
|
+
options.nFft ?? 2048,
|
|
196
|
+
options.hopLength ?? 512,
|
|
197
|
+
options.useBeatSync ?? true,
|
|
198
|
+
options.useHmm ?? false,
|
|
199
|
+
options.hmmBeamWidth ?? 24,
|
|
200
|
+
options.useKeyContext ?? false,
|
|
201
|
+
options.keyRoot ?? PitchClass.C,
|
|
202
|
+
options.keyMode ?? Mode.Major,
|
|
203
|
+
options.detectInversions ?? false,
|
|
204
|
+
chordChromaMethodValue(options.chromaMethod ?? 'stft'),
|
|
205
|
+
);
|
|
206
|
+
return convertChordAnalysisResult(result);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Functional (Roman-numeral) harmonic analysis of the detected chord
|
|
211
|
+
* progression, relative to the given key. Mirrors the C-ABI
|
|
212
|
+
* `sonare_chord_functional_analysis` and the Node/Python `chordFunctionalAnalysis`.
|
|
213
|
+
*
|
|
214
|
+
* @returns One Roman-numeral label (e.g. "I", "IV", "V", "vi") per detected chord
|
|
215
|
+
*/
|
|
216
|
+
export function chordFunctionalAnalysis(
|
|
217
|
+
samples: Float32Array,
|
|
218
|
+
keyRoot: PitchClass,
|
|
219
|
+
keyMode: Mode,
|
|
220
|
+
sampleRate = 22050,
|
|
221
|
+
options: ChordDetectionOptions = {},
|
|
222
|
+
): string[] {
|
|
223
|
+
validateAnalysisInput('chordFunctionalAnalysis', samples, sampleRate, options);
|
|
224
|
+
return requireModule().chordFunctionalAnalysis(
|
|
225
|
+
samples,
|
|
226
|
+
keyRoot,
|
|
227
|
+
keyMode,
|
|
228
|
+
sampleRate,
|
|
229
|
+
options.minDuration ?? 0.3,
|
|
230
|
+
options.smoothingWindow ?? 2.0,
|
|
231
|
+
options.threshold ?? 0.5,
|
|
232
|
+
options.useTriadsOnly ?? false,
|
|
233
|
+
options.nFft ?? 2048,
|
|
234
|
+
options.hopLength ?? 512,
|
|
235
|
+
options.useBeatSync ?? true,
|
|
236
|
+
options.useHmm ?? false,
|
|
237
|
+
options.hmmBeamWidth ?? 24,
|
|
238
|
+
options.useKeyContext ?? false,
|
|
239
|
+
options.detectInversions ?? false,
|
|
240
|
+
chordChromaMethodValue(options.chromaMethod ?? 'stft'),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Perform complete music analysis.
|
|
246
|
+
*
|
|
247
|
+
* @param samples - Audio samples (mono, float32)
|
|
248
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
249
|
+
* @returns Complete analysis result
|
|
250
|
+
*
|
|
251
|
+
* @remarks
|
|
252
|
+
* This call is synchronous and blocks until analysis completes. Unlike the
|
|
253
|
+
* Node binding (which offers `analyzeAsync` on a libuv worker thread), the
|
|
254
|
+
* WASM build runs on a single thread, so there is no non-blocking variant —
|
|
255
|
+
* the DSP pipeline always runs to completion on the calling thread. To keep
|
|
256
|
+
* the UI responsive for long inputs, drive this from a Web Worker and use
|
|
257
|
+
* {@link analyzeWithProgress} to report progress.
|
|
258
|
+
*/
|
|
259
|
+
export function analyze(
|
|
260
|
+
samples: Float32Array,
|
|
261
|
+
sampleRate = 22050,
|
|
262
|
+
options: GuardedOptions = {},
|
|
263
|
+
): AnalysisResult {
|
|
264
|
+
validateAnalysisInput('analyze', samples, sampleRate, options);
|
|
265
|
+
const result = requireModule().analyze(samples, sampleRate);
|
|
266
|
+
return convertAnalysisResult(result);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function analyzeImpulseResponse(
|
|
270
|
+
samples: Float32Array,
|
|
271
|
+
sampleRate = 48000,
|
|
272
|
+
nOctaveBands = 6,
|
|
273
|
+
): AcousticResult {
|
|
274
|
+
validateAnalysisInput('analyzeImpulseResponse', samples, sampleRate);
|
|
275
|
+
const result: WasmAcousticResult = requireModule().analyzeImpulseResponse(
|
|
276
|
+
samples,
|
|
277
|
+
sampleRate,
|
|
278
|
+
nOctaveBands,
|
|
279
|
+
);
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function detectAcoustic(
|
|
284
|
+
samples: Float32Array,
|
|
285
|
+
sampleRate = 48000,
|
|
286
|
+
options: AcousticOptions = {},
|
|
287
|
+
): AcousticResult {
|
|
288
|
+
validateAnalysisInput('detectAcoustic', samples, sampleRate);
|
|
289
|
+
const result: WasmAcousticResult = requireModule().detectAcoustic(
|
|
290
|
+
samples,
|
|
291
|
+
sampleRate,
|
|
292
|
+
options.nOctaveBands ?? 6,
|
|
293
|
+
options.nThirdOctaveSubbands ?? 24,
|
|
294
|
+
options.minDecayDb ?? 30.0,
|
|
295
|
+
options.noiseFloorMarginDb ?? 10.0,
|
|
296
|
+
);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Synthesize a room impulse response from shoebox geometry. `hasError` is true
|
|
302
|
+
* when the source/listener falls outside the room (the RIR is then empty).
|
|
303
|
+
*/
|
|
304
|
+
export function synthesizeRir(options: RirSynthOptions = {}): RirResult {
|
|
305
|
+
const module = requireModule();
|
|
306
|
+
if (typeof module.synthesizeRir !== 'function') {
|
|
307
|
+
throw new Error('libsonare was built without acoustic-simulation support');
|
|
308
|
+
}
|
|
309
|
+
return module.synthesizeRir(options);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Estimate an equivalent room (volume/dimensions/absorption/DRR) from a
|
|
314
|
+
* recording or impulse response.
|
|
315
|
+
*/
|
|
316
|
+
export function estimateRoom(
|
|
317
|
+
samples: Float32Array,
|
|
318
|
+
sampleRate = 48000,
|
|
319
|
+
options: RoomEstimateOptions = {},
|
|
320
|
+
): RoomEstimateResult {
|
|
321
|
+
const module = requireModule();
|
|
322
|
+
if (typeof module.estimateRoom !== 'function') {
|
|
323
|
+
throw new Error('libsonare was built without acoustic-simulation support');
|
|
324
|
+
}
|
|
325
|
+
validateAnalysisInput('estimateRoom', samples, sampleRate);
|
|
326
|
+
return module.estimateRoom(samples, sampleRate, options);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Morph a recording's reverberation toward a target room (creative FX, not
|
|
331
|
+
* dereverberation). Returns the morphed samples (input length plus the target
|
|
332
|
+
* room's reverb tail).
|
|
333
|
+
*/
|
|
334
|
+
export function roomMorph(
|
|
335
|
+
samples: Float32Array,
|
|
336
|
+
sampleRate: number,
|
|
337
|
+
options: RoomMorphOptions = {},
|
|
338
|
+
): Float32Array {
|
|
339
|
+
const module = requireModule();
|
|
340
|
+
if (typeof module.roomMorph !== 'function') {
|
|
341
|
+
throw new Error('libsonare was built without acoustic-simulation support');
|
|
342
|
+
}
|
|
343
|
+
validateAnalysisInput('roomMorph', samples, sampleRate);
|
|
344
|
+
return module.roomMorph(samples, sampleRate, options);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Perform complete music analysis with progress reporting.
|
|
349
|
+
*
|
|
350
|
+
* @param samples - Audio samples (mono, float32)
|
|
351
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
352
|
+
* @param onProgress - Progress callback (progress: 0-1, stage: string)
|
|
353
|
+
* @returns Complete analysis result
|
|
354
|
+
*/
|
|
355
|
+
export function analyzeWithProgress(
|
|
356
|
+
samples: Float32Array,
|
|
357
|
+
sampleRate = 22050,
|
|
358
|
+
onProgress: ProgressCallback,
|
|
359
|
+
): AnalysisResult {
|
|
360
|
+
validateAnalysisInput('analyzeWithProgress', samples, sampleRate);
|
|
361
|
+
const result = requireModule().analyzeWithProgress(samples, sampleRate, onProgress);
|
|
362
|
+
return convertAnalysisResult(result);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface BpmCandidate {
|
|
366
|
+
bpm: number;
|
|
367
|
+
confidence: number;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export interface BpmAnalysisResult {
|
|
371
|
+
bpm: number;
|
|
372
|
+
confidence: number;
|
|
373
|
+
candidates: BpmCandidate[];
|
|
374
|
+
autocorrelation: Float32Array;
|
|
375
|
+
tempogram: Float32Array;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export interface RhythmAnalysisResult {
|
|
379
|
+
timeSignature: { numerator: number; denominator: number; confidence: number };
|
|
380
|
+
syncopation: number;
|
|
381
|
+
grooveType: string;
|
|
382
|
+
patternRegularity: number;
|
|
383
|
+
tempoStability: number;
|
|
384
|
+
bpm: number;
|
|
385
|
+
beatIntervals: Float32Array;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface DynamicsAnalysisResult {
|
|
389
|
+
dynamicRangeDb: number;
|
|
390
|
+
peakDb: number;
|
|
391
|
+
rmsDb: number;
|
|
392
|
+
crestFactor: number;
|
|
393
|
+
loudnessRangeDb: number;
|
|
394
|
+
isCompressed: boolean;
|
|
395
|
+
/** Loudness curve timestamps (seconds), parallel to {@link loudnessRmsDb}. */
|
|
396
|
+
loudnessTimes: Float32Array;
|
|
397
|
+
/** Loudness curve RMS values (dB), parallel to {@link loudnessTimes}. */
|
|
398
|
+
loudnessRmsDb: Float32Array;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** Timbre metrics for one analysis window. Entries are ordered by time in `timbreOverTime`. */
|
|
402
|
+
export interface TimbreFrame {
|
|
403
|
+
brightness: number;
|
|
404
|
+
warmth: number;
|
|
405
|
+
density: number;
|
|
406
|
+
roughness: number;
|
|
407
|
+
complexity: number;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export interface TimbreAnalysisResult extends TimbreFrame {
|
|
411
|
+
spectralCentroid: Float32Array;
|
|
412
|
+
spectralFlatness: Float32Array;
|
|
413
|
+
spectralRolloff: Float32Array;
|
|
414
|
+
/** Time-varying timbre metrics, one entry per analysis window. */
|
|
415
|
+
timbreOverTime: TimbreFrame[];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Detailed BPM analysis (BPM, confidence, alternate candidates, autocorrelation,
|
|
420
|
+
* tempogram). Matches the Node `analyzeBpm` / Python `analyze_bpm` surface.
|
|
421
|
+
*/
|
|
422
|
+
export function analyzeBpm(
|
|
423
|
+
samples: Float32Array,
|
|
424
|
+
sampleRate = 22050,
|
|
425
|
+
options: AnalyzeBpmOptions = {},
|
|
426
|
+
): BpmAnalysisResult {
|
|
427
|
+
validateAnalysisInput('analyzeBpm', samples, sampleRate, options);
|
|
428
|
+
assertNonNegativeInteger('analyzeBpm', options.maxCandidates ?? 5, 'maxCandidates');
|
|
429
|
+
return requireModule().analyzeBpm(
|
|
430
|
+
samples,
|
|
431
|
+
sampleRate,
|
|
432
|
+
options.bpmMin ?? 30.0,
|
|
433
|
+
options.bpmMax ?? 300.0,
|
|
434
|
+
options.startBpm ?? 120.0,
|
|
435
|
+
options.nFft ?? 2048,
|
|
436
|
+
options.hopLength ?? 512,
|
|
437
|
+
options.maxCandidates ?? 5,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Detailed rhythm analysis (time signature, groove, syncopation, beat intervals).
|
|
443
|
+
*/
|
|
444
|
+
export function analyzeRhythm(
|
|
445
|
+
samples: Float32Array,
|
|
446
|
+
sampleRate = 22050,
|
|
447
|
+
options: AnalyzeRhythmOptions = {},
|
|
448
|
+
): RhythmAnalysisResult {
|
|
449
|
+
validateAnalysisInput('analyzeRhythm', samples, sampleRate, options);
|
|
450
|
+
return requireModule().analyzeRhythm(
|
|
451
|
+
samples,
|
|
452
|
+
sampleRate,
|
|
453
|
+
options.bpmMin ?? 60.0,
|
|
454
|
+
options.bpmMax ?? 200.0,
|
|
455
|
+
options.startBpm ?? 120.0,
|
|
456
|
+
options.nFft ?? 2048,
|
|
457
|
+
options.hopLength ?? 512,
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Dynamics analysis (RMS, peak, crest factor, LRA, loudness curve).
|
|
463
|
+
*/
|
|
464
|
+
export function analyzeDynamics(
|
|
465
|
+
samples: Float32Array,
|
|
466
|
+
sampleRate = 22050,
|
|
467
|
+
options: AnalyzeDynamicsOptions = {},
|
|
468
|
+
): DynamicsAnalysisResult {
|
|
469
|
+
validateAnalysisInput('analyzeDynamics', samples, sampleRate, options);
|
|
470
|
+
return requireModule().analyzeDynamics(
|
|
471
|
+
samples,
|
|
472
|
+
sampleRate,
|
|
473
|
+
options.windowSec ?? 0.4,
|
|
474
|
+
options.hopLength ?? 512,
|
|
475
|
+
options.compressionThreshold ?? 6.0,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Timbre analysis (brightness/warmth/density/roughness/complexity plus spectral
|
|
481
|
+
* features and per-window timbre frames).
|
|
482
|
+
*/
|
|
483
|
+
export function analyzeTimbre(
|
|
484
|
+
samples: Float32Array,
|
|
485
|
+
sampleRate = 22050,
|
|
486
|
+
options: AnalyzeTimbreOptions = {},
|
|
487
|
+
): TimbreAnalysisResult {
|
|
488
|
+
validateAnalysisInput('analyzeTimbre', samples, sampleRate, options);
|
|
489
|
+
return requireModule().analyzeTimbre(
|
|
490
|
+
samples,
|
|
491
|
+
sampleRate,
|
|
492
|
+
options.nFft ?? 2048,
|
|
493
|
+
options.hopLength ?? 512,
|
|
494
|
+
options.nMels ?? 128,
|
|
495
|
+
options.nMfcc ?? 13,
|
|
496
|
+
options.windowSec ?? 0.5,
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Whether this WASM build was compiled with FFmpeg support. Mirrors Node /
|
|
502
|
+
* Python `hasFfmpegSupport`. In the published WASM binding this currently
|
|
503
|
+
* always returns `false` (FFmpeg is not bundled into the .wasm), but the API
|
|
504
|
+
* exists so caller code can branch on capabilities portably.
|
|
505
|
+
*/
|
|
506
|
+
export function hasFfmpegSupport(): boolean {
|
|
507
|
+
return requireModule().hasFfmpegSupport();
|
|
508
|
+
}
|