Files
skills/docs/media/audio/audio-codecs/src/decode/decode-source.md
2026-05-29 08:20:56 +01:00

5.1 KiB
Raw Blame History

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

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> — 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<AudioDocument> — 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.