/** * MPEG audio frame headers, and the metadata tags that wrap an MP3 file. * * MP3 has no file-level structure: a `.mp3` is just a run of self-describing * frames, optionally bracketed by ID3 tags. Everything a decoder needs — bitrate, * sample rate, channel mode — is repeated in every 4-byte frame header, which is * what lets playback start from the middle of a stream. */ /** MPEG version, from the 2-bit version field. */ export type MpegVersion = 1 | 2 | 25; /** Channel mode, from the 2-bit mode field. */ export type ChannelMode = 'stereo' | 'joint' | 'dual' | 'mono'; export interface FrameHeader { version: MpegVersion; layer: number; /** True when a 16-bit CRC follows the header. */ hasCrc: boolean; bitrate: number; sampleRate: number; padding: boolean; mode: ChannelMode; /** Intensity/MS stereo flags, only meaningful in joint stereo. */ modeExtension: number; channels: number; /** Total frame length in bytes, including the header. */ frameLength: number; /** Samples produced per channel by this frame. */ samplesPerFrame: number; /** Bytes of side information following the header (and CRC, if present). */ sideInfoSize: number; } /** * Bitrates the encoder can write, in kbps, indexed by the header's 4-bit field. * * Exported so the encoder writes headers from the same table the decoder reads * them with — the two disagreeing is exactly the kind of bug that produces files * only your own decoder can play. */ export declare const MPEG1_LAYER3_BITRATES: readonly number[]; /** * The bitrates an MPEG-1 Layer III frame can actually be written at, ascending. * * {@link MPEG1_LAYER3_BITRATES} is indexed by the header's 4-bit field, so it * carries two sentinels: index 0 is "free format", where the size is not * declared at all, and index 15 is reserved. Neither is a bitrate, and treating * the raw table as a list of choices means accepting `0` as valid and reading * `-1` as the maximum. */ export declare const MPEG1_LAYER3_BITRATE_VALUES: readonly number[]; /** Sample rates the encoder can write, indexed by the header's 2-bit field. */ export declare const MPEG1_SAMPLE_RATES: readonly number[]; /** Header field value for a bitrate in kbps, or -1 if it is not representable. */ export declare function bitrateIndex(kbps: number): number; /** Header field value for a sample rate in Hz, or -1 if it is not representable. */ export declare function sampleRateIndex(hz: number): number; /** * Frame length in bytes for MPEG-1 Layer III. * * 1152 samples at `bitrate` bits per second occupy a non-integer number of * bytes at most sample rates, so frames alternate between two sizes; the padding * bit says which. Getting this wrong desynchronises every following frame. */ export declare function mpeg1FrameLength(bitrateKbps: number, sampleRate: number, padding: boolean): number; /** Side-information size in bytes for MPEG-1 Layer III. */ export declare function mpeg1SideInfoSize(channels: number): number; /** Parses a 4-byte frame header. Returns `null` when the bytes are not a valid header. */ export declare function parseFrameHeader(data: Uint8Array, offset: number): FrameHeader | null; /** * Finds the next valid frame header at or after `from`. * * A single sync word is not proof of a frame — 0xFF bytes occur constantly in * audio data and in tags. The candidate is confirmed by checking that a second * valid header sits exactly `frameLength` bytes later, which makes a false * positive vanishingly unlikely. */ export declare function findFrame(data: Uint8Array, from: number, limit?: number): { offset: number; header: FrameHeader; } | null; /** Size of an ID3v2 tag at `offset`, or 0 if there isn't one. */ export declare function id3v2Size(data: Uint8Array, offset?: number): number; /** Information carried by a Xing/Info/VBRI header in the first frame. */ export interface VbrInfo { /** Total frames in the file, when declared. */ frames?: number; /** Total bytes of audio data, when declared. */ bytes?: number; /** Samples to drop from the start, from the LAME tag. */ encoderDelay?: number; /** Samples to drop from the end, from the LAME tag. */ encoderPadding?: number; /** True for VBR (`Xing`), false for CBR (`Info`). */ vbr: boolean; } /** * Reads a Xing, Info, or VBRI header from the first frame. * * These sit inside a normal (silent) frame, in the space the bit reservoir would * otherwise use. They matter for two reasons: they give an accurate duration for * a VBR file without scanning it, and the LAME extension records the encoder * delay and padding needed for gapless playback. */ export declare function readVbrHeader(data: Uint8Array, frameOffset: number, header: FrameHeader): VbrInfo | null;