# `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 `Source` variant — bytes, URL, file path, stream. - Optional `opts.format` overrides detection (use when callers know the format and want to skip the magic-byte sniff). - Optional `opts.budget` projects required memory before allocating pages — if projection exceeds the budget the call aborts with `AUDIO_CODECS.BUDGET_EXCEEDED` carrying the projected and budgeted byte counts in `context`. - Optional `opts.signal` (AbortSignal) — checked before resolution, before format detection, and before page allocation. Aborts throw `AUDIO_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.registry` lets callers inject a pre-configured `CodecRegistry` (e.g. with extra codecs) instead of using the default. - Zod-validated options — invalid `budget` (non-positive number) or any other schema violation throws `AUDIO_CODECS.INVALID_SOURCE` with the Zod issue list in `context`. ## Usage: Basic Example ```ts 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: ```ts 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` — throws `INVALID_SOURCE` for invalid options or unrecognised source values; `UNKNOWN_FORMAT` if detection finds no codec and `opts.format` is also missing; `BUDGET_EXCEEDED` if projected memory exceeds `opts.budget`; `STREAM_ERROR` for AbortSignal cancellation. - `async function progressiveDecode(source, opts?, registry?): Promise` — same surface as `decodeSource` plus an optional positional `registry` for callers that prefer not to pass it via `opts.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.signal` is 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. - `progressiveDecode` is currently equivalent to `decodeSource` — the streaming-page-fill behaviour described in the spec ("editable before decode completes") requires codec `createDecoder` factories 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.