import { describe, expect, it } from 'vitest';
import { BASS_HZ, HIGH_HZ, MID_HZ, bandLevel } from '~/audio/bands';
const SAMPLE_RATE = 48_000;
const FFT_SIZE = 1024;
const BIN_HZ = SAMPLE_RATE / FFT_SIZE;
const BIN_COUNT = FFT_SIZE / 2;
function silentSpectrum(): Float32Array {
const a = new Float32Array(BIN_COUNT);
a.fill(-100);
return a;
}
function loudBandSpectrum(loHz: number, hiHz: number, db = -10): Float32Array {
const a = silentSpectrum();
const lo = Math.max(0, Math.floor(loHz / BIN_HZ));
const hi = Math.min(BIN_COUNT - 1, Math.ceil(hiHz / BIN_HZ));
for (let i = lo; i <= hi; i++) a[i] = db;
return a;
}
describe('bandLevel', () => {
it('returns 0 for silence (≤ −90 dB floor)', () => {
const s = silentSpectrum();
expect(bandLevel(s, SAMPLE_RATE, FFT_SIZE, BASS_HZ)).toBe(0);
expect(bandLevel(s, SAMPLE_RATE, FFT_SIZE, MID_HZ)).toBe(0);
expect(bandLevel(s, SAMPLE_RATE, FFT_SIZE, HIGH_HZ)).toBe(0);
});
it('clamps to 1 for loud signals (≥ −20 dB ceil)', () => {
const s = loudBandSpectrum(20, 250, -10);
expect(bandLevel(s, SAMPLE_RATE, FFT_SIZE, BASS_HZ)).toBe(1);
});
it('isolates energy to the requested band', () => {
const bassLoud = loudBandSpectrum(20, 250, -10);
expect(bandLevel(bassLoud, SAMPLE_RATE, FFT_SIZE, BASS_HZ)).toBeGreaterThan(0.9);
expect(bandLevel(bassLoud, SAMPLE_RATE, FFT_SIZE, HIGH_HZ)).toBeLessThan(0.05);
});
it('returns a fractional level for mid-range dB', () => {
// −55 dB sits halfway between the −90 floor and −20 ceil → ≈ 0.5
const half = silentSpectrum();
const lo = Math.floor(BASS_HZ[0] / BIN_HZ);
const hi = Math.ceil(BASS_HZ[1] / BIN_HZ);
for (let i = lo; i <= hi; i++) half[i] = -55;
const v = bandLevel(half, SAMPLE_RATE, FFT_SIZE, BASS_HZ);
expect(v).toBeGreaterThan(0.45);
expect(v).toBeLessThan(0.55);
});
});