/** * The psychoacoustic model: how much quantisation noise each scalefactor band * can hide. * * Everything else in the encoder is arithmetic with a right answer. This is the * part that decides what a listener will not notice, and it is the difference * between an MP3 that is merely valid and one that sounds acceptable at 128 kbps. * * ## What it computes * * A loud tone raises the threshold of audibility around itself: noise that would * be obvious in silence is inaudible next to it. The model estimates that * threshold per band, and the quantiser is then free to be as coarse as the * threshold allows — spending bits where they are heard and saving them where * they are not. * * Four things set the threshold: * * 1. **Spreading.** Masking is not confined to the band that causes it; it * spreads across the cochlea, further upward in frequency than downward. Band * energies are convolved with a spreading function on the bark scale. * 2. **Tonality.** A pure tone masks much less noise than a noise band of the * same energy does — roughly 29 dB against 6 dB. Tonality is estimated per * band from spectral flatness: a flat band is noise-like, a peaky one tonal. * 3. **Absolute threshold.** Below the quiet threshold of hearing nothing is * audible whatever else is happening, which matters most at the extremes of * the spectrum where the ear is least sensitive. * 4. **Time.** A threshold may not rise faster than the ear's forward masking * allows, or the quantiser would spread a transient's noise backwards in time * into the silence before it — pre-echo, the most recognisable MP3 artefact. * * ## Why the partitions are finer than the bands * * Thresholds are wanted per scalefactor band, and computing them there directly * is the obvious shortcut. It does not work: a scalefactor band is roughly a * bark wide, and the spreading function is almost flat across ±1 bark, so a * quiet band adjacent to a loud one inherits nearly all of its energy and comes * out with a threshold at its own signal level. Measured that way the model * declared every band already masked at 128 kbps, and the encoder did nothing. * * So energy is partitioned at about a third of a bark, where the spreading * function's shape is actually resolved, and each scalefactor band then takes * the *lowest* threshold among the partitions it covers. Taking the minimum is * the conservative choice, and the right one: a band is only as maskable as its * most exposed part. * * ## Two deliberate simplifications * * **Tonality from spectral flatness**, not from the unpredictability measure of * ISO model 2. The unpredictability measure needs magnitude and phase histories * for two previous granules and is markedly better on transients; flatness needs * only the current spectrum. Short blocks are what really answers transients, * and those arrive with window switching. * * **Thresholds are used as ratios, not absolute levels.** The model works on an * FFT of the input while the quantiser works on MDCT lines, and the two do not * share a scale. Rather than calibrate between them — which would be a constant * fitted to this implementation, and wrong the moment either side changed — each * band's signal-to-mask ratio is applied to that band's *own* MDCT energy. The * absolute threshold of hearing is the one part that needs a real level, and it * assumes the usual convention that full scale is 96 dB SPL. */ /** * Marks the granules that contain an attack sharp enough to need short blocks. * * Pre-echo is a consequence of the transform's length: a long block spreads its * quantisation noise across all 576 samples it covers, so noise belonging to a * drum hit arrives up to 12 ms *before* the hit does, in what should be silence. * The ear notices that far more readily than noise during the hit itself. * * Detection compares each short segment's energy against a decaying average of * what came before. A ratio threshold rather than a fixed level, because the * effect is relative: a loud attack in loud music is masked, and a modest one * after silence is not. * * @param out One flag per granule. Sized by the caller. */ export declare function detectAttacks(input: Float32Array, granuleCount: number, out: Uint8Array): void; /** * Turns attack flags into a legal sequence of block types. * * The format does not allow jumping between window shapes: a short block has to * be introduced by a start block and left by a stop block, because those are the * asymmetric windows whose overlap adds back to unity against their neighbours. * Emitting a short block straight after a long one produces a stream that parses * and decodes to a click. * * So a detected attack in granule N forces granule N-1 to become a start block — * one granule of lookahead, which is why detection runs over the whole input * before any of it is coded. * * @param attacks One flag per granule, consumed here. * @param out Block type per granule: 0 long, 1 start, 2 short, 3 stop. */ export declare function sequenceBlockTypes(attacks: Uint8Array, out: Uint8Array): void; /** * Per-granule masking thresholds for one channel. * * Stateful — it remembers the previous granule's thresholds for the temporal * cap — so one instance per channel, never shared. */ export declare class PsychoacousticModel { #private; constructor(sampleRate: number); /** Forgets history, e.g. when starting a new stream. */ reset(): void; /** * Masking thresholds for one granule, as allowed mean squared error per MDCT * line — the units {@link quantizeGranule} expects. * * @param input The channel's samples. * @param startSample First sample of the granule being coded. * @param spectrum The granule's 576 MDCT lines, which set the scale. * @param out 22 thresholds, one per scalefactor band. */ analyse(input: Float32Array, startSample: number, spectrum: Float32Array, out: Float32Array, short?: boolean): void; }