first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# `decode-pipeline.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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 `Float32Array`s 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)` returns `channels` equal-length
|
||||
`Float32Array`s.
|
||||
- `runDecode(opts)` orchestrates writeFile → run → readFile via
|
||||
`WorkerHost.submitFfmpeg` and returns a normalised `DecodeResult`.
|
||||
- Default sample rate (48 000) and channel count (2) for callers that
|
||||
do not pre-probe their input.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user