@libraz/libsonare 1.5.2 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +69 -725
  2. package/dist/index.d.ts +1155 -183
  3. package/dist/index.js +4924 -3935
  4. package/dist/index.js.map +1 -1
  5. package/dist/sonare.js +1 -1
  6. package/dist/sonare.wasm +0 -0
  7. package/dist/worklet.d.ts +29 -1
  8. package/dist/worklet.js +1141 -970
  9. package/dist/worklet.js.map +1 -1
  10. package/package.json +15 -3
  11. package/src/_chain_config.ts +46 -0
  12. package/src/audio.ts +24 -4
  13. package/src/codes.ts +27 -4
  14. package/src/effects_mastering.ts +25 -1
  15. package/src/effects_transform.ts +292 -46
  16. package/src/effects_voice_change.ts +46 -53
  17. package/src/feature_core.ts +316 -12
  18. package/src/feature_music.ts +371 -4
  19. package/src/feature_pitch.ts +61 -0
  20. package/src/feature_resample.ts +17 -2
  21. package/src/feature_spectral.ts +349 -2
  22. package/src/feature_spectrogram.ts +336 -0
  23. package/src/index.ts +130 -1
  24. package/src/mastering_chain.ts +305 -50
  25. package/src/mastering_core.ts +232 -16
  26. package/src/mastering_dynamics.ts +66 -10
  27. package/src/mastering_repair.ts +119 -7
  28. package/src/metering.ts +370 -83
  29. package/src/mixer.ts +9 -2
  30. package/src/mixing_oneshot.ts +25 -5
  31. package/src/module_state.ts +1 -1
  32. package/src/project_class.ts +30 -6
  33. package/src/project_internal.ts +12 -2
  34. package/src/project_types.ts +25 -2
  35. package/src/public_types_mastering.ts +114 -4
  36. package/src/public_types_spectral.ts +7 -0
  37. package/src/quick_analysis.ts +325 -117
  38. package/src/realtime_engine.ts +8 -0
  39. package/src/realtime_voice_changer.ts +79 -3
  40. package/src/sonare.js.d.ts +35 -0
  41. package/src/stream_analyzer.ts +20 -1
  42. package/src/stream_types.ts +12 -1
  43. package/src/validation.ts +17 -2
  44. package/src/worklet/engine-clips.ts +67 -2
  45. package/src/worklet/engine-processor.ts +33 -5
  46. package/src/worklet/engine.ts +7 -3
  47. package/src/worklet/guards.ts +3 -0
  48. package/src/worklet/messages.ts +27 -0
@@ -37,6 +37,53 @@ function requireModule() {
37
37
 
38
38
  type GuardedOptions = ValidateOptions;
39
39
 
40
+ /** Canonical request form for one-shot analysis functions. */
41
+ export interface SamplesRequest extends GuardedOptions {
42
+ samples: Float32Array;
43
+ sampleRate?: number;
44
+ }
45
+
46
+ /** Canonical request form for key detection functions. */
47
+ export interface DetectKeyRequest extends KeyDetectionOptions, SamplesRequest {}
48
+
49
+ /** Canonical request form for analysis with synchronous progress reporting. */
50
+ export interface AnalyzeWithProgressRequest extends SamplesRequest {
51
+ onProgress: ProgressCallback;
52
+ }
53
+
54
+ /** Canonical request form for chord detection. */
55
+ export interface DetectChordsRequest extends ChordDetectionOptions, SamplesRequest {}
56
+
57
+ /** Canonical request form for functional chord analysis. */
58
+ export interface ChordFunctionalAnalysisRequest extends DetectChordsRequest {
59
+ keyRoot: PitchClass;
60
+ /** Musical mode; defaults to {@link Mode.Major}. */
61
+ keyMode?: Mode;
62
+ }
63
+
64
+ /** Canonical request form for impulse-response analysis. */
65
+ export interface AnalyzeImpulseResponseRequest extends SamplesRequest {
66
+ nOctaveBands?: number;
67
+ }
68
+
69
+ /** Canonical request form for acoustic analysis. */
70
+ export interface DetectAcousticRequest extends AcousticOptions, SamplesRequest {}
71
+
72
+ /** Canonical request form for equivalent-room estimation. */
73
+ export interface EstimateRoomRequest extends RoomEstimateOptions, SamplesRequest {}
74
+
75
+ /** Canonical request form for room-reverb morphing. */
76
+ export interface RoomMorphRequest extends RoomMorphOptions, GuardedOptions {
77
+ samples: Float32Array;
78
+ sampleRate: number;
79
+ }
80
+
81
+ /** Canonical request forms for detailed music-analysis APIs. */
82
+ export interface AnalyzeBpmRequest extends AnalyzeBpmOptions, SamplesRequest {}
83
+ export interface AnalyzeRhythmRequest extends AnalyzeRhythmOptions, SamplesRequest {}
84
+ export interface AnalyzeDynamicsRequest extends AnalyzeDynamicsOptions, SamplesRequest {}
85
+ export interface AnalyzeTimbreRequest extends AnalyzeTimbreOptions, SamplesRequest {}
86
+
40
87
  function validateAnalysisInput(
41
88
  fnName: string,
42
89
  samples: Float32Array,
@@ -58,13 +105,20 @@ function validateAnalysisInput(
58
105
  * @param sampleRate - Sample rate in Hz (default: 22050)
59
106
  * @returns Detected BPM
60
107
  */
108
+ export function detectBpm(request: SamplesRequest): number;
61
109
  export function detectBpm(
62
110
  samples: Float32Array,
111
+ sampleRate?: number,
112
+ options?: GuardedOptions,
113
+ ): number;
114
+ export function detectBpm(
115
+ samples: Float32Array | SamplesRequest,
63
116
  sampleRate = 22050,
64
117
  options: GuardedOptions = {},
65
118
  ): number {
66
- validateAnalysisInput('detectBpm', samples, sampleRate, options);
67
- return requireModule().detectBpm(samples, sampleRate);
119
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
120
+ validateAnalysisInput('detectBpm', request.samples, request.sampleRate ?? 22050, request);
121
+ return requireModule().detectBpm(request.samples, request.sampleRate ?? 22050);
68
122
  }
69
123
 
70
124
  /**
@@ -74,23 +128,30 @@ export function detectBpm(
74
128
  * @param sampleRate - Sample rate in Hz (default: 22050)
75
129
  * @returns Detected key
76
130
  */
131
+ export function detectKey(request: DetectKeyRequest): Key;
77
132
  export function detectKey(
78
133
  samples: Float32Array,
134
+ sampleRate?: number,
135
+ options?: KeyDetectionOptions,
136
+ ): Key;
137
+ export function detectKey(
138
+ samples: Float32Array | DetectKeyRequest,
79
139
  sampleRate = 22050,
80
140
  options: KeyDetectionOptions = {},
81
141
  ): Key {
82
- validateAnalysisInput('detectKey', samples, sampleRate, options);
142
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
143
+ validateAnalysisInput('detectKey', request.samples, request.sampleRate ?? 22050, request);
83
144
  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 ?? '',
145
+ request.samples,
146
+ request.sampleRate ?? 22050,
147
+ request.nFft ?? 4096,
148
+ request.hopLength ?? 512,
149
+ request.useHpss ?? false,
150
+ request.loudnessWeighted ?? false,
151
+ request.highPassHz ?? 0,
152
+ keyModeValues(request.modes),
153
+ keyProfileValue(request.profile),
154
+ request.genreHint ?? '',
94
155
  );
95
156
  return {
96
157
  root: result.root as PitchClass,
@@ -101,28 +162,40 @@ export function detectKey(
101
162
  };
102
163
  }
103
164
 
165
+ export function detectKeyCandidates(request: DetectKeyRequest): KeyCandidate[];
104
166
  export function detectKeyCandidates(
105
167
  samples: Float32Array,
168
+ sampleRate?: number,
169
+ options?: KeyDetectionOptions,
170
+ ): KeyCandidate[];
171
+ export function detectKeyCandidates(
172
+ samples: Float32Array | DetectKeyRequest,
106
173
  sampleRate = 22050,
107
174
  options: KeyDetectionOptions = {},
108
175
  ): KeyCandidate[] {
109
- validateAnalysisInput('detectKeyCandidates', samples, sampleRate, options);
176
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
177
+ validateAnalysisInput(
178
+ 'detectKeyCandidates',
179
+ request.samples,
180
+ request.sampleRate ?? 22050,
181
+ request,
182
+ );
110
183
  // The embind value marshalling returns an array whose constructor is not this
111
184
  // realm's Array; chaining .map() onto it propagates that constructor via
112
185
  // Symbol.species, leaving a result that structuredClone (and so postMessage to
113
186
  // a Worker) rejects with "could not be cloned". Array.from() re-roots it as a
114
187
  // plain Array before mapping.
115
188
  const candidates = requireModule()._detectKeyCandidates(
116
- samples,
117
- sampleRate,
118
- options.nFft ?? 4096,
119
- options.hopLength ?? 512,
120
- options.useHpss ?? false,
121
- options.loudnessWeighted ?? false,
122
- options.highPassHz ?? 0,
123
- keyModeValues(options.modes),
124
- keyProfileValue(options.profile),
125
- options.genreHint ?? '',
189
+ request.samples,
190
+ request.sampleRate ?? 22050,
191
+ request.nFft ?? 4096,
192
+ request.hopLength ?? 512,
193
+ request.useHpss ?? false,
194
+ request.loudnessWeighted ?? false,
195
+ request.highPassHz ?? 0,
196
+ keyModeValues(request.modes),
197
+ keyProfileValue(request.profile),
198
+ request.genreHint ?? '',
126
199
  );
127
200
  return Array.from(candidates, convertKeyCandidate);
128
201
  }
@@ -134,13 +207,20 @@ export function detectKeyCandidates(
134
207
  * @param sampleRate - Sample rate in Hz (default: 22050)
135
208
  * @returns Array of onset times in seconds
136
209
  */
210
+ export function detectOnsets(request: SamplesRequest): Float32Array;
137
211
  export function detectOnsets(
138
212
  samples: Float32Array,
213
+ sampleRate?: number,
214
+ options?: GuardedOptions,
215
+ ): Float32Array;
216
+ export function detectOnsets(
217
+ samples: Float32Array | SamplesRequest,
139
218
  sampleRate = 22050,
140
219
  options: GuardedOptions = {},
141
220
  ): Float32Array {
142
- validateAnalysisInput('detectOnsets', samples, sampleRate, options);
143
- return requireModule().detectOnsets(samples, sampleRate);
221
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
222
+ validateAnalysisInput('detectOnsets', request.samples, request.sampleRate ?? 22050, request);
223
+ return requireModule().detectOnsets(request.samples, request.sampleRate ?? 22050);
144
224
  }
145
225
 
146
226
  /**
@@ -150,13 +230,20 @@ export function detectOnsets(
150
230
  * @param sampleRate - Sample rate in Hz (default: 22050)
151
231
  * @returns Array of beat times in seconds
152
232
  */
233
+ export function detectBeats(request: SamplesRequest): Float32Array;
153
234
  export function detectBeats(
154
235
  samples: Float32Array,
236
+ sampleRate?: number,
237
+ options?: GuardedOptions,
238
+ ): Float32Array;
239
+ export function detectBeats(
240
+ samples: Float32Array | SamplesRequest,
155
241
  sampleRate = 22050,
156
242
  options: GuardedOptions = {},
157
243
  ): Float32Array {
158
- validateAnalysisInput('detectBeats', samples, sampleRate, options);
159
- return requireModule().detectBeats(samples, sampleRate);
244
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
245
+ validateAnalysisInput('detectBeats', request.samples, request.sampleRate ?? 22050, request);
246
+ return requireModule().detectBeats(request.samples, request.sampleRate ?? 22050);
160
247
  }
161
248
 
162
249
  /**
@@ -166,13 +253,20 @@ export function detectBeats(
166
253
  * @param sampleRate - Sample rate in Hz (default: 22050)
167
254
  * @returns Array of downbeat times in seconds
168
255
  */
256
+ export function detectDownbeats(request: SamplesRequest): Float32Array;
169
257
  export function detectDownbeats(
170
258
  samples: Float32Array,
259
+ sampleRate?: number,
260
+ options?: GuardedOptions,
261
+ ): Float32Array;
262
+ export function detectDownbeats(
263
+ samples: Float32Array | SamplesRequest,
171
264
  sampleRate = 22050,
172
265
  options: GuardedOptions = {},
173
266
  ): Float32Array {
174
- validateAnalysisInput('detectDownbeats', samples, sampleRate, options);
175
- return requireModule().detectDownbeats(samples, sampleRate);
267
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
268
+ validateAnalysisInput('detectDownbeats', request.samples, request.sampleRate ?? 22050, request);
269
+ return requireModule().detectDownbeats(request.samples, request.sampleRate ?? 22050);
176
270
  }
177
271
 
178
272
  /**
@@ -183,29 +277,36 @@ export function detectDownbeats(
183
277
  * @param options - Optional chord detection settings
184
278
  * @returns Detected chord segments
185
279
  */
280
+ export function detectChords(request: DetectChordsRequest): ChordAnalysisResult;
186
281
  export function detectChords(
187
282
  samples: Float32Array,
283
+ sampleRate?: number,
284
+ options?: ChordDetectionOptions,
285
+ ): ChordAnalysisResult;
286
+ export function detectChords(
287
+ samples: Float32Array | DetectChordsRequest,
188
288
  sampleRate = 22050,
189
289
  options: ChordDetectionOptions = {},
190
290
  ): ChordAnalysisResult {
191
- validateAnalysisInput('detectChords', samples, sampleRate, options);
291
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
292
+ validateAnalysisInput('detectChords', request.samples, request.sampleRate ?? 22050, request);
192
293
  const result = requireModule().detectChords(
193
- samples,
194
- sampleRate,
195
- options.minDuration ?? 0.3,
196
- options.smoothingWindow ?? 2.0,
197
- options.threshold ?? 0.5,
198
- options.useTriadsOnly ?? false,
199
- options.nFft ?? 2048,
200
- options.hopLength ?? 512,
201
- options.useBeatSync ?? true,
202
- options.useHmm ?? false,
203
- options.hmmBeamWidth ?? 24,
204
- options.useKeyContext ?? false,
205
- options.keyRoot ?? PitchClass.C,
206
- options.keyMode ?? Mode.Major,
207
- options.detectInversions ?? false,
208
- chordChromaMethodValue(options.chromaMethod ?? 'stft'),
294
+ request.samples,
295
+ request.sampleRate ?? 22050,
296
+ request.minDuration ?? 0.3,
297
+ request.smoothingWindow ?? 2.0,
298
+ request.threshold ?? 0.5,
299
+ request.useTriadsOnly ?? false,
300
+ request.nFft ?? 2048,
301
+ request.hopLength ?? 512,
302
+ request.useBeatSync ?? true,
303
+ request.useHmm ?? false,
304
+ request.hmmBeamWidth ?? 24,
305
+ request.useKeyContext ?? false,
306
+ request.keyRoot ?? PitchClass.C,
307
+ request.keyMode ?? Mode.Major,
308
+ request.detectInversions ?? false,
309
+ chordChromaMethodValue(request.chromaMethod ?? 'stft'),
209
310
  );
210
311
  return convertChordAnalysisResult(result);
211
312
  }
@@ -217,31 +318,48 @@ export function detectChords(
217
318
  *
218
319
  * @returns One Roman-numeral label (e.g. "I", "IV", "V", "vi") per detected chord
219
320
  */
321
+ export function chordFunctionalAnalysis(request: ChordFunctionalAnalysisRequest): string[];
220
322
  export function chordFunctionalAnalysis(
221
323
  samples: Float32Array,
222
324
  keyRoot: PitchClass,
223
- keyMode: Mode,
325
+ keyMode?: Mode,
326
+ sampleRate?: number,
327
+ options?: ChordDetectionOptions,
328
+ ): string[];
329
+ export function chordFunctionalAnalysis(
330
+ samples: Float32Array | ChordFunctionalAnalysisRequest,
331
+ keyRoot?: PitchClass,
332
+ keyMode?: Mode,
224
333
  sampleRate = 22050,
225
334
  options: ChordDetectionOptions = {},
226
335
  ): string[] {
227
- validateAnalysisInput('chordFunctionalAnalysis', samples, sampleRate, options);
336
+ const request =
337
+ samples instanceof Float32Array
338
+ ? { samples, keyRoot, keyMode, sampleRate, ...options }
339
+ : samples;
340
+ validateAnalysisInput(
341
+ 'chordFunctionalAnalysis',
342
+ request.samples,
343
+ request.sampleRate ?? 22050,
344
+ request,
345
+ );
228
346
  return requireModule().chordFunctionalAnalysis(
229
- samples,
230
- keyRoot,
231
- keyMode,
232
- sampleRate,
233
- options.minDuration ?? 0.3,
234
- options.smoothingWindow ?? 2.0,
235
- options.threshold ?? 0.5,
236
- options.useTriadsOnly ?? false,
237
- options.nFft ?? 2048,
238
- options.hopLength ?? 512,
239
- options.useBeatSync ?? true,
240
- options.useHmm ?? false,
241
- options.hmmBeamWidth ?? 24,
242
- options.useKeyContext ?? false,
243
- options.detectInversions ?? false,
244
- chordChromaMethodValue(options.chromaMethod ?? 'stft'),
347
+ request.samples,
348
+ request.keyRoot as PitchClass,
349
+ request.keyMode ?? Mode.Major,
350
+ request.sampleRate ?? 22050,
351
+ request.minDuration ?? 0.3,
352
+ request.smoothingWindow ?? 2.0,
353
+ request.threshold ?? 0.5,
354
+ request.useTriadsOnly ?? false,
355
+ request.nFft ?? 2048,
356
+ request.hopLength ?? 512,
357
+ request.useBeatSync ?? true,
358
+ request.useHmm ?? false,
359
+ request.hmmBeamWidth ?? 24,
360
+ request.useKeyContext ?? false,
361
+ request.detectInversions ?? false,
362
+ chordChromaMethodValue(request.chromaMethod ?? 'stft'),
245
363
  );
246
364
  }
247
365
 
@@ -260,43 +378,69 @@ export function chordFunctionalAnalysis(
260
378
  * the UI responsive for long inputs, drive this from a Web Worker and use
261
379
  * {@link analyzeWithProgress} to report progress.
262
380
  */
381
+ export function analyze(request: SamplesRequest): AnalysisResult;
263
382
  export function analyze(
264
383
  samples: Float32Array,
384
+ sampleRate?: number,
385
+ options?: GuardedOptions,
386
+ ): AnalysisResult;
387
+ export function analyze(
388
+ samples: Float32Array | SamplesRequest,
265
389
  sampleRate = 22050,
266
390
  options: GuardedOptions = {},
267
391
  ): AnalysisResult {
268
- validateAnalysisInput('analyze', samples, sampleRate, options);
269
- const result = requireModule().analyze(samples, sampleRate);
392
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
393
+ validateAnalysisInput('analyze', request.samples, request.sampleRate ?? 22050, request);
394
+ const result = requireModule().analyze(request.samples, request.sampleRate ?? 22050);
270
395
  return convertAnalysisResult(result);
271
396
  }
272
397
 
398
+ export function analyzeImpulseResponse(request: AnalyzeImpulseResponseRequest): AcousticResult;
273
399
  export function analyzeImpulseResponse(
274
400
  samples: Float32Array,
401
+ sampleRate?: number,
402
+ nOctaveBands?: number,
403
+ ): AcousticResult;
404
+ export function analyzeImpulseResponse(
405
+ samples: Float32Array | AnalyzeImpulseResponseRequest,
275
406
  sampleRate = 48000,
276
407
  nOctaveBands = 6,
277
408
  ): AcousticResult {
278
- validateAnalysisInput('analyzeImpulseResponse', samples, sampleRate);
409
+ const request = samples instanceof Float32Array ? { samples, sampleRate, nOctaveBands } : samples;
410
+ validateAnalysisInput(
411
+ 'analyzeImpulseResponse',
412
+ request.samples,
413
+ request.sampleRate ?? 48000,
414
+ request,
415
+ );
279
416
  const result: WasmAcousticResult = requireModule().analyzeImpulseResponse(
280
- samples,
281
- sampleRate,
282
- nOctaveBands,
417
+ request.samples,
418
+ request.sampleRate ?? 48000,
419
+ request.nOctaveBands ?? 6,
283
420
  );
284
421
  return result;
285
422
  }
286
423
 
424
+ export function detectAcoustic(request: DetectAcousticRequest): AcousticResult;
287
425
  export function detectAcoustic(
288
426
  samples: Float32Array,
427
+ sampleRate?: number,
428
+ options?: AcousticOptions,
429
+ ): AcousticResult;
430
+ export function detectAcoustic(
431
+ samples: Float32Array | DetectAcousticRequest,
289
432
  sampleRate = 48000,
290
433
  options: AcousticOptions = {},
291
434
  ): AcousticResult {
292
- validateAnalysisInput('detectAcoustic', samples, sampleRate);
435
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
436
+ validateAnalysisInput('detectAcoustic', request.samples, request.sampleRate ?? 48000, request);
293
437
  const result: WasmAcousticResult = requireModule().detectAcoustic(
294
- samples,
295
- sampleRate,
296
- options.nOctaveBands ?? 6,
297
- options.nThirdOctaveSubbands ?? 24,
298
- options.minDecayDb ?? 30.0,
299
- options.noiseFloorMarginDb ?? 10.0,
438
+ request.samples,
439
+ request.sampleRate ?? 48000,
440
+ request.nOctaveBands ?? 6,
441
+ request.nThirdOctaveSubbands ?? 24,
442
+ request.minDecayDb ?? 30.0,
443
+ request.noiseFloorMarginDb ?? 10.0,
300
444
  );
301
445
  return result;
302
446
  }
@@ -317,8 +461,14 @@ export function synthesizeRir(options: RirSynthOptions = {}): RirResult {
317
461
  * Estimate an equivalent room (volume/dimensions/absorption/DRR) from a
318
462
  * recording or impulse response.
319
463
  */
464
+ export function estimateRoom(request: EstimateRoomRequest): RoomEstimateResult;
320
465
  export function estimateRoom(
321
466
  samples: Float32Array,
467
+ sampleRate?: number,
468
+ options?: RoomEstimateOptions,
469
+ ): RoomEstimateResult;
470
+ export function estimateRoom(
471
+ samples: Float32Array | EstimateRoomRequest,
322
472
  sampleRate = 48000,
323
473
  options: RoomEstimateOptions = {},
324
474
  ): RoomEstimateResult {
@@ -326,8 +476,9 @@ export function estimateRoom(
326
476
  if (typeof module.estimateRoom !== 'function') {
327
477
  throw new Error('libsonare was built without acoustic-simulation support');
328
478
  }
329
- validateAnalysisInput('estimateRoom', samples, sampleRate);
330
- return module.estimateRoom(samples, sampleRate, options);
479
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
480
+ validateAnalysisInput('estimateRoom', request.samples, request.sampleRate ?? 48000, request);
481
+ return module.estimateRoom(request.samples, request.sampleRate ?? 48000, request);
331
482
  }
332
483
 
333
484
  /**
@@ -335,17 +486,27 @@ export function estimateRoom(
335
486
  * dereverberation). Returns the morphed samples (input length plus the target
336
487
  * room's reverb tail).
337
488
  */
489
+ export function roomMorph(request: RoomMorphRequest): Float32Array;
338
490
  export function roomMorph(
339
491
  samples: Float32Array,
340
492
  sampleRate: number,
493
+ options?: RoomMorphOptions,
494
+ ): Float32Array;
495
+ export function roomMorph(
496
+ samples: Float32Array | RoomMorphRequest,
497
+ sampleRate?: number,
341
498
  options: RoomMorphOptions = {},
342
499
  ): Float32Array {
343
500
  const module = requireModule();
344
501
  if (typeof module.roomMorph !== 'function') {
345
502
  throw new Error('libsonare was built without acoustic-simulation support');
346
503
  }
347
- validateAnalysisInput('roomMorph', samples, sampleRate);
348
- return module.roomMorph(samples, sampleRate, options);
504
+ const request =
505
+ samples instanceof Float32Array
506
+ ? { samples, sampleRate: sampleRate as number, ...options }
507
+ : samples;
508
+ validateAnalysisInput('roomMorph', request.samples, request.sampleRate, request);
509
+ return module.roomMorph(request.samples, request.sampleRate, request);
349
510
  }
350
511
 
351
512
  /**
@@ -356,13 +517,32 @@ export function roomMorph(
356
517
  * @param onProgress - Progress callback (progress: 0-1, stage: string)
357
518
  * @returns Complete analysis result
358
519
  */
520
+ export function analyzeWithProgress(request: AnalyzeWithProgressRequest): AnalysisResult;
359
521
  export function analyzeWithProgress(
360
522
  samples: Float32Array,
361
- sampleRate = 22050,
523
+ sampleRate: number | undefined,
362
524
  onProgress: ProgressCallback,
525
+ ): AnalysisResult;
526
+ export function analyzeWithProgress(
527
+ samples: Float32Array | AnalyzeWithProgressRequest,
528
+ sampleRate = 22050,
529
+ onProgress?: ProgressCallback,
363
530
  ): AnalysisResult {
364
- validateAnalysisInput('analyzeWithProgress', samples, sampleRate);
365
- const result = requireModule().analyzeWithProgress(samples, sampleRate, onProgress);
531
+ const request: AnalyzeWithProgressRequest =
532
+ samples instanceof Float32Array
533
+ ? { samples, sampleRate, onProgress: onProgress as ProgressCallback }
534
+ : samples;
535
+ validateAnalysisInput(
536
+ 'analyzeWithProgress',
537
+ request.samples,
538
+ request.sampleRate ?? 22050,
539
+ request,
540
+ );
541
+ const result = requireModule().analyzeWithProgress(
542
+ request.samples,
543
+ request.sampleRate ?? 22050,
544
+ request.onProgress,
545
+ );
366
546
  return convertAnalysisResult(result);
367
547
  }
368
548
 
@@ -423,60 +603,81 @@ export interface TimbreAnalysisResult extends TimbreFrame {
423
603
  * Detailed BPM analysis (BPM, confidence, alternate candidates, autocorrelation,
424
604
  * tempogram). Matches the Node `analyzeBpm` / Python `analyze_bpm` surface.
425
605
  */
606
+ export function analyzeBpm(request: AnalyzeBpmRequest): BpmAnalysisResult;
426
607
  export function analyzeBpm(
427
608
  samples: Float32Array,
609
+ sampleRate?: number,
610
+ options?: AnalyzeBpmOptions,
611
+ ): BpmAnalysisResult;
612
+ export function analyzeBpm(
613
+ samples: Float32Array | AnalyzeBpmRequest,
428
614
  sampleRate = 22050,
429
615
  options: AnalyzeBpmOptions = {},
430
616
  ): BpmAnalysisResult {
431
- validateAnalysisInput('analyzeBpm', samples, sampleRate, options);
432
- assertNonNegativeInteger('analyzeBpm', options.maxCandidates ?? 5, 'maxCandidates');
617
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
618
+ validateAnalysisInput('analyzeBpm', request.samples, request.sampleRate ?? 22050, request);
619
+ assertNonNegativeInteger('analyzeBpm', request.maxCandidates ?? 5, 'maxCandidates');
433
620
  return requireModule().analyzeBpm(
434
- samples,
435
- sampleRate,
436
- options.bpmMin ?? 30.0,
437
- options.bpmMax ?? 300.0,
438
- options.startBpm ?? 120.0,
439
- options.nFft ?? 2048,
440
- options.hopLength ?? 512,
441
- options.maxCandidates ?? 5,
621
+ request.samples,
622
+ request.sampleRate ?? 22050,
623
+ request.bpmMin ?? 30.0,
624
+ request.bpmMax ?? 300.0,
625
+ request.startBpm ?? 120.0,
626
+ request.nFft ?? 2048,
627
+ request.hopLength ?? 512,
628
+ request.maxCandidates ?? 5,
442
629
  );
443
630
  }
444
631
 
445
632
  /**
446
633
  * Detailed rhythm analysis (time signature, groove, syncopation, beat intervals).
447
634
  */
635
+ export function analyzeRhythm(request: AnalyzeRhythmRequest): RhythmAnalysisResult;
448
636
  export function analyzeRhythm(
449
637
  samples: Float32Array,
638
+ sampleRate?: number,
639
+ options?: AnalyzeRhythmOptions,
640
+ ): RhythmAnalysisResult;
641
+ export function analyzeRhythm(
642
+ samples: Float32Array | AnalyzeRhythmRequest,
450
643
  sampleRate = 22050,
451
644
  options: AnalyzeRhythmOptions = {},
452
645
  ): RhythmAnalysisResult {
453
- validateAnalysisInput('analyzeRhythm', samples, sampleRate, options);
646
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
647
+ validateAnalysisInput('analyzeRhythm', request.samples, request.sampleRate ?? 22050, request);
454
648
  return requireModule().analyzeRhythm(
455
- samples,
456
- sampleRate,
457
- options.bpmMin ?? 60.0,
458
- options.bpmMax ?? 200.0,
459
- options.startBpm ?? 120.0,
460
- options.nFft ?? 2048,
461
- options.hopLength ?? 512,
649
+ request.samples,
650
+ request.sampleRate ?? 22050,
651
+ request.bpmMin ?? 60.0,
652
+ request.bpmMax ?? 200.0,
653
+ request.startBpm ?? 120.0,
654
+ request.nFft ?? 2048,
655
+ request.hopLength ?? 512,
462
656
  );
463
657
  }
464
658
 
465
659
  /**
466
660
  * Dynamics analysis (RMS, peak, crest factor, LRA, loudness curve).
467
661
  */
662
+ export function analyzeDynamics(request: AnalyzeDynamicsRequest): DynamicsAnalysisResult;
468
663
  export function analyzeDynamics(
469
664
  samples: Float32Array,
665
+ sampleRate?: number,
666
+ options?: AnalyzeDynamicsOptions,
667
+ ): DynamicsAnalysisResult;
668
+ export function analyzeDynamics(
669
+ samples: Float32Array | AnalyzeDynamicsRequest,
470
670
  sampleRate = 22050,
471
671
  options: AnalyzeDynamicsOptions = {},
472
672
  ): DynamicsAnalysisResult {
473
- validateAnalysisInput('analyzeDynamics', samples, sampleRate, options);
673
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
674
+ validateAnalysisInput('analyzeDynamics', request.samples, request.sampleRate ?? 22050, request);
474
675
  return requireModule().analyzeDynamics(
475
- samples,
476
- sampleRate,
477
- options.windowSec ?? 0.4,
478
- options.hopLength ?? 512,
479
- options.compressionThreshold ?? 6.0,
676
+ request.samples,
677
+ request.sampleRate ?? 22050,
678
+ request.windowSec ?? 0.4,
679
+ request.hopLength ?? 512,
680
+ request.compressionThreshold ?? 6.0,
480
681
  );
481
682
  }
482
683
 
@@ -484,20 +685,27 @@ export function analyzeDynamics(
484
685
  * Timbre analysis (brightness/warmth/density/roughness/complexity plus spectral
485
686
  * features and per-window timbre frames).
486
687
  */
688
+ export function analyzeTimbre(request: AnalyzeTimbreRequest): TimbreAnalysisResult;
487
689
  export function analyzeTimbre(
488
690
  samples: Float32Array,
691
+ sampleRate?: number,
692
+ options?: AnalyzeTimbreOptions,
693
+ ): TimbreAnalysisResult;
694
+ export function analyzeTimbre(
695
+ samples: Float32Array | AnalyzeTimbreRequest,
489
696
  sampleRate = 22050,
490
697
  options: AnalyzeTimbreOptions = {},
491
698
  ): TimbreAnalysisResult {
492
- validateAnalysisInput('analyzeTimbre', samples, sampleRate, options);
699
+ const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
700
+ validateAnalysisInput('analyzeTimbre', request.samples, request.sampleRate ?? 22050, request);
493
701
  return requireModule().analyzeTimbre(
494
- samples,
495
- sampleRate,
496
- options.nFft ?? 2048,
497
- options.hopLength ?? 512,
498
- options.nMels ?? 128,
499
- options.nMfcc ?? 13,
500
- options.windowSec ?? 0.5,
702
+ request.samples,
703
+ request.sampleRate ?? 22050,
704
+ request.nFft ?? 2048,
705
+ request.hopLength ?? 512,
706
+ request.nMels ?? 128,
707
+ request.nMfcc ?? 13,
708
+ request.windowSec ?? 0.5,
501
709
  );
502
710
  }
503
711