/** * FormatInfo: parsed header data needed to stream and seek an audio file. * Populated by IFormatDecoder.tryParseHeader; used by StreamDecoder throughout playback. */ export interface FormatInfo { /** Samples per second (e.g. 44100). */ sampleRate: number; /** Number of audio channels. */ channels: number; /** * Nominal bit depth — 16 for MP3 (conventional), 16/24/32 for WAV/FLAC. * Used for display; decoders handle the actual sample format internally. */ bitsPerSample: number; /** * Average bytes per second. Used for CBR byte-offset estimation. * For WAV: exact (sampleRate * blockAlign). * For MP3 CBR: bitrate_kbps * 125. * For FLAC: approximate (fileSize / duration). */ byteRate: number; /** * For WAV: PCM frame size in bytes (channels * bitsPerSample / 8). * For MP3: frame size in bytes (constant for CBR, 0 for VBR with TOC). * For FLAC: 0 (frame sizes vary; use sync scan instead). * Used by getAlignedSegmentSize to round to clean frame boundaries. */ blockAlign: number; /** Total duration in seconds, from the header (null if unavailable). */ totalDuration: number | null; /** Byte offset where audio frames begin in the original file. */ audioDataOffset: number; /** * Format-specific accelerator for seek-beyond-buffer byte calculation. * WAV: null (uses byteRate/blockAlign directly). * MP3 VBR: Xing/VBRI TOC (100-entry Uint8Array, values are file-percentage * 255). * FLAC: SeekTable (array of {sampleNumber: number, streamOffset: number} — stream_offset * is bytes from the start of audio frames, i.e. after all metadata blocks). */ seekData?: Mp3VbrSeekData | FlacSeekData | null; } export interface Mp3VbrSeekData { kind: 'mp3-vbr'; toc: Uint8Array; // 100 entries; toc[i] = file-byte-fraction at i% of duration totalBytes: number; // total audio bytes (from Xing header) } export interface FlacSeekData { kind: 'flac-seektable'; points: Array<{ sampleNumber: number; streamOffset: number }>; streamInfoBytes: Uint8Array; // raw STREAMINFO metadata block for segment wrapping metadataBlocksSize: number; // total bytes of all metadata blocks (for stream_offset relative math) } /** * IFormatDecoder: per-format strategy for header parsing, segment boundary detection, * segment wrapping, and seek offset calculation. * * Implementations: WavFormatDecoder (Wave 1), Mp3FormatDecoder (Wave 2), FlacFormatDecoder (Wave 2). */ export interface IFormatDecoder { /** * Attempt to parse the header from accumulated bytes. Returns null if more bytes are needed. * Called with growing chunks array until it succeeds or exceeds MAX_HEADER_SEARCH_BYTES. */ tryParseHeader(chunks: Uint8Array[], totalSize: number): FormatInfo | null; /** * Return the largest decodable byte count ≤ requestedSize that ends on a clean frame/block * boundary in the audio data (post-header). Returns 0 if not enough data yet. * @param info - the parsed FormatInfo * @param availableBytes - bytes available starting at the current processedBytes position * @param requestedSize - maximum desired segment size * @param streamComplete - true when the stream has ended (allows draining the tail) * @param rawData - optional; the first `Math.min(requestedSize, availableBytes)` raw audio * bytes (starting at the current processedBytes position in the stream), made available * for format-specific frame-sync scanning. WAV and MP3 decoders ignore this parameter; * FLAC and similar variable-frame formats use it to find the last clean frame boundary * within the candidate window. */ getAlignedSegmentSize( info: FormatInfo, availableBytes: number, requestedSize: number, streamComplete: boolean, rawData?: Uint8Array ): number; /** * Wrap raw audio bytes in the minimal decodable container for decodeAudioData. * WAV: prepend a 44-byte standard PCM header. * MP3: pass through unchanged (raw frames are self-contained). * FLAC: prepend fLaC marker + STREAMINFO metadata block. */ wrapSegment(info: FormatInfo, rawBytes: Uint8Array): Uint8Array; /** * Calculate the file-absolute byte offset for a seek-beyond-buffer Range request. * The returned value includes the header offset (result ≥ info.audioDataOffset). * WAV: exact PCM frame alignment. * MP3 CBR: frame-aligned estimate; MP3 VBR: TOC interpolation. * FLAC: SEEKTABLE lookup when available. */ calculateByteOffset(info: FormatInfo, positionSeconds: number): number; }