5.1 KiB
decode/decode-source
Universal decode entry point — the single function callers use to turn
any Source into a decoded AudioDocument.
(no submodules)
Purpose
decodeSource is the top-level public-facing decode API. Callers do
not need to know about codec registries, format detection, or
runtime-gated source resolution — they pass in a Source (string,
URL, ArrayBuffer, Uint8Array, or ReadableStream) plus optional
hints/callbacks, and receive a fully-decoded AudioDocument from the
default registry (which ships every native + WASM codec).
The companion progressiveDecode provides the same surface but is the
hook for future per-chunk-fed decoders that allow editing the
document while decoding is still in progress. The current
implementation falls back to a full-buffer decode for every codec
because none of the native or WASM wrappers expose
createDecoder factories yet — the contract is in place so a future
streaming-aware codec can plug in without callers changing their
code.
Features
- Accepts any
Sourcevariant — bytes, URL, file path, stream. - Optional
opts.formatoverrides detection (use when callers know the format and want to skip the magic-byte sniff). - Optional
opts.budgetprojects required memory before allocating pages — if projection exceeds the budget the call aborts withAUDIO_CODECS.BUDGET_EXCEEDEDcarrying the projected and budgeted byte counts incontext. - Optional
opts.signal(AbortSignal) — checked before resolution, before format detection, and before page allocation. Aborts throwAUDIO_CODECS.STREAM_ERROR. - Optional
opts.notify()fires once the first page is materialised;opts.ondata(delta)fires per decoded block delta. Useful for progress UI on long-running decodes. - Optional
opts.registrylets callers inject a pre-configuredCodecRegistry(e.g. with extra codecs) instead of using the default. - Zod-validated options — invalid
budget(non-positive number) or any other schema violation throwsAUDIO_CODECS.INVALID_SOURCEwith the Zod issue list incontext.
Usage: Basic Example
import { decodeSource } from "@suaveplan/audio-codecs/decode/decode-source";
const doc = await decodeSource(wavBytes);
console.log(doc.sourceLength, doc.sampleRate);
Usage: Advanced Example
Decode a stream with a memory budget, abort signal, and progress callbacks:
import { decodeSource } from "@suaveplan/audio-codecs/decode/decode-source";
const ctrl = new AbortController();
const ui = makeProgressUi();
const doc = await decodeSource(
fetch("https://cdn/audio.flac").then((r) => r.body!),
{
budget: 500 * 1024 * 1024, // 500 MB cap
signal: ctrl.signal,
notify: () => ui.startedDecoding(),
ondata: ({ samples }) => ui.updateProgress(samples),
}
);
API Reference
interface DecodeSourceOpts { format?, budget?, signal?, notify?, ondata?, registry? }.const DecodeSourceOptsSchema— Zod schema for the options object.async function decodeSource(source: Source, opts?: DecodeSourceOpts): Promise<AudioDocument>— throwsINVALID_SOURCEfor invalid options or unrecognised source values;UNKNOWN_FORMATif detection finds no codec andopts.formatis also missing;BUDGET_EXCEEDEDif projected memory exceedsopts.budget;STREAM_ERRORfor AbortSignal cancellation.async function progressiveDecode(source, opts?, registry?): Promise<AudioDocument>— same surface asdecodeSourceplus an optional positionalregistryfor callers that prefer not to pass it viaopts.registry.re-export CodecRegistry— convenience so consumers needing custom codec registration can import it from the same subpath.
Implementation Notes
- The default registry is constructed lazily via a top-level promise
(
DEFAULT_REGISTRY_PROMISE) so the side-effects of registering ten codecs only happen the first time the registry is needed. Imports of the package barrel itself don't trigger codec registration. opts.signalis checked at every coarse-grained boundary — before resolution, after registry availability, before detection. Codec-internal aborts are codec-specific and not covered by this function.- The budget projection uses
sampleRate * channels * sampleCount * 4(4 bytes per float32 sample) as an upper bound. For compressed sources the actual decoded size depends on the codec's expansion ratio; the projection assumes a conservative 5× expansion of input bytes which catches the common cases (MP3, FLAC, Opus all decompress within 5×) without false positives for raw WAV. progressiveDecodeis currently equivalent todecodeSource— the streaming-page-fill behaviour described in the spec ("editable before decode completes") requires codeccreateDecoderfactories which only WAV currently exposes. The function name and signature reserve the contract so future streaming-aware codecs drop in without API changes.- Both functions are async — even when the input is fully in-memory, format detection and codec dispatch involve at least one microtask boundary because the codec methods are async.