# `mel-scale` — hertz↔mel mapping and triangular filterbank ## Purpose The mel-scale module provides the perceptual frequency warp used by MFCC and any caller that wants a psychoacoustically motivated reduction of an FFT magnitude spectrum. It exposes the Slaney/HTK closed-form `hzToMel` and its inverse `melToHz`, plus a builder that produces a triangular filterbank for a given `(frameSize, sampleRate, melBands, minHz, maxHz)` tuple. Each filterbank is cached so repeated frames pay zero filter-construction cost; applying a filterbank to a magnitude spectrum is then a single dense matrix-vector multiply. ## Features - Closed-form hertz↔mel mapping that round-trips to machine precision. - Triangular filterbank with `melBands` bands evenly spaced on the mel scale. - Per-spec caching keyed by `(frameSize, sampleRate, melBands, minHz, maxHz)`. - Boundary validation that `melBands` is a positive integer. ## Usage: Basic ```ts import { applyMelFilterbank, buildMelFilterbank, } from "@suaveplan/audio-analysis-spectrum/algorithms/mel-scale"; const fb = buildMelFilterbank({ frameSize: 2048, sampleRate: 48_000, melBands: 40, minHz: 0, maxHz: 24_000, }); const mel = applyMelFilterbank(spectrum, fb); ``` ## Usage: Advanced ```ts import { clearMelFilterbankCache, hzToMel, melToHz, } from "@suaveplan/audio-analysis-spectrum/algorithms/mel-scale"; const m = hzToMel(1000); const back = melToHz(m); // ≈ 1000 within 1e-3 clearMelFilterbankCache(); // before a benchmark sweep ``` ## API Reference - `hzToMel(hz): number` / `melToHz(mel): number` — Slaney/HTK closed forms. - `buildMelFilterbank(spec): MelFilterbank` — cached triangular filterbank. - `applyMelFilterbank(spectrum, bank): Float32Array` — dense mel-band vector. - `clearMelFilterbankCache(): void` — drop every cached filterbank. ## Implementation Notes The builder allocates `melBands + 2` mel-spaced points so each band has both a lower and an upper neighbour centre. Bin frequencies are computed once as `(i * sampleRate) / frameSize` to avoid a per-band recomputation. Each band's weights peak at `1` and taper linearly to `0` at the neighbour centres; no per-band energy normalisation is applied because the downstream MFCC log+DCT step is insensitive to global scaling.