/** * Encoder-side analysis: polyphase filterbank, MDCT, and the aliasing butterfly. * * This is the decoder's chain run backwards. Where decoding goes * * dequantise → alias reduction → IMDCT → frequency inversion → synthesis * * encoding goes * * analysis → frequency inversion → MDCT → aliasing butterfly → quantise * * ## The analysis window * * `C[512]` lives in the generated `analysis-window.ts`. Its magnitudes are the * synthesis window's, shifted 64 taps and scaled by 1/32 — the two share a * prototype filter — but its *signs* are solved, and that distinction turned out * to matter more than anything else in the encoder. * * Plain `C = D/32` was what this shipped first, on the reasoning that a window * solved by least squares would be fitted to this implementation's own synthesis * error and would therefore diverge from the specification, hurting interop with * real decoders. Sound reasoning, and wrong. The two differ in 28 of 512 signs, * and the test that settles it is to encode with each and let *ffmpeg* decode: * * window ffmpeg vs source audiobox vs source * C = D/32 41.8 dB 41.6 dB * solved 73.1 dB 54.0 dB * * An overfitted window would have made ffmpeg worse. Instead ffmpeg gains 31 dB * — more than our own decoder gains — which is only possible if the solved signs * are the specification's. Our own round trip now sits 19 dB below ffmpeg's * because the *synthesis* window has become the limiting term, exactly the * margin by which the decoder already differed from ffmpeg. * * The lesson worth keeping: a round trip through a matched pair cannot tell a * correct transform from two compensating errors. Only an independent decoder * can, and the cost of not asking was 31 dB. */ export { ANALYSIS_WINDOW, FILTERBANK_DELAY } from './analysis-window.js'; /** * The 32-band polyphase analysis filterbank. * * Stateful — it carries a 512-sample delay line — so one instance per channel, * and never share them. */ export declare class AnalysisFilterbank { #private; /** Discards history, e.g. when starting a new stream. */ reset(): void; /** * Consumes 32 input samples and produces 32 sub-band values. * * @param input Source samples. * @param offset Index of the first of the 32 samples to consume. * @param out Destination for 32 sub-band values. */ process(input: Float32Array, offset: number, out: Float32Array): void; } /** * Negates every other sample in odd sub-bands. * * Mirrors `invertFrequency` in the decoder. Self-inverse, so the same operation * serves both directions — but it has to happen at the mirrored point in the * chain, between the filterbank and the MDCT. */ export declare function invertFrequency(samples: Float32Array): void; /** * Forward MDCT over one granule. * * Each sub-band's 18 new sub-band samples are windowed together with the * previous granule's 18 — the 50% overlap that makes the transform critically * sampled — and transformed to 18 spectral lines. * * @param subbands 576 sub-band samples, sub-band major (18 per sub-band). * @param overlap The previous granule's 18 samples per sub-band, updated here. * @param out 576 spectral lines, sub-band major. */ export declare function mdct(subbands: Float32Array, overlap: Float32Array, out: Float32Array, blockType?: number): void; /** * Interleaves a short-block granule into the order it is transmitted in. * * The MDCT leaves each sub-band's three windows contiguous, which is what the * decoder's IMDCT wants back. The bitstream carries them the other way round — * scalefactor band first, then window, then line — because that is the order the * scalefactors apply in. This is the exact inverse of `reorderShortBlock`. * * @param spectrum 576 lines, sub-band major, rewritten in place. * @param scratch 576 floats of working space. */ export declare function reorderForTransmission(spectrum: Float32Array, sampleRate: number, scratch: Float32Array): void; /** * The aliasing butterfly, applied on the encode side. * * The polyphase bands overlap, so the decoder cancels the resulting aliasing * with eight butterflies across each sub-band boundary. The encoder applies the * inverse rotation so that cancellation lands correctly — the same coefficients, * with the cross terms swapped in sign. */ export declare function applyAliasing(spectrum: Float32Array): void; /** * Runs one channel's full analysis for a granule: 576 input samples to 576 * spectral lines, ready for quantisation. * * State lives in `filterbank` and `overlap`, so successive calls must pass the * same objects for the same channel. */ export declare function analyseGranule(input: Float32Array, startSample: number, filterbank: AnalysisFilterbank, overlap: Float32Array, subbandScratch: Float32Array, slotScratch: Float32Array, out: Float32Array, blockType?: number, sampleRate?: number): void; //# sourceMappingURL=analysis.d.ts.map