2.8 KiB
decode-pipeline.ts
(no submodules)
Purpose
This module owns the pure logic of the decode pipeline: format sniffing
from magic bytes, the ffmpeg argument list that emits f32le interleaved
samples to a MEMFS scratch file, and the deinterleave step that produces
per-channel Float32Arrays for the DecodeResult shape. The pipeline
runs entirely through the supplied WorkerHost so cancellation,
timeouts, and OOM detection are inherited from a single source of
truth.
Features
sniffFormat(bytes)recognises WAV, MP3 (ID3v2 + frame-sync), Ogg (Vorbis vs Opus by inspecting the bos packet), FLAC, AAC ADTS, and WebM/EBML headers.deinterleave(samples, channels)returnschannelsequal-lengthFloat32Arrays.runDecode(opts)orchestrates writeFile → run → readFile viaWorkerHost.submitFfmpegand returns a normalisedDecodeResult.- Default sample rate (48 000) and channel count (2) for callers that do not pre-probe their input.
Usage: Basic
import { runDecode } from "@suaveplan/audio-codec-ffmpeg-wasm/decode/decode-pipeline";
const result = await runDecode({ host, bytes, format: "wav" });
console.log(result.channelData.length); // channel count
console.log(result.channelData[0].length); // frame count
console.log(result.sampleRate);
Usage: Advanced
import {
runDecode,
sniffFormat,
deinterleave,
} from "@suaveplan/audio-codec-ffmpeg-wasm/decode/decode-pipeline";
const fmt = sniffFormat(bytes);
if (!fmt) throw new Error("unsupported");
const result = await runDecode({
host,
bytes,
format: fmt as "wav" | "mp3" | "ogg" | "opus" | "flac" | "aac" | "webm",
sampleRate: 44_100,
channels: 1,
timeoutMs: 30_000,
signal: controller.signal,
});
// Drop a deinterleaved view for downstream processing
const ch = deinterleave(new Float32Array([0, 1, 2, 3]), 2);
API Reference
DEFAULT_SAMPLE_RATE: 48_000.DEFAULT_CHANNELS: 2.sniffFormat(bytes: Uint8Array): string | undefined.deinterleave(interleaved: Float32Array, channels: number): Float32Array[].runDecode(opts: DecodePipelineOptions): Promise<DecodeResult>.
Implementation Notes
The deinterleave step copies the raw Uint8Array through a Float32Array
view to honour the f32le wire layout and then takes a .slice() so the
returned arrays own their own backing buffer — callers can transfer or
mutate them without aliasing the worker output. The pipeline never
silently catches MEMFS cleanup failures here; the host's submitFfmpeg
handles try/finally cleanup and logs any delete failures via the
injected logger. A non-zero ffmpeg exit is mapped to
AUDIO_CODEC_FFMPEG_WASM.DECODE_FAILED inside the worker host so this
module never sees it directly.