@octoseq/mir 0.1.0-main.4ecb074 → 0.1.0-main.5fdb072

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 (47) hide show
  1. package/dist/chunk-HF3QHCRK.js +3234 -0
  2. package/dist/chunk-HF3QHCRK.js.map +1 -0
  3. package/dist/index.d.ts +2122 -47
  4. package/dist/index.js +3524 -39
  5. package/dist/index.js.map +1 -1
  6. package/dist/runMir-CnJQbBr8.d.ts +187 -0
  7. package/dist/runner/runMir.d.ts +2 -2
  8. package/dist/runner/runMir.js +1 -1
  9. package/dist/runner/workerProtocol.d.ts +8 -1
  10. package/dist/runner/workerProtocol.js.map +1 -1
  11. package/dist/types-DqH4umN8.d.ts +761 -0
  12. package/package.json +2 -2
  13. package/src/dsp/activity.ts +544 -0
  14. package/src/dsp/bandCqt.ts +662 -0
  15. package/src/dsp/bandEvents.ts +351 -0
  16. package/src/dsp/bandMask.ts +225 -0
  17. package/src/dsp/bandMir.ts +524 -0
  18. package/src/dsp/bandProposal.ts +552 -0
  19. package/src/dsp/beatCandidates.ts +299 -0
  20. package/src/dsp/cqt.ts +386 -0
  21. package/src/dsp/cqtSignals.ts +462 -0
  22. package/src/dsp/customSignalReduction.ts +841 -0
  23. package/src/dsp/eventToSignal.ts +531 -0
  24. package/src/dsp/frequencyBand.ts +956 -0
  25. package/src/dsp/mel.ts +56 -3
  26. package/src/dsp/musicalTime.ts +240 -0
  27. package/src/dsp/onset.ts +296 -30
  28. package/src/dsp/peakPicking.ts +519 -0
  29. package/src/dsp/phaseAlignment.ts +153 -0
  30. package/src/dsp/pitch.ts +289 -0
  31. package/src/dsp/resample.ts +44 -0
  32. package/src/dsp/signalTransforms.ts +660 -0
  33. package/src/dsp/silenceGating.ts +511 -0
  34. package/src/dsp/spectral.ts +124 -4
  35. package/src/dsp/tempoHypotheses.ts +395 -0
  36. package/src/gpu/bufferPool.ts +266 -0
  37. package/src/gpu/context.ts +30 -6
  38. package/src/gpu/helpers.ts +85 -0
  39. package/src/gpu/melProject.ts +83 -0
  40. package/src/index.ts +366 -5
  41. package/src/runner/runMir.ts +234 -2
  42. package/src/runner/workerProtocol.ts +9 -1
  43. package/src/types.ts +768 -3
  44. package/dist/chunk-DUWYCAVG.js +0 -1525
  45. package/dist/chunk-DUWYCAVG.js.map +0 -1
  46. package/dist/runMir-CSIBwNZ3.d.ts +0 -84
  47. package/dist/types-BE3py4fZ.d.ts +0 -83
package/dist/index.d.ts CHANGED
@@ -1,23 +1,7 @@
1
- export { c as Mir1DResult, d as Mir2DResult, h as MirAudioPayload, M as MirBackend, f as MirFunctionId, e as MirResult, b as MirRunMeta, g as MirRunRequest, a as MirRunTimings } from './types-BE3py4fZ.js';
2
- import { S as Spectrogram, M as MirGPU } from './runMir-CSIBwNZ3.js';
3
- export { a as SpectrogramConfig, r as runMir, s as spectrogram } from './runMir-CSIBwNZ3.js';
4
-
5
- /**
6
- * Spectral centroid per frame (Hz).
7
- *
8
- * Output is aligned 1:1 with `spec.times`.
9
- */
10
- declare function spectralCentroid(spec: Spectrogram): Float32Array;
11
- /**
12
- * Spectral flux per frame (unitless).
13
- *
14
- * Definition used here:
15
- * - L1 distance between successive *normalised* magnitude spectra.
16
- * - First frame flux is 0.
17
- *
18
- * Output is aligned 1:1 with `spec.times`.
19
- */
20
- declare function spectralFlux(spec: Spectrogram): Float32Array;
1
+ import { B as BeatCandidate, T as TempoHypothesis, P as PhaseAlignmentConfig, a as PhaseHypothesis, M as MusicalTimeSegment, b as BeatPosition, c as MusicalTimeStructure, d as BeatGrid, F as FrequencySegment, e as FrequencyBand, f as FrequencyBandStructure, g as FrequencyBoundsAtTime, h as FrequencyKeyframe, i as BandMirFunctionId, j as BandMir1DResult, k as BandEventFunctionId, l as BandEventsResult, C as CqtConfig, m as BandCqtFunctionId, n as BandCqt1DResult, o as CqtSpectrogram, p as MirRunMeta, q as CqtSignalResult, r as CqtSignalId, s as BandProposalConfig, t as BandProposalResult } from './types-DqH4umN8.js';
2
+ export { S as BandEventDiagnostics, Q as BandMirDiagnostics, R as BandMirEvent, V as BandProposal, U as BandProposalSource, H as BeatCandidateSource, I as BeatCandidatesResult, O as FrequencyBandProvenance, N as FrequencyBandTimeScope, w as Mir1DResult, x as Mir2DResult, y as MirActivityDiagnostics, z as MirActivityResult, G as MirAudioPayload, u as MirBackend, D as MirFunctionId, A as MirResult, E as MirRunRequest, v as MirRunTimings, L as MusicalTimeProvenance, K as TempoHypothesesResult, J as TempoHypothesisEvidence } from './types-DqH4umN8.js';
3
+ import { S as Spectrogram, M as MirGPU, A as AudioBufferLike$1 } from './runMir-CnJQbBr8.js';
4
+ export { a as SpectrogramConfig, r as runMir, s as spectrogram } from './runMir-CnJQbBr8.js';
21
5
 
22
6
  type MelConfig = {
23
7
  nMels: number;
@@ -32,6 +16,42 @@ type MelSpectrogram = {
32
16
  gpuSubmitToReadbackMs: number;
33
17
  };
34
18
  };
19
+ /**
20
+ * Convert frequency in Hz to mel scale.
21
+ * Uses Slaney-style (HTK-like) approximation.
22
+ */
23
+ declare function hzToMel(hz: number): number;
24
+ /**
25
+ * Convert mel scale value to frequency in Hz.
26
+ * Inverse of hzToMel.
27
+ */
28
+ declare function melToHz(mel: number): number;
29
+ /**
30
+ * Configuration for mel frequency conversions.
31
+ */
32
+ type MelConversionConfig = {
33
+ nMels: number;
34
+ fMin: number;
35
+ fMax: number;
36
+ };
37
+ /**
38
+ * Convert a frequency in Hz to a feature index (0 to nMels-1).
39
+ * The mapping is linear in mel space.
40
+ *
41
+ * @param hz - Frequency in Hz
42
+ * @param config - Mel configuration with nMels, fMin, fMax
43
+ * @returns Feature index as a continuous value (may be fractional)
44
+ */
45
+ declare function hzToFeatureIndex(hz: number, config: MelConversionConfig): number;
46
+ /**
47
+ * Convert a feature index (0 to nMels-1) to frequency in Hz.
48
+ * Inverse of hzToFeatureIndex.
49
+ *
50
+ * @param index - Feature index (can be fractional)
51
+ * @param config - Mel configuration with nMels, fMin, fMax
52
+ * @returns Frequency in Hz
53
+ */
54
+ declare function featureIndexToHz(index: number, config: MelConversionConfig): number;
35
55
  /**
36
56
  * Compute a (log) mel spectrogram by projecting an existing spectrogram.
37
57
  *
@@ -41,10 +61,437 @@ type MelSpectrogram = {
41
61
  */
42
62
  declare function melSpectrogram(spec: Spectrogram, config: MelConfig, gpu?: MirGPU): Promise<MelSpectrogram>;
43
63
 
64
+ /**
65
+ * Activity Signal - First-class MIR feature for audibility detection.
66
+ *
67
+ * The Activity Signal represents whether audible content is present at each frame.
68
+ * It is the foundation for silence-aware MIR processing across all features.
69
+ *
70
+ * Design Principles:
71
+ * 1. Silence is a distinct state, not "low energy sound"
72
+ * 2. Activity detection is shared, not reimplemented per feature
73
+ * 3. Activity is computed from whole-track context (adaptive thresholds)
74
+ * 4. The signal is inspectable and visualizable
75
+ *
76
+ * Semantics:
77
+ * - activity_level: 0 = silence/inaudible, 1 = confidently audible
78
+ * - is_active: boolean derived from activity_level
79
+ */
80
+
81
+ /**
82
+ * Configuration for activity detection.
83
+ */
84
+ type ActivityConfig = {
85
+ /**
86
+ * Percentile (0-100) used to estimate the noise floor from frame energies.
87
+ * Lower values are more conservative (estimate floor from quieter frames).
88
+ * @default 10
89
+ */
90
+ energyPercentile?: number;
91
+ /**
92
+ * Margin above noise floor to enter active state.
93
+ * For log-scale energies (mel), this is effectively dB.
94
+ * @default 6
95
+ */
96
+ enterMargin?: number;
97
+ /**
98
+ * Margin above noise floor to remain in active state.
99
+ * Must be less than enterMargin for proper hysteresis.
100
+ * @default 3
101
+ */
102
+ exitMargin?: number;
103
+ /**
104
+ * Duration in milliseconds to remain active after energy drops below exit threshold.
105
+ * Prevents rapid toggling near silence.
106
+ * @default 50
107
+ */
108
+ hangoverMs?: number;
109
+ /**
110
+ * Minimum duration in milliseconds a region must be active.
111
+ * Regions shorter than this are marked inactive.
112
+ * @default 30
113
+ */
114
+ minActiveMs?: number;
115
+ /**
116
+ * Smoothing window in milliseconds for activity_level output.
117
+ * Produces smoother transitions. 0 = no smoothing.
118
+ * @default 20
119
+ */
120
+ smoothMs?: number;
121
+ };
122
+ /**
123
+ * Activity Signal result - the main output type.
124
+ */
125
+ type ActivitySignal = {
126
+ /** Frame times in seconds, aligned with source spectrogram/mel. */
127
+ times: Float32Array;
128
+ /**
129
+ * Continuous activity level in range [0, 1].
130
+ * 0 = silence, 1 = confidently active.
131
+ * Intermediate values occur at transitions and represent uncertainty.
132
+ */
133
+ activityLevel: Float32Array;
134
+ /**
135
+ * Binary activity mask (0 = inactive, 1 = active).
136
+ * Derived from the underlying hysteresis state machine.
137
+ */
138
+ isActive: Uint8Array;
139
+ /**
140
+ * Per-frame suppression mask (0 = allow, 1 = suppress).
141
+ * Used to suppress features immediately after silence-to-active transitions.
142
+ */
143
+ suppressMask: Uint8Array;
144
+ /** Diagnostic information for debugging and visualization. */
145
+ diagnostics: ActivityDiagnostics;
146
+ };
147
+ /**
148
+ * Diagnostics for inspecting activity detection behavior.
149
+ */
150
+ type ActivityDiagnostics = {
151
+ /** Per-frame energy values used for detection. */
152
+ frameEnergy: Float32Array;
153
+ /** Estimated noise floor value. */
154
+ noiseFloor: number;
155
+ /** Threshold for entering active state. */
156
+ enterThreshold: number;
157
+ /** Threshold for exiting active state. */
158
+ exitThreshold: number;
159
+ };
160
+ /**
161
+ * Compute Activity Signal from mel spectrogram.
162
+ *
163
+ * This is the primary entry point for activity detection.
164
+ * Mel spectrograms are preferred because their log-scale energies
165
+ * provide more perceptually meaningful thresholds.
166
+ *
167
+ * @param mel - Mel spectrogram (log-scale energies)
168
+ * @param config - Activity detection configuration
169
+ * @returns Complete activity signal with diagnostics
170
+ */
171
+ declare function computeActivityFromMel(mel: MelSpectrogram, config?: ActivityConfig): ActivitySignal;
172
+ /**
173
+ * Compute Activity Signal from linear magnitude spectrogram.
174
+ *
175
+ * This function first converts magnitudes to log scale for threshold computation.
176
+ *
177
+ * @param spec - Linear magnitude spectrogram
178
+ * @param config - Activity detection configuration
179
+ * @returns Complete activity signal with diagnostics
180
+ */
181
+ declare function computeActivityFromSpectrogram(spec: Spectrogram, config?: ActivityConfig): ActivitySignal;
182
+ /**
183
+ * Compute Activity Signal from raw audio samples.
184
+ *
185
+ * Uses a simple RMS-based energy computation. This is useful when
186
+ * no spectrogram is available, but mel-based detection is preferred.
187
+ *
188
+ * @param samples - Mono audio samples
189
+ * @param sampleRate - Sample rate of the audio
190
+ * @param hopSize - Analysis hop size in samples
191
+ * @param windowSize - Analysis window size in samples
192
+ * @param config - Activity detection configuration
193
+ * @returns Complete activity signal with diagnostics
194
+ */
195
+ declare function computeActivityFromAudio(samples: Float32Array, sampleRate: number, hopSize: number, windowSize: number, config?: ActivityConfig): ActivitySignal;
196
+ /**
197
+ * Apply activity gating to a 1D signal.
198
+ *
199
+ * Zeros out values where activity is low.
200
+ *
201
+ * @param values - Signal values to gate (modified in place)
202
+ * @param activity - Activity signal
203
+ * @param options - Gating options
204
+ */
205
+ declare function applyActivityGating(values: Float32Array, activity: ActivitySignal, options?: {
206
+ /** Use binary mask instead of continuous level. Default: true */
207
+ useBinaryMask?: boolean;
208
+ /** Also apply post-silence suppression. Default: true */
209
+ suppressPostSilence?: boolean;
210
+ /** Threshold for activity_level when not using binary mask. Default: 0.5 */
211
+ levelThreshold?: number;
212
+ }): void;
213
+ /**
214
+ * Interpolate activity signal to a different time grid.
215
+ *
216
+ * Useful when you have activity computed at one resolution (e.g., mel frames)
217
+ * but need it at another resolution (e.g., pitch frames).
218
+ *
219
+ * @param activity - Source activity signal
220
+ * @param targetTimes - Target time grid
221
+ * @returns Activity signal interpolated to target times
222
+ */
223
+ declare function interpolateActivity(activity: ActivitySignal, targetTimes: Float32Array): ActivitySignal;
224
+
225
+ /**
226
+ * Options for activity-aware spectral features.
227
+ */
228
+ type SpectralActivityOptions = {
229
+ /**
230
+ * Activity signal to use for gating.
231
+ * If provided, feature values will be zeroed in inactive regions.
232
+ */
233
+ activity?: ActivitySignal;
234
+ /**
235
+ * Behavior for inactive frames.
236
+ * - "zero": Output 0 (default)
237
+ * - "hold": Hold the last active value
238
+ */
239
+ inactiveBehavior?: "zero" | "hold";
240
+ };
241
+ type AmplitudeEnvelopeConfig = {
242
+ /** Hop size in samples (determines time resolution). Default: 512 */
243
+ hopSize?: number;
244
+ /** Window size for RMS calculation. Default: same as hopSize */
245
+ windowSize?: number;
246
+ };
247
+ type AmplitudeEnvelopeResult = {
248
+ times: Float32Array;
249
+ values: Float32Array;
250
+ };
251
+ /**
252
+ * Amplitude envelope from raw audio samples.
253
+ *
254
+ * Computes RMS amplitude over windows of the time-domain signal.
255
+ * More efficient than spectrogram-based computation for full spectrum.
256
+ *
257
+ * @param samples - Mono audio samples
258
+ * @param sampleRate - Sample rate of the audio
259
+ * @param config - Configuration options
260
+ * @returns Times (seconds) and RMS amplitude values
261
+ */
262
+ declare function amplitudeEnvelope(samples: Float32Array, sampleRate: number, config?: AmplitudeEnvelopeConfig): AmplitudeEnvelopeResult;
263
+ /**
264
+ * Spectral centroid per frame (Hz).
265
+ *
266
+ * Output is aligned 1:1 with `spec.times`.
267
+ *
268
+ * When activity gating is enabled:
269
+ * - Inactive frames output 0 (or hold last valid value if configured)
270
+ * - This prevents noise-induced centroid values during silence
271
+ *
272
+ * @param spec - Input spectrogram
273
+ * @param options - Activity gating options
274
+ */
275
+ declare function spectralCentroid(spec: Spectrogram, options?: SpectralActivityOptions): Float32Array;
276
+ /**
277
+ * Spectral flux per frame (unitless).
278
+ *
279
+ * Definition used here:
280
+ * - L1 distance between successive *normalised* magnitude spectra.
281
+ * - First frame flux is 0.
282
+ *
283
+ * Output is aligned 1:1 with `spec.times`.
284
+ *
285
+ * When activity gating is enabled:
286
+ * - Inactive frames output 0
287
+ * - Post-silence suppression is applied (first active frames after silence are zeroed)
288
+ * - This prevents false flux spikes at silence/sound boundaries
289
+ *
290
+ * @param spec - Input spectrogram
291
+ * @param options - Activity gating options
292
+ */
293
+ declare function spectralFlux(spec: Spectrogram, options?: SpectralActivityOptions): Float32Array;
294
+
295
+ /**
296
+ * Silence-aware gating for onset detection.
297
+ *
298
+ * This module implements a production-grade silence detection and activity masking
299
+ * pipeline. Silence is treated as a first-class state, not "low energy sound".
300
+ *
301
+ * Pipeline stages:
302
+ * 1. Frame-level energy computation
303
+ * 2. Adaptive noise floor estimation (percentile-based)
304
+ * 3. Activity mask with hysteresis and hangover
305
+ * 4. Post-silence suppression window
306
+ *
307
+ * Design principles:
308
+ * - No fixed absolute thresholds - all thresholds are relative to estimated noise floor
309
+ * - Hysteresis prevents oscillation at threshold boundaries
310
+ * - Hangover prevents premature exit from active state
311
+ * - Post-silence suppression prevents the first audible frame from being an onset
312
+ */
313
+ /**
314
+ * Configuration for silence-aware gating.
315
+ */
316
+ type SilenceGateConfig = {
317
+ /**
318
+ * Whether gating is enabled. If false, all frames are considered active.
319
+ * @default true
320
+ */
321
+ enabled?: boolean;
322
+ /**
323
+ * Percentile (0-100) used to estimate the noise floor from frame energies.
324
+ * Lower values are more conservative (estimate floor from quieter frames).
325
+ * @default 10
326
+ */
327
+ energyPercentile?: number;
328
+ /**
329
+ * Margin above noise floor (in the same units as frame energy) to enter active state.
330
+ * For log-scale energies (mel), this is effectively dB.
331
+ * @default 6 (approximately 6 dB above floor for mel-based energy)
332
+ */
333
+ enterMargin?: number;
334
+ /**
335
+ * Margin above noise floor to remain in active state.
336
+ * Must be less than enterMargin for proper hysteresis.
337
+ * @default 3 (approximately 3 dB above floor)
338
+ */
339
+ exitMargin?: number;
340
+ /**
341
+ * Duration in milliseconds to remain active after energy drops below exit threshold.
342
+ * Prevents premature exit during brief dips.
343
+ * @default 50
344
+ */
345
+ hangoverMs?: number;
346
+ /**
347
+ * Minimum duration in milliseconds a region must be active before allowing onsets.
348
+ * @default 0
349
+ */
350
+ minActiveMs?: number;
351
+ /**
352
+ * Duration in milliseconds after transitioning from inactive to active
353
+ * during which onsets are suppressed.
354
+ * Prevents the first audible frame from being detected as an onset.
355
+ * @default 50
356
+ */
357
+ postSilenceSuppressMs?: number;
358
+ };
359
+ /**
360
+ * Configuration for bin-level gating (CPU paths only).
361
+ * Ignores bins whose energy is below a relative floor.
362
+ */
363
+ type BinGateConfig = {
364
+ /**
365
+ * Whether bin-level gating is enabled.
366
+ * @default true
367
+ */
368
+ enabled?: boolean;
369
+ /**
370
+ * Relative floor as a fraction of frame energy.
371
+ * Bins with value < frameEnergy * binFloorRel are ignored.
372
+ * @default 0.05
373
+ */
374
+ binFloorRel?: number;
375
+ };
376
+ /**
377
+ * Result of silence gating analysis.
378
+ * Useful for debugging and visualization.
379
+ */
380
+ type SilenceGateResult = {
381
+ /** Per-frame energy values used for gating. */
382
+ frameEnergy: Float32Array;
383
+ /** Estimated noise floor value. */
384
+ noiseFloor: number;
385
+ /** Threshold for entering active state. */
386
+ enterThreshold: number;
387
+ /** Threshold for exiting active state. */
388
+ exitThreshold: number;
389
+ /** Per-frame activity mask (true = active). */
390
+ activityMask: Uint8Array;
391
+ /** Per-frame onset suppression mask (true = suppress onset). */
392
+ suppressionMask: Uint8Array;
393
+ };
394
+ /**
395
+ * Merge user config with defaults.
396
+ */
397
+ declare function withSilenceGateDefaults(config?: SilenceGateConfig): Required<SilenceGateConfig>;
398
+ /**
399
+ * Merge user config with defaults for bin gating.
400
+ */
401
+ declare function withBinGateDefaults(config?: BinGateConfig): Required<BinGateConfig>;
402
+ /**
403
+ * Compute per-frame energy from mel spectrogram.
404
+ *
405
+ * Since mel bands are already log10(eps + energy), we compute the mean
406
+ * of the log-energy values. This gives us a perceptually-relevant
407
+ * frame energy proxy in log space.
408
+ *
409
+ * @param melBands - Array of frames, each containing mel band values (log10 scale)
410
+ * @returns Per-frame energy values (log scale)
411
+ */
412
+ declare function computeFrameEnergyFromMel(melBands: Float32Array[]): Float32Array;
413
+ /**
414
+ * Compute per-frame energy from linear magnitude spectrogram.
415
+ *
416
+ * Uses mean magnitude (or power) across bins as the energy proxy.
417
+ *
418
+ * @param magnitudes - Array of frames, each containing magnitude values (linear scale)
419
+ * @param usePower - If true, use sum of squared magnitudes (power). Default: false (RMS-like).
420
+ * @returns Per-frame energy values (linear scale)
421
+ */
422
+ declare function computeFrameEnergyFromSpectrogram(magnitudes: Float32Array[], usePower?: boolean): Float32Array;
423
+ /**
424
+ * Estimate adaptive noise floor from frame energies.
425
+ *
426
+ * Uses a robust percentile-based estimation rather than absolute thresholds.
427
+ *
428
+ * @param frameEnergy - Per-frame energy values
429
+ * @param percentile - Percentile to use for floor estimation (0-100)
430
+ * @returns Estimated noise floor
431
+ */
432
+ declare function estimateNoiseFloor(frameEnergy: Float32Array, percentile: number): number;
433
+ /**
434
+ * Build activity mask with hysteresis and hangover.
435
+ *
436
+ * State machine:
437
+ * - INACTIVE: energy < enterThreshold -> stay inactive
438
+ * - INACTIVE: energy >= enterThreshold -> enter ACTIVE
439
+ * - ACTIVE: energy >= exitThreshold -> stay active, reset hangover counter
440
+ * - ACTIVE: energy < exitThreshold -> decrement hangover counter
441
+ * - ACTIVE: hangover counter exhausted -> enter INACTIVE
442
+ *
443
+ * @param frameEnergy - Per-frame energy values
444
+ * @param enterThreshold - Threshold to enter active state
445
+ * @param exitThreshold - Threshold to remain in active state
446
+ * @param hangoverFrames - Number of frames to remain active after dropping below exit threshold
447
+ * @returns Activity mask (1 = active, 0 = inactive)
448
+ */
449
+ declare function buildActivityMask(frameEnergy: Float32Array, enterThreshold: number, exitThreshold: number, hangoverFrames: number): Uint8Array;
450
+ /**
451
+ * Full silence gating analysis.
452
+ *
453
+ * Computes frame energy, noise floor, thresholds, activity mask, and suppression mask.
454
+ *
455
+ * @param frameEnergy - Per-frame energy values
456
+ * @param frameDurationSec - Duration of one frame in seconds
457
+ * @param config - Silence gate configuration
458
+ * @returns Complete gating result including all intermediate values
459
+ */
460
+ declare function computeSilenceGating(frameEnergy: Float32Array, frameDurationSec: number, config?: SilenceGateConfig): SilenceGateResult;
461
+
44
462
  type OnsetEnvelope = {
45
463
  times: Float32Array;
46
464
  values: Float32Array;
47
465
  };
466
+ /**
467
+ * Extended onset envelope result with optional debugging info.
468
+ */
469
+ type OnsetEnvelopeResult = OnsetEnvelope & {
470
+ /**
471
+ * Optional silence gating diagnostics.
472
+ * Present when silenceGate.enabled is true and returnDiagnostics is true.
473
+ */
474
+ diagnostics?: OnsetDiagnostics;
475
+ };
476
+ /**
477
+ * Diagnostics for inspecting onset detection behavior.
478
+ */
479
+ type OnsetDiagnostics = {
480
+ /** Per-frame energy used for gating. */
481
+ frameEnergy: Float32Array;
482
+ /** Estimated noise floor. */
483
+ noiseFloor: number;
484
+ /** Threshold for entering active state. */
485
+ enterThreshold: number;
486
+ /** Threshold for exiting active state. */
487
+ exitThreshold: number;
488
+ /** Per-frame activity mask (1 = active, 0 = inactive). */
489
+ activityMask: Uint8Array;
490
+ /** Per-frame suppression mask (1 = suppressed, 0 = allowed). */
491
+ suppressionMask: Uint8Array;
492
+ /** Raw onset novelty before gating was applied. */
493
+ rawNovelty: Float32Array;
494
+ };
48
495
  type OnsetEnvelopeOptions = {
49
496
  /** If true, log-compress magnitudes/energies before differencing. */
50
497
  useLog?: boolean;
@@ -52,45 +499,1032 @@ type OnsetEnvelopeOptions = {
52
499
  smoothMs?: number;
53
500
  /** How to convert temporal differences into novelty. */
54
501
  diffMethod?: "rectified" | "abs";
502
+ /**
503
+ * Silence-aware gating configuration.
504
+ * Suppresses false onsets in silence or near-silence regions.
505
+ * @default { enabled: true }
506
+ */
507
+ silenceGate?: SilenceGateConfig;
508
+ /**
509
+ * Bin-level gating configuration (CPU paths only).
510
+ * Ignores bins with energy below a relative threshold.
511
+ * @default { enabled: true }
512
+ */
513
+ binGate?: BinGateConfig;
514
+ /**
515
+ * If true, include diagnostics in the result for debugging.
516
+ * @default false
517
+ */
518
+ returnDiagnostics?: boolean;
55
519
  };
56
- declare function onsetEnvelopeFromSpectrogram(spec: Spectrogram, options?: OnsetEnvelopeOptions): OnsetEnvelope;
57
- declare function onsetEnvelopeFromMel(mel: MelSpectrogram, options?: OnsetEnvelopeOptions): OnsetEnvelope;
520
+ /**
521
+ * Compute onset envelope from spectrogram with silence-aware gating.
522
+ *
523
+ * The pipeline:
524
+ * 1. Compute per-frame spectral flux (novelty)
525
+ * 2. Compute per-frame energy from magnitudes
526
+ * 3. Build activity mask using adaptive noise floor + hysteresis
527
+ * 4. Apply silence gating and post-silence suppression
528
+ * 5. Apply optional smoothing
529
+ */
530
+ declare function onsetEnvelopeFromSpectrogram(spec: Spectrogram, options?: OnsetEnvelopeOptions): OnsetEnvelopeResult;
531
+ /**
532
+ * Compute onset envelope from mel spectrogram with silence-aware gating.
533
+ *
534
+ * The pipeline:
535
+ * 1. Compute per-frame mel flux (novelty)
536
+ * 2. Compute per-frame energy from mel bands (already log scale)
537
+ * 3. Build activity mask using adaptive noise floor + hysteresis
538
+ * 4. Apply silence gating and post-silence suppression
539
+ * 5. Apply optional smoothing
540
+ */
541
+ declare function onsetEnvelopeFromMel(mel: MelSpectrogram, options?: OnsetEnvelopeOptions): OnsetEnvelopeResult;
58
542
  type OnsetEnvelopeGpuResult = {
59
543
  times: Float32Array;
60
544
  values: Float32Array;
61
545
  gpuTimings: {
62
546
  gpuSubmitToReadbackMs: number;
63
547
  };
548
+ /** Optional silence gating diagnostics. */
549
+ diagnostics?: OnsetDiagnostics;
550
+ };
551
+ /**
552
+ * GPU-accelerated onset envelope from mel spectrogram with silence-aware gating.
553
+ *
554
+ * Notes:
555
+ * - GPU kernel handles diff+reduction only (keeps WGSL simple)
556
+ * - Silence gating is applied on CPU after GPU readback
557
+ * - CPU and GPU paths produce comparable results
558
+ * - Callers should fall back to CPU on errors
559
+ */
560
+ declare function onsetEnvelopeFromMelGpu(mel: MelSpectrogram, gpu: MirGPU, options?: Pick<OnsetEnvelopeOptions, "diffMethod" | "silenceGate" | "returnDiagnostics" | "smoothMs">): Promise<OnsetEnvelopeGpuResult>;
561
+
562
+ type PeakPickEvent = {
563
+ time: number;
564
+ strength: number;
565
+ index: number;
566
+ };
567
+ type PeakPickOptions = {
568
+ /** Minimum peak height (absolute). */
569
+ threshold?: number;
570
+ /** Minimum inter-peak interval (seconds). */
571
+ minIntervalSec?: number;
572
+ /** If provided, use adaptive threshold: mean(values) + factor*std(values). */
573
+ adaptive?: {
574
+ method?: "meanStd" | "median";
575
+ factor?: number;
576
+ };
577
+ /** If true, prefer strict maxima (> neighbors); else allow flat plateaus. */
578
+ strict?: boolean;
579
+ };
580
+ declare function peakPick(times: Float32Array, values: Float32Array, options?: PeakPickOptions): PeakPickEvent[];
581
+
582
+ /**
583
+ * Configuration for beat candidate detection.
584
+ */
585
+ type BeatCandidatesOptions = {
586
+ /** Minimum inter-candidate interval in seconds. Default: 0.1 (100ms). */
587
+ minIntervalSec?: number;
588
+ /** Threshold factor for adaptive peak detection. Lower = more candidates. Default: 0.5. */
589
+ thresholdFactor?: number;
590
+ /** Smoothing window for salience signal in ms. Default: 50. */
591
+ smoothMs?: number;
592
+ };
593
+ /**
594
+ * Result of beat candidate detection.
595
+ */
596
+ type BeatCandidatesOutput = {
597
+ candidates: BeatCandidate[];
598
+ /** The computed salience signal (for debugging/visualization). */
599
+ salience: {
600
+ times: Float32Array;
601
+ values: Float32Array;
602
+ };
603
+ };
604
+ /**
605
+ * Compute a beat-oriented salience signal from mel spectrogram.
606
+ *
607
+ * This combines:
608
+ * - Onset envelope (captures transients/attacks)
609
+ * - Spectral flux from the underlying spectrogram (captures spectral change)
610
+ *
611
+ * The signals are normalized and combined to produce a single salience curve
612
+ * suitable for peak picking.
613
+ *
614
+ * Key design choices:
615
+ * - Whole-track normalization (z-score) for consistent behavior
616
+ * - Gentle smoothing to suppress micro-transients while preserving beat structure
617
+ * - No BPM inference or grid assumptions
618
+ */
619
+ type BeatSalienceSignal = {
620
+ times: Float32Array;
621
+ values: Float32Array;
622
+ };
623
+ /**
624
+ * Compute beat salience signal from mel spectrogram.
625
+ *
626
+ * This is an intermediate signal suitable for peak picking to extract
627
+ * beat candidates. It combines onset envelope with additional smoothing
628
+ * tuned for beat-like (rather than onset-like) detection.
629
+ */
630
+ declare function beatSalienceFromMel(mel: MelSpectrogram, spec: Spectrogram, options?: {
631
+ smoothMs?: number;
632
+ }): BeatSalienceSignal;
633
+ /**
634
+ * Detect beat candidates from mel spectrogram and spectrogram.
635
+ *
636
+ * This is the main entry point for beat candidate detection.
637
+ *
638
+ * Design principles:
639
+ * - Dense candidates (err on side of too many)
640
+ * - No BPM inference
641
+ * - No grid assumptions
642
+ * - Whole-track normalization for consistency
643
+ * - Deterministic (same input -> same output)
644
+ */
645
+ declare function detectBeatCandidates(mel: MelSpectrogram, spec: Spectrogram, options?: BeatCandidatesOptions): BeatCandidatesOutput;
646
+
647
+ /**
648
+ * Configuration for tempo hypothesis generation.
649
+ */
650
+ type TempoHypothesesOptions = {
651
+ /** Minimum BPM to consider. Default: 24. */
652
+ minBpm?: number;
653
+ /** Maximum BPM to consider. Default: 300. */
654
+ maxBpm?: number;
655
+ /** Histogram bin size in BPM. Default: 1.0. */
656
+ binSizeBpm?: number;
657
+ /** Maximum hypotheses to return. Default: 10. */
658
+ maxHypotheses?: number;
659
+ /** Minimum confidence threshold (0-1). Default: 0.05. */
660
+ minConfidence?: number;
661
+ /** Weight IOIs by candidate strength. Default: true. */
662
+ weightByStrength?: boolean;
663
+ /** Include histogram data in output. Default: false. */
664
+ includeHistogram?: boolean;
665
+ };
666
+ type TempoHypothesesOutput = {
667
+ hypotheses: TempoHypothesis[];
668
+ inputCandidateCount: number;
669
+ histogram?: {
670
+ bpmBins: Float32Array;
671
+ counts: Float32Array;
672
+ };
64
673
  };
65
674
  /**
66
- * GPU-accelerated onset envelope from mel spectrogram.
675
+ * Generate tempo hypotheses from beat candidates.
676
+ *
677
+ * Algorithm:
678
+ * 1. Compute inter-onset intervals (IOIs) from beat candidates
679
+ * 2. Filter IOIs to musically plausible range (0.2s-2.5s -> 24-300 BPM)
680
+ * 3. Build weighted histogram with configurable bin size
681
+ * 4. Extract peaks as tempo candidates
682
+ * 5. Refine BPM estimates using weighted centroid
683
+ * 6. Group into harmonic families
684
+ * 7. Normalize confidence scores
685
+ *
686
+ * @param candidates - Beat candidates from B1
687
+ * @param options - Configuration options
688
+ * @returns Tempo hypotheses with confidence and family groupings
689
+ */
690
+ declare function generateTempoHypotheses(candidates: BeatCandidate[], options?: TempoHypothesesOptions): TempoHypothesesOutput;
691
+
692
+ /**
693
+ * Phase alignment algorithm for beat grid generation.
694
+ *
695
+ * Given a BPM and beat candidates, this module computes optimal phase offsets
696
+ * that align the beat grid with detected beat candidates.
697
+ *
698
+ * The algorithm is deterministic: same inputs always produce same outputs.
699
+ */
700
+
701
+ /**
702
+ * Compute phase hypotheses for a given BPM against beat candidates.
703
+ *
704
+ * Algorithm:
705
+ * 1. Compute beat period = 60 / bpm
706
+ * 2. Generate N phase offsets spanning [0, period) at resolution = period/N
707
+ * 3. For each phase offset:
708
+ * a. For each beat candidate, find closest grid line
709
+ * b. If within tolerance, add weighted score based on candidate strength
710
+ * c. Track systematic offset error for penalty
711
+ * 4. Normalize scores and apply offset penalty
712
+ * 5. Return top K phases sorted by score
713
+ *
714
+ * @param bpm - Tempo in beats per minute
715
+ * @param candidates - Beat candidate events with time and strength
716
+ * @param audioDuration - Total audio duration in seconds
717
+ * @param config - Optional configuration overrides
718
+ * @returns Array of top K phase hypotheses, sorted by score descending
719
+ */
720
+ declare function computePhaseHypotheses(bpm: number, candidates: BeatCandidate[], audioDuration: number, config?: PhaseAlignmentConfig): PhaseHypothesis[];
721
+ /**
722
+ * Generate beat times for a given grid within the audio duration.
723
+ *
724
+ * @param bpm - Tempo in beats per minute
725
+ * @param phaseOffset - Base phase offset in seconds
726
+ * @param userNudge - User adjustment in seconds (additive)
727
+ * @param audioDuration - Total audio duration in seconds
728
+ * @returns Array of beat times in seconds
729
+ */
730
+ declare function generateBeatTimes(bpm: number, phaseOffset: number, userNudge: number, audioDuration: number): number[];
731
+
732
+ /**
733
+ * Peak Picking Algorithm
734
+ *
735
+ * Detects local maxima in a 1D signal that exceed a threshold.
736
+ * Used to create event streams from continuous signals.
737
+ */
738
+ interface PeakPickingParams {
739
+ /** Minimum normalized value (0-1) for a peak to be considered. Default: 0.3 */
740
+ threshold: number;
741
+ /** Minimum time between peaks in seconds. Default: 0.1 */
742
+ minDistance: number;
743
+ /** Number of samples to look back for local max comparison. Default: 2 */
744
+ preMax?: number;
745
+ /** Number of samples to look forward for local max comparison. Default: 2 */
746
+ postMax?: number;
747
+ }
748
+ interface PeakPickingResult {
749
+ /** Times of detected peaks (seconds) */
750
+ times: Float32Array;
751
+ /** Normalized strength of each peak (0-1, based on normalized value) */
752
+ strengths: Float32Array;
753
+ }
754
+ /**
755
+ * Default peak picking parameters
756
+ */
757
+ declare const DEFAULT_PEAK_PICKING_PARAMS: Required<PeakPickingParams>;
758
+ /**
759
+ * Pick peaks from a continuous signal.
760
+ *
761
+ * @param times - Time values in seconds (Float32Array)
762
+ * @param values - Signal values (Float32Array)
763
+ * @param params - Peak picking parameters
764
+ * @returns Detected peaks with times and strengths
765
+ */
766
+ declare function pickPeaks(times: Float32Array, values: Float32Array, params?: Partial<PeakPickingParams>): PeakPickingResult;
767
+ /**
768
+ * Result from adaptive peak picking, including the threshold curve for visualization.
769
+ */
770
+ interface AdaptivePeakPickingResult extends PeakPickingResult {
771
+ /** The adaptive threshold curve (normalized 0-1) for visualization */
772
+ thresholdCurve: Float32Array;
773
+ /** Time values corresponding to threshold curve samples */
774
+ thresholdTimes: Float32Array;
775
+ }
776
+ /**
777
+ * Compute the adaptive threshold curve for visualization.
778
+ * This is the normalized threshold that would be used by pickPeaksAdaptive.
779
+ *
780
+ * @param values - Signal values (Float32Array)
781
+ * @param windowSize - Window size for local statistics (in samples)
782
+ * @param thresholdMultiplier - Multiplier for local std (default 1.5)
783
+ * @returns Normalized threshold curve (0-1 range)
784
+ */
785
+ declare function computeAdaptiveThreshold(values: Float32Array, windowSize?: number, thresholdMultiplier?: number): Float32Array;
786
+ /**
787
+ * Pick peaks with adaptive threshold based on local statistics.
788
+ *
789
+ * @param times - Time values in seconds
790
+ * @param values - Signal values
791
+ * @param windowSize - Window size for local statistics (in samples)
792
+ * @param params - Base peak picking parameters (threshold used as multiplier of local std)
793
+ * @param includeThresholdCurve - If true, returns the threshold curve for visualization
794
+ */
795
+ declare function pickPeaksAdaptive(times: Float32Array, values: Float32Array, windowSize?: number, params?: Partial<PeakPickingParams>, includeThresholdCurve?: boolean): PeakPickingResult | AdaptivePeakPickingResult;
796
+ /**
797
+ * Hysteresis gate parameters for peak filtering.
798
+ */
799
+ interface HysteresisGateParams {
800
+ /** Upper threshold (0-1) - signal must exceed this to trigger "on" state */
801
+ onThreshold: number;
802
+ /** Lower threshold (0-1) - signal must fall below this to trigger "off" state */
803
+ offThreshold: number;
804
+ /** Minimum time between peaks in seconds */
805
+ minDistance: number;
806
+ }
807
+ /**
808
+ * Apply hysteresis gating to peaks.
809
+ * Peaks are only kept if the signal has dropped below offThreshold since the last peak.
810
+ * This prevents multiple triggers during sustained high-value regions.
811
+ *
812
+ * @param times - Time values of the original signal
813
+ * @param values - Values of the original signal (for checking hysteresis)
814
+ * @param peakTimes - Times of detected peaks
815
+ * @param peakStrengths - Strengths of detected peaks
816
+ * @param params - Hysteresis gate parameters
817
+ * @returns Filtered peaks after hysteresis gating
818
+ */
819
+ declare function applyHysteresisGate(times: Float32Array, values: Float32Array, peakTimes: Float32Array, peakStrengths: Float32Array, params: HysteresisGateParams): PeakPickingResult;
820
+
821
+ /**
822
+ * Musical Time utilities for B4.
823
+ *
824
+ * These functions compute derived values from musical time structures.
825
+ * All computations are deterministic and pure.
826
+ */
827
+
828
+ /**
829
+ * Find the segment containing a given time.
830
+ * Segments are non-overlapping, so at most one segment contains any time.
831
+ *
832
+ * @param time - Time in seconds from track start
833
+ * @param segments - Ordered list of segments (by startTime ascending)
834
+ * @returns The containing segment, or null if time is outside all segments
835
+ */
836
+ declare function findSegmentAtTime(time: number, segments: MusicalTimeSegment[]): MusicalTimeSegment | null;
837
+ /**
838
+ * Compute the beat position at a given time.
839
+ *
840
+ * Beat position formula:
841
+ * period = 60 / bpm
842
+ * beatsFromStart = (time - phaseOffset) / period
843
+ * beatIndex = floor(beatsFromStart)
844
+ * beatPhase = beatsFromStart - beatIndex (fractional part, 0-1)
845
+ * beatPosition = beatsFromStart (continuous)
846
+ *
847
+ * @param time - Time in seconds from track start
848
+ * @param segments - Ordered list of segments (by startTime ascending)
849
+ * @returns BeatPosition if time is within a segment, null otherwise
850
+ */
851
+ declare function computeBeatPosition(time: number, segments: MusicalTimeSegment[]): BeatPosition | null;
852
+ /**
853
+ * Compute beat position from a MusicalTimeStructure.
854
+ * Convenience wrapper for computeBeatPosition.
855
+ */
856
+ declare function computeBeatPositionFromStructure(time: number, structure: MusicalTimeStructure | null): BeatPosition | null;
857
+ /**
858
+ * Generate a unique segment ID.
859
+ */
860
+ declare function generateSegmentId(): string;
861
+ /**
862
+ * Create a new MusicalTimeSegment from a BeatGrid.
863
+ *
864
+ * @param grid - The beat grid to promote
865
+ * @param startTime - Segment start boundary in seconds
866
+ * @param endTime - Segment end boundary in seconds
867
+ * @returns A new MusicalTimeSegment
868
+ */
869
+ declare function createSegmentFromGrid(grid: BeatGrid, startTime: number, endTime: number): MusicalTimeSegment;
870
+ /**
871
+ * Create a new empty MusicalTimeStructure.
872
+ */
873
+ declare function createMusicalTimeStructure(): MusicalTimeStructure;
874
+ /**
875
+ * Validate that segments are non-overlapping and ordered.
876
+ *
877
+ * @param segments - List of segments to validate
878
+ * @returns Array of validation errors (empty if valid)
879
+ */
880
+ declare function validateSegments(segments: MusicalTimeSegment[]): string[];
881
+ /**
882
+ * Sort segments by startTime ascending.
883
+ * Returns a new array (does not mutate input).
884
+ */
885
+ declare function sortSegments(segments: MusicalTimeSegment[]): MusicalTimeSegment[];
886
+ /**
887
+ * Split a segment at a given time.
888
+ *
889
+ * @param segment - The segment to split
890
+ * @param splitTime - Time in seconds where to split
891
+ * @returns Tuple of [beforeSegment, afterSegment]
892
+ * @throws If splitTime is outside segment bounds
893
+ */
894
+ declare function splitSegment(segment: MusicalTimeSegment, splitTime: number): [MusicalTimeSegment, MusicalTimeSegment];
895
+ /**
896
+ * Generate beat times within a segment.
897
+ * Used for rendering beat markers.
898
+ *
899
+ * @param segment - The segment to generate beats for
900
+ * @returns Array of beat times in seconds
901
+ */
902
+ declare function generateSegmentBeatTimes(segment: MusicalTimeSegment): number[];
903
+
904
+ /**
905
+ * Frequency Band utilities for F1.
906
+ *
907
+ * These functions provide validation, creation, and query operations
908
+ * for frequency band structures. All computations are deterministic and pure.
909
+ */
910
+
911
+ /**
912
+ * Generate a unique band ID.
913
+ */
914
+ declare function generateBandId(): string;
915
+ /**
916
+ * Validate frequency segments for a band.
917
+ *
918
+ * Checks:
919
+ * - lowHz < highHz at all segment boundaries
920
+ * - All frequency values are non-negative
921
+ * - startTime < endTime for each segment
922
+ * - Segments don't overlap in time
923
+ * - Segments are ordered by startTime
924
+ *
925
+ * @param segments - Frequency segments to validate
926
+ * @returns Array of validation errors (empty if valid)
927
+ */
928
+ declare function validateFrequencySegments(segments: FrequencySegment[]): string[];
929
+ /**
930
+ * Validate a frequency band.
931
+ *
932
+ * @param band - Frequency band to validate
933
+ * @returns Array of validation errors (empty if valid)
934
+ */
935
+ declare function validateFrequencyBand(band: FrequencyBand): string[];
936
+ /**
937
+ * Validate a complete frequency band structure.
938
+ *
939
+ * @param structure - Structure to validate
940
+ * @returns Array of validation errors (empty if valid)
941
+ */
942
+ declare function validateBandStructure(structure: FrequencyBandStructure): string[];
943
+ /**
944
+ * Create an empty FrequencyBandStructure.
945
+ */
946
+ declare function createBandStructure(): FrequencyBandStructure;
947
+ /**
948
+ * Create a simple constant-frequency band (no time variation).
949
+ *
950
+ * @param label - Human-readable band label
951
+ * @param lowHz - Lower frequency bound in Hz
952
+ * @param highHz - Upper frequency bound in Hz
953
+ * @param duration - Duration in seconds (defines segment end time)
954
+ * @param options - Optional configuration
955
+ * @returns A new FrequencyBand
956
+ */
957
+ declare function createConstantBand(label: string, lowHz: number, highHz: number, duration: number, options?: {
958
+ enabled?: boolean;
959
+ sortOrder?: number;
960
+ id?: string;
961
+ /** The audio source this band belongs to. Defaults to "mixdown". */
962
+ sourceId?: string;
963
+ }): FrequencyBand;
964
+ /**
965
+ * Create a sectioned band (applies only to a time range).
966
+ *
967
+ * @param label - Human-readable band label
968
+ * @param lowHz - Lower frequency bound in Hz
969
+ * @param highHz - Upper frequency bound in Hz
970
+ * @param startTime - Section start time in seconds
971
+ * @param endTime - Section end time in seconds
972
+ * @param options - Optional configuration
973
+ * @returns A new FrequencyBand
974
+ */
975
+ declare function createSectionedBand(label: string, lowHz: number, highHz: number, startTime: number, endTime: number, options?: {
976
+ enabled?: boolean;
977
+ sortOrder?: number;
978
+ id?: string;
979
+ /** The audio source this band belongs to. Defaults to "mixdown". */
980
+ sourceId?: string;
981
+ }): FrequencyBand;
982
+ /**
983
+ * Create standard frequency bands for a track.
984
+ *
985
+ * Creates a standard 6-band frequency split:
986
+ * - Sub Bass: 20-60 Hz
987
+ * - Bass: 60-250 Hz
988
+ * - Low Mids: 250-500 Hz
989
+ * - Mids: 500-2000 Hz
990
+ * - High Mids: 2000-4000 Hz
991
+ * - Highs: 4000-20000 Hz
992
+ *
993
+ * @param duration - Track duration in seconds
994
+ * @param sourceId - The audio source these bands belong to. Defaults to "mixdown".
995
+ * @returns Array of FrequencyBand objects
996
+ */
997
+ declare function createStandardBands(duration: number, sourceId?: string): FrequencyBand[];
998
+ /**
999
+ * Get all bands active at a given time.
1000
+ *
1001
+ * A band is active if:
1002
+ * - It is enabled
1003
+ * - The time falls within its time scope
1004
+ *
1005
+ * @param structure - Frequency band structure (can be null)
1006
+ * @param time - Time in seconds
1007
+ * @param sourceId - Optional: filter to a specific audio source
1008
+ * @returns Array of active bands
1009
+ */
1010
+ declare function bandsActiveAt(structure: FrequencyBandStructure | null, time: number, sourceId?: string): FrequencyBand[];
1011
+ /**
1012
+ * Get frequency bounds for a band at a given time.
1013
+ *
1014
+ * Uses linear interpolation within segments.
1015
+ *
1016
+ * @param band - The frequency band to query
1017
+ * @param time - Time in seconds
1018
+ * @returns Frequency bounds if band is active and has defined bounds, null otherwise
1019
+ */
1020
+ declare function frequencyBoundsAt(band: FrequencyBand, time: number): FrequencyBoundsAtTime | null;
1021
+ /**
1022
+ * Get all frequency bounds at a given time.
1023
+ *
1024
+ * Returns bounds for all active bands that have defined frequency at the given time.
1025
+ *
1026
+ * @param structure - Frequency band structure (can be null)
1027
+ * @param time - Time in seconds
1028
+ * @returns Array of frequency bounds, sorted by sortOrder
1029
+ */
1030
+ declare function allFrequencyBoundsAt(structure: FrequencyBandStructure | null, time: number): FrequencyBoundsAtTime[];
1031
+ /**
1032
+ * Find a band by its ID.
1033
+ *
1034
+ * @param structure - Frequency band structure (can be null)
1035
+ * @param id - Band ID to find
1036
+ * @returns The band if found, null otherwise
1037
+ */
1038
+ declare function findBandById(structure: FrequencyBandStructure | null, id: string): FrequencyBand | null;
1039
+ /**
1040
+ * Sort bands by sortOrder.
1041
+ * Returns a new array (does not mutate input).
1042
+ *
1043
+ * @param bands - Bands to sort
1044
+ * @returns New array sorted by sortOrder ascending
1045
+ */
1046
+ declare function sortBands(bands: FrequencyBand[]): FrequencyBand[];
1047
+ /**
1048
+ * Sort frequency segments by startTime.
1049
+ * Returns a new array (does not mutate input).
1050
+ *
1051
+ * @param segments - Segments to sort
1052
+ * @returns New array sorted by startTime ascending
1053
+ */
1054
+ declare function sortFrequencySegments(segments: FrequencySegment[]): FrequencySegment[];
1055
+ /**
1056
+ * Update the modifiedAt timestamp of a structure.
1057
+ * Returns a new structure (does not mutate input).
1058
+ *
1059
+ * @param structure - Structure to update
1060
+ * @returns New structure with updated modifiedAt
1061
+ */
1062
+ declare function touchStructure(structure: FrequencyBandStructure): FrequencyBandStructure;
1063
+ /**
1064
+ * Add a band to a structure.
1065
+ * Returns a new structure (does not mutate input).
1066
+ *
1067
+ * @param structure - Structure to add to
1068
+ * @param band - Band to add
1069
+ * @returns New structure with the band added
1070
+ */
1071
+ declare function addBandToStructure(structure: FrequencyBandStructure, band: FrequencyBand): FrequencyBandStructure;
1072
+ /**
1073
+ * Remove a band from a structure by ID.
1074
+ * Returns a new structure (does not mutate input).
1075
+ *
1076
+ * @param structure - Structure to remove from
1077
+ * @param bandId - ID of band to remove
1078
+ * @returns New structure with the band removed
1079
+ */
1080
+ declare function removeBandFromStructure(structure: FrequencyBandStructure, bandId: string): FrequencyBandStructure;
1081
+ /**
1082
+ * Update a band in a structure.
1083
+ * Returns a new structure (does not mutate input).
1084
+ *
1085
+ * @param structure - Structure containing the band
1086
+ * @param bandId - ID of band to update
1087
+ * @param updates - Partial band updates to apply
1088
+ * @returns New structure with the band updated
1089
+ */
1090
+ declare function updateBandInStructure(structure: FrequencyBandStructure, bandId: string, updates: Partial<Omit<FrequencyBand, "id">>): FrequencyBandStructure;
1091
+ /**
1092
+ * Extract keyframes from a band's frequency shape.
1093
+ *
1094
+ * Keyframes are a UI abstraction over the segment model.
1095
+ * Each segment has a start keyframe and an end keyframe.
1096
+ * Adjacent segments share keyframes at their boundaries.
1097
+ *
1098
+ * @param band - The frequency band to extract keyframes from
1099
+ * @returns Array of keyframes sorted by time
1100
+ */
1101
+ declare function keyframesFromBand(band: FrequencyBand): FrequencyKeyframe[];
1102
+ /**
1103
+ * Convert keyframes back to frequency segments.
1104
+ *
1105
+ * Consecutive keyframes become segment boundaries.
1106
+ * This is the inverse of keyframesFromBand.
1107
+ *
1108
+ * @param keyframes - Array of keyframes (must be sorted by time)
1109
+ * @returns Array of frequency segments
1110
+ */
1111
+ declare function segmentsFromKeyframes(keyframes: FrequencyKeyframe[]): FrequencySegment[];
1112
+ /**
1113
+ * Split a band's segment at a given time, creating a new keyframe.
1114
+ *
1115
+ * The frequency values at the split point are interpolated from the
1116
+ * existing segment. Returns a new band with updated frequencyShape.
1117
+ *
1118
+ * @param band - The band to split
1119
+ * @param time - Time in seconds to split at
1120
+ * @returns New band with the split segment, or original band if time is invalid
1121
+ */
1122
+ declare function splitBandSegmentAt(band: FrequencyBand, time: number): FrequencyBand;
1123
+ /**
1124
+ * Merge adjacent segments where frequencies match at the boundary.
1125
+ *
1126
+ * This is useful after deleting a keyframe to clean up redundant segments.
1127
+ *
1128
+ * @param band - The band to merge segments in
1129
+ * @param tolerance - Frequency tolerance for considering values equal (default 0.1 Hz)
1130
+ * @returns New band with merged segments
1131
+ */
1132
+ declare function mergeAdjacentSegments(band: FrequencyBand, tolerance?: number): FrequencyBand;
1133
+ /**
1134
+ * Remove a keyframe from a band.
1135
+ *
1136
+ * This merges the two adjacent segments into one.
1137
+ * Cannot remove first or last keyframe (would make band invalid).
1138
+ *
1139
+ * @param band - The band to modify
1140
+ * @param time - Time of the keyframe to remove
1141
+ * @returns New band with keyframe removed, or original if removal is invalid
1142
+ */
1143
+ declare function removeKeyframe(band: FrequencyBand, time: number): FrequencyBand;
1144
+ /**
1145
+ * Update a keyframe's frequency values.
1146
+ *
1147
+ * This updates the corresponding segment boundary.
1148
+ *
1149
+ * @param band - The band to modify
1150
+ * @param time - Time of the keyframe to update
1151
+ * @param lowHz - New lower frequency (or undefined to keep current)
1152
+ * @param highHz - New upper frequency (or undefined to keep current)
1153
+ * @returns New band with updated keyframe
1154
+ */
1155
+ declare function updateKeyframe(band: FrequencyBand, time: number, lowHz?: number, highHz?: number): FrequencyBand;
1156
+ /**
1157
+ * Move a keyframe in time.
1158
+ *
1159
+ * Updates the endTime of the segment before and startTime of the segment after.
1160
+ * Cannot move first or last keyframe.
1161
+ *
1162
+ * @param band - The band to modify
1163
+ * @param oldTime - Current time of the keyframe
1164
+ * @param newTime - New time for the keyframe
1165
+ * @returns New band with moved keyframe, or original if move is invalid
1166
+ */
1167
+ declare function moveKeyframeTime(band: FrequencyBand, oldTime: number, newTime: number): FrequencyBand;
1168
+
1169
+ /**
1170
+ * Band Masking utilities for F3.
1171
+ *
1172
+ * These functions apply frequency band boundaries as spectral masks
1173
+ * to an existing spectrogram, enabling per-band MIR analysis.
1174
+ */
1175
+
1176
+ type BandMaskOptions = {
1177
+ /** Soft edge width in Hz for smooth transitions (0 = hard edge). Default: 0 */
1178
+ edgeSmoothHz?: number;
1179
+ };
1180
+ type MaskedSpectrogram = Spectrogram & {
1181
+ /** ID of the band this mask was computed for */
1182
+ bandId: string;
1183
+ /** Fraction of energy retained per frame (0-1) for diagnostics */
1184
+ energyRetainedPerFrame: Float32Array;
1185
+ };
1186
+ /**
1187
+ * Convert an FFT bin index to frequency in Hz.
1188
+ *
1189
+ * @param bin - FFT bin index (0 to fftSize/2)
1190
+ * @param sampleRate - Audio sample rate in Hz
1191
+ * @param fftSize - FFT size (number of samples)
1192
+ * @returns Frequency in Hz
1193
+ */
1194
+ declare function binToHz(bin: number, sampleRate: number, fftSize: number): number;
1195
+ /**
1196
+ * Convert a frequency in Hz to an FFT bin index.
1197
+ *
1198
+ * @param hz - Frequency in Hz
1199
+ * @param sampleRate - Audio sample rate in Hz
1200
+ * @param fftSize - FFT size (number of samples)
1201
+ * @returns FFT bin index (may be fractional)
1202
+ */
1203
+ declare function hzToBin(hz: number, sampleRate: number, fftSize: number): number;
1204
+ /**
1205
+ * Compute a spectral mask for a band at a specific time.
1206
+ *
1207
+ * Returns a Float32Array of length (fftSize/2 + 1) containing
1208
+ * mask values between 0 and 1.
1209
+ *
1210
+ * @param band - The frequency band to mask
1211
+ * @param time - Time in seconds
1212
+ * @param sampleRate - Audio sample rate in Hz
1213
+ * @param fftSize - FFT size
1214
+ * @param options - Mask options
1215
+ * @returns Mask array, or null if band is inactive at this time
1216
+ */
1217
+ declare function computeBandMaskAtTime(band: FrequencyBand, time: number, sampleRate: number, fftSize: number, options?: BandMaskOptions): Float32Array | null;
1218
+ /**
1219
+ * Apply a band mask to an entire spectrogram.
1220
+ *
1221
+ * Creates a new spectrogram with band-masked magnitudes.
1222
+ * Uses linear interpolation of band bounds for time-varying bands.
1223
+ *
1224
+ * @param spec - Source spectrogram
1225
+ * @param band - Frequency band to apply
1226
+ * @param options - Mask options
1227
+ * @returns Masked spectrogram with energy retention diagnostics
1228
+ */
1229
+ declare function applyBandMaskToSpectrogram(spec: Spectrogram, band: FrequencyBand, options?: BandMaskOptions): MaskedSpectrogram;
1230
+ /**
1231
+ * Compute the energy of a magnitude spectrum.
1232
+ *
1233
+ * @param magnitudes - Magnitude spectrum (one frame)
1234
+ * @returns Sum of squared magnitudes
1235
+ */
1236
+ declare function computeFrameEnergy(magnitudes: Float32Array): number;
1237
+ /**
1238
+ * Compute the amplitude (sum of magnitudes) of a frame.
1239
+ *
1240
+ * @param magnitudes - Magnitude spectrum (one frame)
1241
+ * @returns Sum of magnitudes
1242
+ */
1243
+ declare function computeFrameAmplitude(magnitudes: Float32Array): number;
1244
+
1245
+ /**
1246
+ * Band-Scoped MIR utilities for F3.
1247
+ *
1248
+ * These functions compute MIR features (amplitude envelope, onset strength,
1249
+ * spectral flux) for frequency bands by applying spectral masks to an
1250
+ * existing spectrogram.
1251
+ */
1252
+
1253
+ type BandMirOptions = {
1254
+ /** Soft edge width in Hz for mask transitions. Default: 0 */
1255
+ edgeSmoothHz?: number;
1256
+ /** Options for onset strength computation */
1257
+ onset?: {
1258
+ /** If true, log-compress magnitudes before differencing. */
1259
+ useLog?: boolean;
1260
+ /** Moving-average smoothing window length in milliseconds. 0 disables smoothing. */
1261
+ smoothMs?: number;
1262
+ /** How to convert temporal differences into novelty. */
1263
+ diffMethod?: "rectified" | "abs";
1264
+ };
1265
+ /** Optional cancellation hook */
1266
+ isCancelled?: () => boolean;
1267
+ };
1268
+ /**
1269
+ * Compute amplitude envelope for a frequency band.
1270
+ *
1271
+ * Returns the sum of magnitudes per frame within the band's frequency range.
1272
+ *
1273
+ * @param spec - Source spectrogram
1274
+ * @param band - Frequency band to analyze
1275
+ * @param options - Computation options
1276
+ * @returns Band MIR result with amplitude envelope
1277
+ */
1278
+ declare function bandAmplitudeEnvelope(spec: Spectrogram, band: FrequencyBand, options?: BandMirOptions): BandMir1DResult;
1279
+ /**
1280
+ * Compute onset strength for a frequency band.
1281
+ *
1282
+ * Uses temporal differences of band-masked magnitudes.
1283
+ * Adapted from onset.ts:onsetEnvelopeFromSpectrogram.
1284
+ *
1285
+ * @param spec - Source spectrogram
1286
+ * @param band - Frequency band to analyze
1287
+ * @param options - Computation options
1288
+ * @returns Band MIR result with onset strength
1289
+ */
1290
+ declare function bandOnsetStrength(spec: Spectrogram, band: FrequencyBand, options?: BandMirOptions): BandMir1DResult;
1291
+ /**
1292
+ * Compute spectral flux for a frequency band.
1293
+ *
1294
+ * Uses L1 distance between consecutive normalized band-masked spectra.
1295
+ * Adapted from spectral.ts:spectralFlux.
1296
+ *
1297
+ * @param spec - Source spectrogram
1298
+ * @param band - Frequency band to analyze
1299
+ * @param options - Computation options
1300
+ * @returns Band MIR result with spectral flux
1301
+ */
1302
+ declare function bandSpectralFlux(spec: Spectrogram, band: FrequencyBand, options?: BandMirOptions): BandMir1DResult;
1303
+ /**
1304
+ * Compute spectral centroid for a frequency band.
1305
+ *
1306
+ * Returns the weighted average of frequency bins within the band (center of mass).
1307
+ * Output is in Hz per frame.
1308
+ *
1309
+ * @param spec - Source spectrogram
1310
+ * @param band - Frequency band to analyze
1311
+ * @param options - Computation options
1312
+ * @returns Band MIR result with spectral centroid in Hz
1313
+ */
1314
+ declare function bandSpectralCentroid(spec: Spectrogram, band: FrequencyBand, options?: BandMirOptions): BandMir1DResult;
1315
+ type BandMirBatchRequest = {
1316
+ bands: FrequencyBand[];
1317
+ functions: BandMirFunctionId[];
1318
+ /** Maximum number of bands to process concurrently. Default: 4 */
1319
+ maxConcurrent?: number;
1320
+ };
1321
+ type BandMirBatchResult = {
1322
+ /** Results keyed by bandId, each containing results for requested functions */
1323
+ results: Map<string, BandMir1DResult[]>;
1324
+ /** Total computation time in ms */
1325
+ totalTimingMs: number;
1326
+ };
1327
+ /**
1328
+ * Run band MIR analysis for multiple bands.
1329
+ *
1330
+ * Processes bands sequentially (web workers don't have real parallelism
1331
+ * within a single thread). The maxConcurrent option is reserved for
1332
+ * future multi-worker support.
1333
+ *
1334
+ * @param spec - Source spectrogram
1335
+ * @param request - Batch request specifying bands and functions
1336
+ * @param options - Computation options
1337
+ * @returns Map of results by band ID
1338
+ */
1339
+ declare function runBandMirBatch(spec: Spectrogram, request: BandMirBatchRequest, options?: BandMirOptions): Promise<BandMirBatchResult>;
1340
+ /**
1341
+ * Get a human-readable label for a band MIR function.
1342
+ *
1343
+ * @param fn - Band MIR function ID
1344
+ * @returns Human-readable label
1345
+ */
1346
+ declare function getBandMirFunctionLabel(fn: BandMirFunctionId): string;
1347
+
1348
+ /**
1349
+ * Band Event Extraction for F3.
1350
+ *
1351
+ * These functions extract discrete events (onset peaks, beat candidates)
1352
+ * from band-scoped 1D signals.
1353
+ */
1354
+
1355
+ type BandOnsetPeaksOptions = {
1356
+ /** Minimum inter-peak interval in seconds. Default: 0.0625 (~0.125 beats at 120 BPM). */
1357
+ minIntervalSec?: number;
1358
+ /** Adaptive threshold factor. Default: 0.8 (conservative for bands). */
1359
+ adaptiveFactor?: number;
1360
+ /** Use strict peak detection (> neighbors). Default: true. */
1361
+ strict?: boolean;
1362
+ };
1363
+ type BandBeatCandidatesOptions = {
1364
+ /** Minimum inter-candidate interval in seconds. Default: 0.1. */
1365
+ minIntervalSec?: number;
1366
+ /** Threshold factor for adaptive detection. Lower = more candidates. Default: 0.5. */
1367
+ thresholdFactor?: number;
1368
+ };
1369
+ /**
1370
+ * Extract onset peaks from a band MIR 1D signal.
1371
+ *
1372
+ * Uses peak picking with conservative defaults optimized for band-scoped
1373
+ * extraction. Typically applied to bandOnsetStrength or bandAmplitudeEnvelope.
1374
+ *
1375
+ * @param signal - The band MIR 1D result to extract peaks from
1376
+ * @param options - Peak picking options
1377
+ * @returns Band events result with onset peaks
1378
+ */
1379
+ declare function bandOnsetPeaks(signal: BandMir1DResult, options?: BandOnsetPeaksOptions): BandEventsResult;
1380
+ /**
1381
+ * Extract beat candidates from a band's onset peaks.
1382
+ *
1383
+ * Similar to full-track beat candidate detection but simplified for
1384
+ * single-band input. Uses the onset peaks to identify beat-like events.
1385
+ *
1386
+ * @param onsetPeaks - Band onset peaks result
1387
+ * @param options - Beat candidate options
1388
+ * @returns Band events result with beat candidates
1389
+ */
1390
+ declare function bandBeatCandidates(onsetPeaks: BandEventsResult, options?: BandBeatCandidatesOptions): BandEventsResult;
1391
+ type BandEventsBatchRequest = {
1392
+ /** Band MIR results to extract events from (keyed by bandId) */
1393
+ bandMirResults: Map<string, BandMir1DResult[]>;
1394
+ /** Event functions to run */
1395
+ functions: BandEventFunctionId[];
1396
+ /** Source signal function to use for onset peaks. Default: bandOnsetStrength */
1397
+ sourceFunction?: "bandOnsetStrength" | "bandAmplitudeEnvelope";
1398
+ /** Options for onset peaks extraction */
1399
+ onsetPeaksOptions?: BandOnsetPeaksOptions;
1400
+ /** Options for beat candidates extraction */
1401
+ beatCandidatesOptions?: BandBeatCandidatesOptions;
1402
+ };
1403
+ type BandEventsBatchResult = {
1404
+ /** Results keyed by bandId, each containing results for requested functions */
1405
+ results: Map<string, BandEventsResult[]>;
1406
+ /** Total computation time in ms */
1407
+ totalTimingMs: number;
1408
+ };
1409
+ /**
1410
+ * Run band event extraction for multiple bands.
1411
+ *
1412
+ * @param request - Batch request specifying bands, functions, and options
1413
+ * @returns Map of results by band ID
1414
+ */
1415
+ declare function runBandEventsBatch(request: BandEventsBatchRequest): Promise<BandEventsBatchResult>;
1416
+ /**
1417
+ * Get a human-readable label for a band event function.
1418
+ *
1419
+ * @param fn - Band event function ID
1420
+ * @returns Human-readable label
1421
+ */
1422
+ declare function getBandEventFunctionLabel(fn: BandEventFunctionId): string;
1423
+
1424
+ /**
1425
+ * Band-Scoped CQT utilities for F3.
1426
+ *
1427
+ * These functions compute CQT-derived features (harmonic energy, bass pitch motion,
1428
+ * tonal stability) for frequency bands by applying spectral masks to a CQT
1429
+ * spectrogram.
1430
+ *
1431
+ * CQT uses log-frequency bins, so band masking requires mapping Hz ranges
1432
+ * to CQT bin indices using hzToCqtBin().
1433
+ */
1434
+
1435
+ type BandCqtOptions = {
1436
+ /** Soft edge width in bins for mask transitions. Default: 0 */
1437
+ edgeSmoothBins?: number;
1438
+ /** Optional cancellation hook */
1439
+ isCancelled?: () => boolean;
1440
+ };
1441
+ type MaskedCqtSpectrogram = {
1442
+ /** Original CQT times */
1443
+ times: Float32Array;
1444
+ /** Masked CQT magnitudes */
1445
+ magnitudes: Float32Array[];
1446
+ /** CQT config */
1447
+ config: CqtConfig;
1448
+ /** Energy retained per frame (0-1) */
1449
+ energyRetainedPerFrame: Float32Array;
1450
+ /** CQT bin frequencies */
1451
+ binFrequencies: Float32Array;
1452
+ };
1453
+ /**
1454
+ * Apply a frequency band mask to a CQT spectrogram.
1455
+ *
1456
+ * Maps the band's Hz range to CQT bin indices and masks accordingly.
1457
+ *
1458
+ * @param cqt - Source CQT spectrogram
1459
+ * @param band - Frequency band to apply
1460
+ * @param options - Masking options
1461
+ * @returns Masked CQT spectrogram with energy retention diagnostics
1462
+ */
1463
+ declare function applyBandMaskToCqt(cqt: CqtSpectrogram, band: FrequencyBand, options?: BandCqtOptions): MaskedCqtSpectrogram;
1464
+ /**
1465
+ * Compute harmonic energy for a frequency band using CQT.
1466
+ *
1467
+ * Measures the ratio of energy at harmonic intervals within the band.
1468
+ * Adapted from cqtSignals.ts:harmonicEnergy.
1469
+ *
1470
+ * @param cqt - Source CQT spectrogram
1471
+ * @param band - Frequency band to analyze
1472
+ * @param options - Computation options
1473
+ * @returns Band CQT result with harmonic energy
1474
+ */
1475
+ declare function bandCqtHarmonicEnergy(cqt: CqtSpectrogram, band: FrequencyBand, options?: BandCqtOptions): BandCqt1DResult;
1476
+ /**
1477
+ * Compute bass pitch motion for a frequency band using CQT.
67
1478
  *
68
- * Notes:
69
- * - This bypasses JS loops for the diff+reduction step.
70
- * - Smoothing/log options are intentionally limited for v0.1 (keeps WGSL simple).
71
- * - Callers should fall back to CPU on errors.
1479
+ * Measures pitch movement in bass-range CQT bins within the band.
1480
+ * Most meaningful for bands overlapping the bass range (20-300 Hz).
1481
+ *
1482
+ * @param cqt - Source CQT spectrogram
1483
+ * @param band - Frequency band to analyze
1484
+ * @param options - Computation options
1485
+ * @returns Band CQT result with bass pitch motion
72
1486
  */
73
- declare function onsetEnvelopeFromMelGpu(mel: MelSpectrogram, gpu: MirGPU, options?: Pick<OnsetEnvelopeOptions, "diffMethod">): Promise<OnsetEnvelopeGpuResult>;
74
-
75
- type PeakPickEvent = {
76
- time: number;
77
- strength: number;
78
- index: number;
1487
+ declare function bandCqtBassPitchMotion(cqt: CqtSpectrogram, band: FrequencyBand, options?: BandCqtOptions): BandCqt1DResult;
1488
+ /**
1489
+ * Compute tonal stability for a frequency band using CQT.
1490
+ *
1491
+ * Measures consistency of chroma distribution over time within the band.
1492
+ * Adapted from cqtSignals.ts:tonalStability.
1493
+ *
1494
+ * @param cqt - Source CQT spectrogram
1495
+ * @param band - Frequency band to analyze
1496
+ * @param options - Computation options
1497
+ * @returns Band CQT result with tonal stability
1498
+ */
1499
+ declare function bandCqtTonalStability(cqt: CqtSpectrogram, band: FrequencyBand, options?: BandCqtOptions): BandCqt1DResult;
1500
+ type BandCqtBatchRequest = {
1501
+ bands: FrequencyBand[];
1502
+ functions: BandCqtFunctionId[];
1503
+ /** Maximum number of bands to process concurrently. Default: 4 */
1504
+ maxConcurrent?: number;
79
1505
  };
80
- type PeakPickOptions = {
81
- /** Minimum peak height (absolute). */
82
- threshold?: number;
83
- /** Minimum inter-peak interval (seconds). */
84
- minIntervalSec?: number;
85
- /** If provided, use adaptive threshold: mean(values) + factor*std(values). */
86
- adaptive?: {
87
- method?: "meanStd" | "median";
88
- factor?: number;
89
- };
90
- /** If true, prefer strict maxima (> neighbors); else allow flat plateaus. */
91
- strict?: boolean;
1506
+ type BandCqtBatchResult = {
1507
+ /** Results keyed by bandId */
1508
+ results: Map<string, BandCqt1DResult[]>;
1509
+ /** Total computation time in ms */
1510
+ totalTimingMs: number;
92
1511
  };
93
- declare function peakPick(times: Float32Array, values: Float32Array, options?: PeakPickOptions): PeakPickEvent[];
1512
+ /**
1513
+ * Run band CQT analysis for multiple bands.
1514
+ *
1515
+ * @param cqt - Source CQT spectrogram
1516
+ * @param request - Batch request specifying bands and functions
1517
+ * @param options - Computation options
1518
+ * @returns Map of results by band ID
1519
+ */
1520
+ declare function runBandCqtBatch(cqt: CqtSpectrogram, request: BandCqtBatchRequest, options?: BandCqtOptions): Promise<BandCqtBatchResult>;
1521
+ /**
1522
+ * Get a human-readable label for a band CQT function.
1523
+ *
1524
+ * @param fn - Band CQT function ID
1525
+ * @returns Human-readable label
1526
+ */
1527
+ declare function getBandCqtFunctionLabel(fn: BandCqtFunctionId): string;
94
1528
 
95
1529
  type SpectrogramLike2D = {
96
1530
  times: Float32Array;
@@ -132,6 +1566,634 @@ type Features2D = {
132
1566
  declare function delta(features: Features2D, options?: DeltaOptions): Features2D;
133
1567
  declare function deltaDelta(features: Features2D, options?: DeltaOptions): Features2D;
134
1568
 
1569
+ /**
1570
+ * Constant-Q Transform (CQT) implementation for F5.
1571
+ *
1572
+ * CQT provides log-frequency resolution aligned to musical pitch ratios.
1573
+ * This implementation builds on the existing STFT infrastructure by:
1574
+ * 1. Computing an STFT using the existing spectrogram function
1575
+ * 2. Applying a CQT filterbank in the frequency domain
1576
+ *
1577
+ * The CQT is an internal spectral view, not a user-facing spectrogram.
1578
+ */
1579
+
1580
+ /** Default CQT configuration values */
1581
+ declare const CQT_DEFAULTS: {
1582
+ /** Quarter-tone resolution (24 bins per octave) */
1583
+ readonly binsPerOctave: 24;
1584
+ /** C1 (lowest note on a standard piano) */
1585
+ readonly fMin: 32.7;
1586
+ /** C9 (well above audible range for most content) */
1587
+ readonly fMax: 8372;
1588
+ };
1589
+ /**
1590
+ * Convert CQT bin index to frequency in Hz.
1591
+ */
1592
+ declare function cqtBinToHz(bin: number, config: CqtConfig): number;
1593
+ /**
1594
+ * Convert frequency in Hz to CQT bin index (may be fractional).
1595
+ */
1596
+ declare function hzToCqtBin(hz: number, config: CqtConfig): number;
1597
+ /**
1598
+ * Calculate number of octaves covered by the CQT config.
1599
+ */
1600
+ declare function getNumOctaves(config: CqtConfig): number;
1601
+ /**
1602
+ * Calculate total number of CQT bins.
1603
+ */
1604
+ declare function getNumBins(config: CqtConfig): number;
1605
+ /**
1606
+ * Generate the center frequencies for all CQT bins.
1607
+ */
1608
+ declare function getCqtBinFrequencies(config: CqtConfig): Float32Array;
1609
+ type CqtOptions = {
1610
+ /** Optional cancellation hook; checked periodically. */
1611
+ isCancelled?: () => boolean;
1612
+ };
1613
+ /**
1614
+ * Apply default values to a partial CQT config.
1615
+ */
1616
+ declare function withCqtDefaults(partial?: Partial<CqtConfig>): CqtConfig;
1617
+ /**
1618
+ * Compute a CQT spectrogram from audio.
1619
+ *
1620
+ * This function:
1621
+ * 1. Computes an STFT using the existing spectrogram infrastructure
1622
+ * 2. Applies a CQT filterbank to each STFT frame
1623
+ * 3. Returns a log-frequency representation
1624
+ *
1625
+ * @param audio - Audio buffer to analyze
1626
+ * @param config - CQT configuration
1627
+ * @param options - Optional processing options
1628
+ * @returns CQT spectrogram with log-frequency resolution
1629
+ */
1630
+ declare function cqtSpectrogram(audio: AudioBufferLike$1, config: CqtConfig, options?: CqtOptions): Promise<CqtSpectrogram>;
1631
+ /**
1632
+ * Compute CQT and return with metadata for MIR pipeline.
1633
+ */
1634
+ declare function computeCqt(audio: AudioBufferLike$1, config?: Partial<CqtConfig>, options?: CqtOptions): Promise<{
1635
+ cqt: CqtSpectrogram;
1636
+ meta: MirRunMeta;
1637
+ }>;
1638
+
1639
+ /**
1640
+ * CQT-Derived 1D Signals for F5.
1641
+ *
1642
+ * These signals extract musically meaningful 1D features from the CQT representation:
1643
+ * - harmonicEnergy: Measures tonal presence vs noise
1644
+ * - bassPitchMotion: Measures bassline activity and low-end groove
1645
+ * - tonalStability: Measures harmonic stability vs modulation
1646
+ *
1647
+ * All signals are:
1648
+ * - 1D (one value per frame)
1649
+ * - Time-aligned with the CQT frames
1650
+ * - Deterministic
1651
+ * - Normalized to [0, 1] range
1652
+ */
1653
+
1654
+ /**
1655
+ * Compute harmonic energy signal from CQT.
1656
+ *
1657
+ * Measures sustained, pitch-aligned energy across harmonic bins.
1658
+ * Intended to capture "tonal presence" vs noise.
1659
+ */
1660
+ declare function harmonicEnergy(cqt: CqtSpectrogram): CqtSignalResult;
1661
+ /**
1662
+ * Compute bass pitch motion signal from CQT.
1663
+ *
1664
+ * Measures rate and magnitude of pitch movement in low-frequency CQT bins.
1665
+ * Intended to capture bassline motion, groove, and low-end activity.
1666
+ *
1667
+ * Algorithm:
1668
+ * 1. Extract bass-range bins from each frame
1669
+ * 2. Compute pitch centroid in bass range
1670
+ * 3. Compute absolute difference between consecutive frames
1671
+ */
1672
+ declare function bassPitchMotion(cqt: CqtSpectrogram): CqtSignalResult;
1673
+ /**
1674
+ * Compute tonal stability signal from CQT.
1675
+ *
1676
+ * Measures consistency of dominant pitch structure over time.
1677
+ * High values imply harmonic stability; low values imply modulation or noise.
1678
+ *
1679
+ * Algorithm:
1680
+ * 1. Compute chroma histogram for each frame
1681
+ * 2. Over a sliding window, compute variance of chroma distribution
1682
+ * 3. Low variance = stable tonality; high variance = unstable
1683
+ * 4. Invert so that high values = stable
1684
+ */
1685
+ declare function tonalStability(cqt: CqtSpectrogram): CqtSignalResult;
1686
+ /**
1687
+ * Compute a CQT-derived signal by ID.
1688
+ */
1689
+ declare function computeCqtSignal(cqt: CqtSpectrogram, signalId: CqtSignalId): CqtSignalResult;
1690
+ /**
1691
+ * Compute all CQT-derived signals.
1692
+ */
1693
+ declare function computeAllCqtSignals(cqt: CqtSpectrogram): Map<CqtSignalId, CqtSignalResult>;
1694
+
1695
+ /**
1696
+ * YIN-based monophonic pitch detection.
1697
+ *
1698
+ * Implements the YIN algorithm for fundamental frequency (f0) estimation.
1699
+ * Designed for monophonic sources: vocals, bass, lead synths.
1700
+ *
1701
+ * Reference: de Cheveigné, A., & Kawahara, H. (2002).
1702
+ * "YIN, a fundamental frequency estimator for speech and music."
1703
+ * Journal of the Acoustical Society of America, 111(4), 1917-1930.
1704
+ */
1705
+
1706
+ /** Options for activity gating in pitch detection */
1707
+ type PitchActivityOptions = {
1708
+ /** Pre-computed activity signal to use for gating */
1709
+ activity?: ActivitySignal;
1710
+ /** How to handle inactive frames. Default: "zero" */
1711
+ inactiveBehavior?: "zero" | "hold";
1712
+ };
1713
+ type PitchConfig = {
1714
+ /** Minimum detectable frequency in Hz. Default: 50 */
1715
+ fMinHz?: number;
1716
+ /** Maximum detectable frequency in Hz. Default: 1000 */
1717
+ fMaxHz?: number;
1718
+ /** Hop size in samples (determines time resolution). Default: 512 */
1719
+ hopSize?: number;
1720
+ /** Window size for analysis. Default: 2048 */
1721
+ windowSize?: number;
1722
+ /** YIN threshold for voiced detection (0-1). Default: 0.15 */
1723
+ threshold?: number;
1724
+ /** Activity gating options */
1725
+ activityOptions?: PitchActivityOptions;
1726
+ };
1727
+ type PitchResult = {
1728
+ times: Float32Array;
1729
+ values: Float32Array;
1730
+ };
1731
+ /**
1732
+ * Extract fundamental frequency (f0) from mono audio.
1733
+ *
1734
+ * Uses the YIN algorithm for robust pitch detection.
1735
+ * Returns 0 Hz for unvoiced/silent frames.
1736
+ *
1737
+ * @param samples - Mono audio samples
1738
+ * @param sampleRate - Sample rate of the audio
1739
+ * @param config - Configuration options
1740
+ * @returns Times (seconds) and f0 values (Hz, 0 for unvoiced)
1741
+ */
1742
+ declare function pitchF0(samples: Float32Array, sampleRate: number, config?: PitchConfig): PitchResult;
1743
+ /**
1744
+ * Extract pitch detection confidence from mono audio.
1745
+ *
1746
+ * Uses the YIN algorithm's aperiodicity measure.
1747
+ * Returns values in [0, 1] where 1 = highly periodic (confident pitch).
1748
+ *
1749
+ * @param samples - Mono audio samples
1750
+ * @param sampleRate - Sample rate of the audio
1751
+ * @param config - Configuration options
1752
+ * @returns Times (seconds) and confidence values (0-1)
1753
+ */
1754
+ declare function pitchConfidence(samples: Float32Array, sampleRate: number, config?: PitchConfig): PitchResult;
1755
+
1756
+ /**
1757
+ * Band Proposal Generation for F5.
1758
+ *
1759
+ * Generates automated suggestions for "interesting" frequency bands.
1760
+ * Proposals are advisory only - they must be explicitly promoted by the user.
1761
+ *
1762
+ * Algorithm overview:
1763
+ * 1. Analyze spectral structure using CQT
1764
+ * 2. Identify regions with concentrated energy or distinct activity
1765
+ * 3. Score and rank candidates
1766
+ * 4. Generate proposal objects with explanations
1767
+ */
1768
+
1769
+ type BandProposalOptions = {
1770
+ config?: BandProposalConfig;
1771
+ isCancelled?: () => boolean;
1772
+ };
1773
+ /**
1774
+ * Generate band proposals from audio.
1775
+ *
1776
+ * @param audio - Audio buffer to analyze
1777
+ * @param duration - Track duration in seconds
1778
+ * @param options - Optional configuration
1779
+ * @returns Array of band proposals with salience scores and reasons
1780
+ */
1781
+ declare function generateBandProposals(audio: AudioBufferLike$1, duration: number, options?: BandProposalOptions): Promise<BandProposalResult>;
1782
+
1783
+ /**
1784
+ * Custom Signal Reduction
1785
+ *
1786
+ * Provides algorithms for reducing 2D spectral data (mel spectrogram, HPSS,
1787
+ * MFCC, etc.) to 1D signals with configurable bin ranges and reduction methods.
1788
+ */
1789
+ /**
1790
+ * Input format for 2D time-aligned data.
1791
+ * Shape: data[timeIndex][featureIndex]
1792
+ */
1793
+ interface ReductionInput {
1794
+ /** 2D data: data[timeIndex][featureIndex] */
1795
+ data: Float32Array[];
1796
+ /** Time in seconds for each frame */
1797
+ times: Float32Array;
1798
+ }
1799
+ /**
1800
+ * Reduction algorithm identifiers.
1801
+ */
1802
+ type ReductionAlgorithmId = "mean" | "max" | "sum" | "variance" | "amplitude" | "spectralFlux" | "spectralCentroid" | "onsetStrength";
1803
+ /**
1804
+ * Options for bin range selection.
1805
+ */
1806
+ interface BinRangeOptions {
1807
+ /** Low bin index (inclusive). Default: 0 */
1808
+ lowBin?: number;
1809
+ /** High bin index (exclusive). Default: all bins */
1810
+ highBin?: number;
1811
+ }
1812
+ /**
1813
+ * Options for onset strength algorithm.
1814
+ */
1815
+ interface OnsetStrengthOptions {
1816
+ /** Smoothing window in milliseconds. Default: 10 */
1817
+ smoothMs?: number;
1818
+ /** Whether to log-compress before differencing. Default: true */
1819
+ useLog?: boolean;
1820
+ /** Difference method. Default: "rectified" */
1821
+ diffMethod?: "rectified" | "abs";
1822
+ }
1823
+ /**
1824
+ * Options for spectral flux algorithm.
1825
+ */
1826
+ interface SpectralFluxOptions {
1827
+ /** Whether to normalize frames before computing flux. Default: true */
1828
+ normalized?: boolean;
1829
+ }
1830
+ /**
1831
+ * Combined options for reduction.
1832
+ */
1833
+ interface ReductionOptions {
1834
+ /** Bin range selection */
1835
+ binRange?: BinRangeOptions;
1836
+ /** Onset strength parameters */
1837
+ onsetStrength?: OnsetStrengthOptions;
1838
+ /** Spectral flux parameters */
1839
+ spectralFlux?: SpectralFluxOptions;
1840
+ }
1841
+ /**
1842
+ * Result of a reduction operation.
1843
+ */
1844
+ interface ReductionResult {
1845
+ /** Frame times in seconds */
1846
+ times: Float32Array;
1847
+ /** Reduced values per frame */
1848
+ values: Float32Array;
1849
+ /** Value range for normalization */
1850
+ valueRange: {
1851
+ min: number;
1852
+ max: number;
1853
+ };
1854
+ }
1855
+ /**
1856
+ * Reduce 2D time-aligned data to a 1D signal using the specified algorithm.
1857
+ *
1858
+ * @param input - 2D input data (frames x bins)
1859
+ * @param algorithm - Reduction algorithm to use
1860
+ * @param options - Algorithm options including bin range selection
1861
+ * @returns Reduction result with times, values, and value range
1862
+ */
1863
+ declare function reduce2DToSignal(input: ReductionInput, algorithm: ReductionAlgorithmId, options?: ReductionOptions): ReductionResult;
1864
+ /**
1865
+ * Get human-readable label for a reduction algorithm.
1866
+ */
1867
+ declare function getReductionAlgorithmLabel(algorithm: ReductionAlgorithmId): string;
1868
+ /**
1869
+ * Get description for a reduction algorithm.
1870
+ */
1871
+ declare function getReductionAlgorithmDescription(algorithm: ReductionAlgorithmId): string;
1872
+ /**
1873
+ * Polarity interpretation mode.
1874
+ * - "signed": Preserve direction (signal can be positive or negative)
1875
+ * - "magnitude": Activity level only (always positive, uses absolute value)
1876
+ */
1877
+ type PolarityMode = "signed" | "magnitude";
1878
+ /**
1879
+ * Apply polarity interpretation to a signal.
1880
+ * Called after reduction, before stabilization.
1881
+ *
1882
+ * @param values - Input signal values
1883
+ * @param mode - Polarity mode to apply
1884
+ * @returns Transformed signal values
1885
+ */
1886
+ declare function applyPolarity(values: Float32Array, mode: PolarityMode): Float32Array;
1887
+ /**
1888
+ * Stabilization mode presets.
1889
+ */
1890
+ type StabilizationMode = "none" | "light" | "medium" | "heavy";
1891
+ /**
1892
+ * Envelope mode for signal shaping.
1893
+ */
1894
+ type EnvelopeMode = "raw" | "attackRelease";
1895
+ /**
1896
+ * Options for signal stabilization.
1897
+ */
1898
+ interface StabilizationOptions {
1899
+ /** Smoothing intensity preset. */
1900
+ mode: StabilizationMode;
1901
+ /** Envelope shaping mode. */
1902
+ envelopeMode: EnvelopeMode;
1903
+ /** Attack time in seconds (only for attackRelease mode). */
1904
+ attackTimeSec?: number;
1905
+ /** Release time in seconds (only for attackRelease mode). */
1906
+ releaseTimeSec?: number;
1907
+ }
1908
+ /**
1909
+ * Apply stabilization to a signal.
1910
+ *
1911
+ * @param values - Input signal values
1912
+ * @param times - Frame times in seconds
1913
+ * @param options - Stabilization options
1914
+ * @returns Stabilized signal values
1915
+ */
1916
+ declare function stabilizeSignal(values: Float32Array, times: Float32Array, options: StabilizationOptions): Float32Array;
1917
+ /**
1918
+ * Compute percentile values from a signal.
1919
+ *
1920
+ * @param values - Signal values
1921
+ * @param percentiles - Array of percentiles to compute (0-100)
1922
+ * @returns Object mapping percentile to value
1923
+ */
1924
+ declare function computePercentiles(values: Float32Array, percentiles: number[]): Record<number, number>;
1925
+ /**
1926
+ * Compute local (viewport) statistics for a signal.
1927
+ *
1928
+ * @param values - Signal values
1929
+ * @param times - Frame times in seconds
1930
+ * @param startTime - Viewport start time
1931
+ * @param endTime - Viewport end time
1932
+ * @returns Statistics within the viewport
1933
+ */
1934
+ declare function computeLocalStats(values: Float32Array, times: Float32Array, startTime: number, endTime: number): {
1935
+ min: number;
1936
+ max: number;
1937
+ p5: number;
1938
+ p95: number;
1939
+ };
1940
+
1941
+ /**
1942
+ * Event to Signal Conversion
1943
+ *
1944
+ * Converts discrete event streams (onsets, beats, authored events)
1945
+ * into continuous 1D signals for use in derived signal pipelines.
1946
+ *
1947
+ * Reducer algorithms:
1948
+ * - eventCount: Count events per window
1949
+ * - eventDensity: Normalized count (events per second)
1950
+ * - weightedSum: Sum of event weights per window
1951
+ * - weightedMean: Mean of event weights per window
1952
+ * - envelope: Generate continuous envelope from events
1953
+ */
1954
+ /**
1955
+ * A discrete event with time and optional weight.
1956
+ */
1957
+ interface DiscreteEvent {
1958
+ /** Event time in seconds. */
1959
+ time: number;
1960
+ /** Event weight (default 1.0). */
1961
+ weight?: number;
1962
+ /** Optional duration in seconds. */
1963
+ duration?: number;
1964
+ }
1965
+ /**
1966
+ * Window specification for event aggregation.
1967
+ */
1968
+ type EventWindowSpec = {
1969
+ kind: "seconds";
1970
+ windowSize: number;
1971
+ } | {
1972
+ kind: "samples";
1973
+ windowSize: number;
1974
+ };
1975
+ /**
1976
+ * Envelope shape for event-to-signal conversion.
1977
+ */
1978
+ type EnvelopeShape = {
1979
+ kind: "impulse";
1980
+ } | {
1981
+ kind: "gaussian";
1982
+ widthMs: number;
1983
+ } | {
1984
+ kind: "attackDecay";
1985
+ attackMs: number;
1986
+ decayMs: number;
1987
+ } | {
1988
+ kind: "gate";
1989
+ };
1990
+ /**
1991
+ * Options for event-to-signal conversion.
1992
+ */
1993
+ interface EventToSignalOptions {
1994
+ /** Sample rate for output signal (samples per second). */
1995
+ sampleRate: number;
1996
+ /** Total duration in seconds. */
1997
+ duration: number;
1998
+ /** Whether to normalize output to 0-1 range. */
1999
+ normalize?: boolean;
2000
+ }
2001
+ /**
2002
+ * Result of event-to-signal conversion.
2003
+ */
2004
+ interface EventSignalResult {
2005
+ /** Signal values. */
2006
+ values: Float32Array;
2007
+ /** Time points in seconds. */
2008
+ times: Float32Array;
2009
+ /** Value range before normalization. */
2010
+ rawRange: {
2011
+ min: number;
2012
+ max: number;
2013
+ };
2014
+ }
2015
+ /**
2016
+ * Count events per window (sliding window approach).
2017
+ */
2018
+ declare function eventCount(events: DiscreteEvent[], windowSpec: EventWindowSpec, options: EventToSignalOptions): EventSignalResult;
2019
+ /**
2020
+ * Event density: events per second in each window.
2021
+ */
2022
+ declare function eventDensity(events: DiscreteEvent[], windowSpec: EventWindowSpec, options: EventToSignalOptions): EventSignalResult;
2023
+ /**
2024
+ * Weighted sum of events per window.
2025
+ */
2026
+ declare function weightedSum(events: DiscreteEvent[], windowSpec: EventWindowSpec, options: EventToSignalOptions): EventSignalResult;
2027
+ /**
2028
+ * Weighted mean of events per window.
2029
+ */
2030
+ declare function weightedMean(events: DiscreteEvent[], windowSpec: EventWindowSpec, options: EventToSignalOptions): EventSignalResult;
2031
+ /**
2032
+ * Generate continuous envelope from events.
2033
+ * Each event contributes an envelope shape to the output.
2034
+ */
2035
+ declare function eventEnvelope(events: DiscreteEvent[], shape: EnvelopeShape, options: EventToSignalOptions): EventSignalResult;
2036
+ /**
2037
+ * Convert events to signal using specified reducer.
2038
+ */
2039
+ type EventReducer = "eventCount" | "eventDensity" | "weightedSum" | "weightedMean" | "envelope";
2040
+ interface EventToSignalParams {
2041
+ /** Reducer algorithm. */
2042
+ reducer: EventReducer;
2043
+ /** Window spec for count/density/sum/mean reducers. */
2044
+ window?: EventWindowSpec;
2045
+ /** Envelope shape for envelope reducer. */
2046
+ envelopeShape?: EnvelopeShape;
2047
+ }
2048
+ declare function eventsToSignal(events: DiscreteEvent[], params: EventToSignalParams, options: EventToSignalOptions): EventSignalResult;
2049
+
2050
+ /**
2051
+ * Signal Transforms
2052
+ *
2053
+ * Transform chain implementation for derived signals.
2054
+ * All transforms are pure functions that operate on Float32Array values.
2055
+ *
2056
+ * Transform categories:
2057
+ * - Smooth: movingAverage, exponential, gaussian
2058
+ * - Normalize: minMax, robust, zScore
2059
+ * - Scale: linear scaling with offset
2060
+ * - Polarity: signed, magnitude
2061
+ * - Clamp: min/max bounds
2062
+ * - Remap: input range to output range with curve
2063
+ */
2064
+ /**
2065
+ * Base transform type.
2066
+ */
2067
+ interface TransformBase {
2068
+ kind: string;
2069
+ }
2070
+ /**
2071
+ * Smooth with moving average.
2072
+ */
2073
+ interface TransformSmoothMovingAverage extends TransformBase {
2074
+ kind: "smooth";
2075
+ method: "movingAverage";
2076
+ windowMs: number;
2077
+ }
2078
+ /**
2079
+ * Smooth with exponential filter.
2080
+ */
2081
+ interface TransformSmoothExponential extends TransformBase {
2082
+ kind: "smooth";
2083
+ method: "exponential";
2084
+ timeConstantMs: number;
2085
+ }
2086
+ /**
2087
+ * Smooth with Gaussian kernel.
2088
+ */
2089
+ interface TransformSmoothGaussian extends TransformBase {
2090
+ kind: "smooth";
2091
+ method: "gaussian";
2092
+ windowMs: number;
2093
+ }
2094
+ /**
2095
+ * All smoothing transform types.
2096
+ */
2097
+ type TransformSmooth = TransformSmoothMovingAverage | TransformSmoothExponential | TransformSmoothGaussian;
2098
+ /**
2099
+ * Normalize to 0-1 range using min/max.
2100
+ */
2101
+ interface TransformNormalizeMinMax extends TransformBase {
2102
+ kind: "normalize";
2103
+ method: "minMax";
2104
+ targetMin?: number;
2105
+ targetMax?: number;
2106
+ }
2107
+ /**
2108
+ * Normalize using robust percentile range.
2109
+ */
2110
+ interface TransformNormalizeRobust extends TransformBase {
2111
+ kind: "normalize";
2112
+ method: "robust";
2113
+ percentileLow?: number;
2114
+ percentileHigh?: number;
2115
+ targetMin?: number;
2116
+ targetMax?: number;
2117
+ }
2118
+ /**
2119
+ * Normalize using z-score (mean and std dev).
2120
+ */
2121
+ interface TransformNormalizeZScore extends TransformBase {
2122
+ kind: "normalize";
2123
+ method: "zScore";
2124
+ }
2125
+ /**
2126
+ * All normalization transform types.
2127
+ */
2128
+ type TransformNormalize = TransformNormalizeMinMax | TransformNormalizeRobust | TransformNormalizeZScore;
2129
+ /**
2130
+ * Linear scale and offset.
2131
+ */
2132
+ interface TransformScale extends TransformBase {
2133
+ kind: "scale";
2134
+ scale: number;
2135
+ offset: number;
2136
+ }
2137
+ /**
2138
+ * Polarity mode.
2139
+ */
2140
+ interface TransformPolarity extends TransformBase {
2141
+ kind: "polarity";
2142
+ mode: "signed" | "magnitude";
2143
+ }
2144
+ /**
2145
+ * Clamp to bounds.
2146
+ */
2147
+ interface TransformClamp extends TransformBase {
2148
+ kind: "clamp";
2149
+ min?: number;
2150
+ max?: number;
2151
+ }
2152
+ /**
2153
+ * Remap from input range to output range.
2154
+ */
2155
+ interface TransformRemap extends TransformBase {
2156
+ kind: "remap";
2157
+ inputMin: number;
2158
+ inputMax: number;
2159
+ outputMin: number;
2160
+ outputMax: number;
2161
+ curve?: "linear" | "ease" | "easeIn" | "easeOut";
2162
+ }
2163
+ /**
2164
+ * Union of all transform step types.
2165
+ */
2166
+ type TransformStep = TransformSmooth | TransformNormalize | TransformScale | TransformPolarity | TransformClamp | TransformRemap;
2167
+ /**
2168
+ * A chain of transforms to apply in sequence.
2169
+ */
2170
+ type TransformChain = TransformStep[];
2171
+ /**
2172
+ * Context for transform chain execution.
2173
+ */
2174
+ interface TransformContext {
2175
+ /** Sample rate of the signal (samples per second). */
2176
+ sampleRate: number;
2177
+ /** Time points in seconds (optional, for time-aware transforms). */
2178
+ times?: Float32Array;
2179
+ }
2180
+ /**
2181
+ * Apply a single transform step.
2182
+ */
2183
+ declare function applyTransformStep(values: Float32Array, step: TransformStep, context: TransformContext): Float32Array;
2184
+ /**
2185
+ * Apply a chain of transforms to a signal.
2186
+ */
2187
+ declare function applyTransformChain(values: Float32Array, chain: TransformChain, context: TransformContext): Float32Array;
2188
+ /**
2189
+ * Get human-readable label for a transform step.
2190
+ */
2191
+ declare function getTransformLabel(step: TransformStep): string;
2192
+ /**
2193
+ * Create a default transform step for a kind.
2194
+ */
2195
+ declare function createDefaultTransform(kind: TransformStep["kind"]): TransformStep | null;
2196
+
135
2197
  type NormaliseForWaveformOptions = {
136
2198
  min?: number;
137
2199
  max?: number;
@@ -189,6 +2251,19 @@ declare function spectrogramToDb(magnitudes2d: Spectrogram2D, options?: Spectrog
189
2251
  */
190
2252
  declare function clampDb(db2d: Spectrogram2D, minDb: number, maxDb: number): Spectrogram2D;
191
2253
 
2254
+ /**
2255
+ * Resample audio using linear interpolation.
2256
+ *
2257
+ * This is a simple, fast resampling algorithm suitable for MIR analysis
2258
+ * where perfect reconstruction is not critical.
2259
+ *
2260
+ * @param samples - Input audio samples
2261
+ * @param fromRate - Original sample rate (Hz)
2262
+ * @param toRate - Target sample rate (Hz)
2263
+ * @returns Resampled audio samples
2264
+ */
2265
+ declare function resample(samples: Float32Array, fromRate: number, toRate: number): Float32Array;
2266
+
192
2267
  type MinMax = {
193
2268
  min: number;
194
2269
  max: number;
@@ -447,4 +2522,4 @@ type AudioBufferLike = {
447
2522
  */
448
2523
  declare function helloMir(name?: string): string;
449
2524
 
450
- export { type AudioBufferLike, type DeltaOptions, type Features2D, type HpssOptions, MIR_VERSION, type MelConfig, type MelSpectrogram, type MfccOptions, type MfccResult, type MinMax, type MirFingerprintV1, type MirFingerprintVectorWeights, MirGPU, type MirRefinementCandidateLabelV1, type MirSearchCandidate, type MirSearchCurveKindV1, type MirSearchGuidedOptionsV1, type MirSearchOptionsV1, type MirSearchResultV1, type MirSearchResultV1Guided, type MirVersion, type OnsetEnvelope, type OnsetEnvelopeGpuResult, type OnsetEnvelopeOptions, type PeakPickEvent, type PeakPickOptions, Spectrogram, type Spectrogram2D, type SpectrogramLike2D, type SpectrogramToDbOptions, clampDb, delta, deltaDelta, fingerprintToVectorV1, fingerprintV1, helloMir, hpss, melSpectrogram, mfcc, minMax, normaliseForWaveform, onsetEnvelopeFromMel, onsetEnvelopeFromMelGpu, onsetEnvelopeFromSpectrogram, peakPick, searchTrackV1, searchTrackV1Guided, similarityFingerprintV1, spectralCentroid, spectralFlux, spectrogramToDb };
2525
+ export { type ActivityConfig, type ActivityDiagnostics, type ActivitySignal, type AdaptivePeakPickingResult, type AmplitudeEnvelopeConfig, type AmplitudeEnvelopeResult, type AudioBufferLike, type BandBeatCandidatesOptions, BandCqt1DResult, type BandCqtBatchRequest, type BandCqtBatchResult, BandCqtFunctionId, type BandCqtOptions, BandEventFunctionId, type BandEventsBatchRequest, type BandEventsBatchResult, BandEventsResult, type BandMaskOptions, BandMir1DResult, type BandMirBatchRequest, type BandMirBatchResult, BandMirFunctionId, type BandMirOptions, type BandOnsetPeaksOptions, BandProposalConfig, type BandProposalOptions, BandProposalResult, BeatCandidate, type BeatCandidatesOptions, type BeatCandidatesOutput, BeatGrid, BeatPosition, type BeatSalienceSignal, type BinGateConfig, type BinRangeOptions, CQT_DEFAULTS, CqtConfig, type CqtOptions, CqtSignalId, CqtSignalResult, CqtSpectrogram, DEFAULT_PEAK_PICKING_PARAMS, type DeltaOptions, type DiscreteEvent, type EnvelopeMode, type EnvelopeShape, type EventReducer, type EventSignalResult, type EventToSignalOptions, type EventToSignalParams, type EventWindowSpec, type Features2D, FrequencyBand, FrequencyBandStructure, FrequencyBoundsAtTime, FrequencyKeyframe, FrequencySegment, type HpssOptions, type HysteresisGateParams, MIR_VERSION, type MaskedCqtSpectrogram, type MaskedSpectrogram, type MelConfig, type MelConversionConfig, type MelSpectrogram, type MfccOptions, type MfccResult, type MinMax, type MirFingerprintV1, type MirFingerprintVectorWeights, MirGPU, type MirRefinementCandidateLabelV1, MirRunMeta, type MirSearchCandidate, type MirSearchCurveKindV1, type MirSearchGuidedOptionsV1, type MirSearchOptionsV1, type MirSearchResultV1, type MirSearchResultV1Guided, type MirVersion, MusicalTimeSegment, MusicalTimeStructure, type OnsetDiagnostics, type OnsetEnvelope, type OnsetEnvelopeGpuResult, type OnsetEnvelopeOptions, type OnsetEnvelopeResult, type OnsetStrengthOptions, type PeakPickEvent, type PeakPickOptions, type PeakPickingParams, type PeakPickingResult, PhaseAlignmentConfig, PhaseHypothesis, type PitchActivityOptions, type PitchConfig, type PitchResult, type PolarityMode, type ReductionAlgorithmId, type ReductionInput, type ReductionOptions, type ReductionResult, type SilenceGateConfig, type SilenceGateResult, type SpectralFluxOptions, Spectrogram, type Spectrogram2D, type SpectrogramLike2D, type SpectrogramToDbOptions, type StabilizationMode, type StabilizationOptions, type TempoHypothesesOptions, type TempoHypothesesOutput, TempoHypothesis, type TransformChain, type TransformClamp, type TransformContext, type TransformNormalize, type TransformNormalizeMinMax, type TransformNormalizeRobust, type TransformNormalizeZScore, type TransformPolarity, type TransformRemap, type TransformScale, type TransformSmooth, type TransformSmoothExponential, type TransformSmoothGaussian, type TransformSmoothMovingAverage, type TransformStep, addBandToStructure, allFrequencyBoundsAt, amplitudeEnvelope, applyActivityGating, applyBandMaskToCqt, applyBandMaskToSpectrogram, applyHysteresisGate, applyPolarity, applyTransformChain, applyTransformStep, bandAmplitudeEnvelope, bandBeatCandidates, bandCqtBassPitchMotion, bandCqtHarmonicEnergy, bandCqtTonalStability, bandOnsetPeaks, bandOnsetStrength, bandSpectralCentroid, bandSpectralFlux, bandsActiveAt, bassPitchMotion, beatSalienceFromMel, binToHz, buildActivityMask, clampDb, computeActivityFromAudio, computeActivityFromMel, computeActivityFromSpectrogram, computeAdaptiveThreshold, computeAllCqtSignals, computeBandMaskAtTime, computeBeatPosition, computeBeatPositionFromStructure, computeCqt, computeCqtSignal, computeFrameAmplitude, computeFrameEnergy, computeFrameEnergyFromMel, computeFrameEnergyFromSpectrogram, computeLocalStats, computePercentiles, computePhaseHypotheses, computeSilenceGating, cqtBinToHz, cqtSpectrogram, createBandStructure, createConstantBand, createDefaultTransform, createMusicalTimeStructure, createSectionedBand, createSegmentFromGrid, createStandardBands, delta, deltaDelta, detectBeatCandidates, estimateNoiseFloor, eventCount, eventDensity, eventEnvelope, eventsToSignal, featureIndexToHz, findBandById, findSegmentAtTime, fingerprintToVectorV1, fingerprintV1, frequencyBoundsAt, generateBandId, generateBandProposals, generateBeatTimes, generateSegmentBeatTimes, generateSegmentId, generateTempoHypotheses, getBandCqtFunctionLabel, getBandEventFunctionLabel, getBandMirFunctionLabel, getCqtBinFrequencies, getNumBins, getNumOctaves, getReductionAlgorithmDescription, getReductionAlgorithmLabel, getTransformLabel, harmonicEnergy, helloMir, hpss, hzToBin, hzToCqtBin, hzToFeatureIndex, hzToMel, interpolateActivity, keyframesFromBand, melSpectrogram, melToHz, mergeAdjacentSegments, mfcc, minMax, moveKeyframeTime, normaliseForWaveform, onsetEnvelopeFromMel, onsetEnvelopeFromMelGpu, onsetEnvelopeFromSpectrogram, peakPick, pickPeaks, pickPeaksAdaptive, pitchConfidence, pitchF0, reduce2DToSignal, removeBandFromStructure, removeKeyframe, resample, runBandCqtBatch, runBandEventsBatch, runBandMirBatch, searchTrackV1, searchTrackV1Guided, segmentsFromKeyframes, similarityFingerprintV1, sortBands, sortFrequencySegments, sortSegments, spectralCentroid, spectralFlux, spectrogramToDb, splitBandSegmentAt, splitSegment, stabilizeSignal, tonalStability, touchStructure, updateBandInStructure, updateKeyframe, validateBandStructure, validateFrequencyBand, validateFrequencySegments, validateSegments, weightedMean, weightedSum, withBinGateDefaults, withCqtDefaults, withSilenceGateDefaults };