Files
deepdrft/DeepDrftPublic/Interop/audio/IFormatDecoder.ts
T

111 lines
4.9 KiB
TypeScript

import { OpusSeekData } from './OpusSidecar.js';
/**
* 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).
* Opus: OpusSeekData — the precomputed granule->byte index + OpusHead/OpusTags setup bytes,
* parsed from the sidecar artifact (NOT byteRate math; see OpusFormatDecoder).
*/
seekData?: Mp3VbrSeekData | FlacSeekData | OpusSeekData | 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;
}