first commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# `renderOffline`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`renderOffline` is the bridge to non-realtime export. It wires a
|
||||
`BrowserTimelineAdapter` to an `OfflineAudioContext`, attaches the
|
||||
adapter to the supplied `TimelineEngine`, drives playback from `0`, and
|
||||
resolves with the rendered `AudioBuffer`. Because the adapter accepts an
|
||||
injected context, offline render reuses the exact same rendering code
|
||||
path as realtime playback — the "tests" and the "export feature" share
|
||||
one seam.
|
||||
|
||||
Failure modes are uniform: pre-flight checks (bad duration, bad sample
|
||||
rate, pre-aborted signal), context construction failure, and
|
||||
`startRendering()` rejection all raise
|
||||
`BrowserAudioError(OFFLINE_RENDER_FAILED)` with the underlying error
|
||||
attached as `cause`.
|
||||
|
||||
## Features
|
||||
|
||||
- Pre-flight validation of `duration` (> 0, finite) and `sampleRate`
|
||||
(> 0, finite).
|
||||
- `AbortSignal` honoured at the boundary.
|
||||
- Optional `contextFactory` for tests that cannot construct a real
|
||||
`OfflineAudioContext` (e.g. `happy-dom`); production callers omit
|
||||
it and the function dynamically imports
|
||||
`standardized-audio-context`.
|
||||
- OTel span (`engine_browser.offline_render`) and duration histogram
|
||||
(`audio_engine_browser_offline_render_duration_ms`).
|
||||
- Disposes the adapter in `finally` so a render failure does not leak
|
||||
resources.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { renderOffline } from "@suaveplan/audio-engine-browser/offline/offline-render";
|
||||
|
||||
const buffer = await renderOffline(timeline, 2, 48000);
|
||||
console.log(buffer.length, buffer.sampleRate, buffer.numberOfChannels);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { renderOffline } from "@suaveplan/audio-engine-browser/offline/offline-render";
|
||||
|
||||
const buffer = await renderOffline(timeline, 4, 44100, {
|
||||
signal: controller.signal,
|
||||
contextFactory: ({ length, sampleRate, channels }) =>
|
||||
new MyDeterministicOfflineContext({ length, sampleRate, channels }),
|
||||
});
|
||||
|
||||
// buffer.length === Math.ceil(4 * 44100)
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `renderOffline(timeline, duration, sampleRate, options?) → Promise<AudioBuffer>`
|
||||
|
||||
- `timeline` — the `TimelineEngine` to drive.
|
||||
- `duration` — seconds; must be finite and positive.
|
||||
- `sampleRate` — Hz; must be finite and positive.
|
||||
- `options.signal` — optional `AbortSignal`; an already-aborted signal
|
||||
short-circuits before any context work.
|
||||
- `options.contextFactory` — optional injected factory. Production
|
||||
omits this; tests provide one.
|
||||
|
||||
Returns the `AudioBuffer` from `OfflineAudioContext.startRendering`.
|
||||
|
||||
### `RenderOfflineOptions`
|
||||
|
||||
```ts
|
||||
interface RenderOfflineOptions {
|
||||
signal?: AbortSignal;
|
||||
contextFactory?: (args: {
|
||||
length: number;
|
||||
sampleRate: number;
|
||||
channels: number;
|
||||
}) => OfflineContextLike;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Length is computed as `Math.ceil(duration * sampleRate)` so a 2-second
|
||||
render at 48 kHz produces exactly 96 000 frames.
|
||||
- The dynamic `import("standardized-audio-context")` keeps the shim out
|
||||
of the import graph when `contextFactory` is always supplied (e.g.
|
||||
in the test suite).
|
||||
- The adapter is disposed in `finally`. The injected `OfflineAudioContext`
|
||||
is NOT closed by the adapter because the adapter sees it as injected;
|
||||
the caller owns the context lifetime if they passed one in via the
|
||||
factory. The function does not call `close()` itself either, because
|
||||
`OfflineAudioContext.startRendering` resolves to a single buffer and
|
||||
the spec considers the context spent at that point.
|
||||
- The duration histogram is recorded in `finally` so both success and
|
||||
failure produce a sample. Failure samples are useful for SLO
|
||||
monitoring (the percentile of failed renders).
|
||||
Reference in New Issue
Block a user