first commit
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# `Html5AudioFallback`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`Html5AudioFallback` is the graceful-degradation path for browsers and
|
||||
runtimes without the Web Audio API. It implements the same
|
||||
`TimelineAdapter` interface as `BrowserTimelineAdapter` so consumers
|
||||
swap engines without conditional branching, but every operation that the
|
||||
underlying `HTMLAudioElement` cannot honour — per-track volume / pan /
|
||||
solo / mute, `setTempo`, FX hosting, sample-accurate scheduling — is a
|
||||
defined no-op. Unsupported per-track operations on **unknown** track ids
|
||||
still raise `AUDIO_ENGINE_BROWSER.TRACK_NOT_FOUND` so caller bugs are
|
||||
detected.
|
||||
|
||||
The fallback never constructs an `AudioContext`. It is feature-bounded by
|
||||
design — `<audio>` plays a single source at a time, so the adapter
|
||||
honours the first track's first clip and ignores the rest. Consumers
|
||||
that need polyphony or per-track FX use `BrowserTimelineAdapter`.
|
||||
|
||||
## Features
|
||||
|
||||
- Implements `TimelineAdapter` (every member callable).
|
||||
- Real transport (`play` / `pause` / `stop` / `seek` / `getCurrentTime`)
|
||||
through an injectable `HTMLAudioElement`-shaped value.
|
||||
- `setMasterVolume` clamped to `[0, 1]`.
|
||||
- `setLoop` honours the `enabled` flag by toggling the element's `loop`
|
||||
property and records the window for `getLoopWindow()` debugging.
|
||||
- Per-track ops validate the track id and then no-op.
|
||||
- `dispose()` is idempotent.
|
||||
- Methods after `dispose()` raise
|
||||
`AUDIO_ENGINE_BROWSER.ALREADY_DISPOSED`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { Html5AudioFallback } from "@suaveplan/audio-engine-browser/fallback/html5-fallback";
|
||||
|
||||
const fallback = new Html5AudioFallback();
|
||||
fallback.init();
|
||||
fallback.setTracks([{ id: "t1", name: "lead", clips: [{ id: "c1", documentId: "/song.mp3", startSample: 0, offsetSamples: 0, durationSamples: 0 }], volume: 1, muted: false, soloed: false, pan: 0 }]);
|
||||
fallback.play();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { Html5AudioFallback } from "@suaveplan/audio-engine-browser/fallback/html5-fallback";
|
||||
|
||||
// Inject a custom element (e.g. a test double or a pre-buffered element)
|
||||
const fallback = new Html5AudioFallback({
|
||||
element: customAudioElement,
|
||||
volume: 0.8,
|
||||
loop: true,
|
||||
});
|
||||
|
||||
fallback.init({ signal: controller.signal });
|
||||
fallback.setMasterVolume(0.5);
|
||||
fallback.setLoop(0, 8, true);
|
||||
|
||||
fallback.play(2); // start at 2s
|
||||
console.log(fallback.getCurrentTime());
|
||||
|
||||
fallback.dispose(); // idempotent — subsequent ops throw ALREADY_DISPOSED
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new Html5AudioFallback(options?)`
|
||||
|
||||
- `options.element` — pre-constructed `HTMLAudioElement` (default
|
||||
`new Audio()`).
|
||||
- `options.volume` — initial master volume.
|
||||
- `options.loop` — initial loop flag.
|
||||
- `options.lookAhead` / `options.ppqn` — readonly properties surfaced
|
||||
for `TimelineAdapter` parity.
|
||||
|
||||
### `init(options?) → void`
|
||||
|
||||
Synchronous in this implementation. Honours a pre-aborted `AbortSignal`
|
||||
by throwing `AUDIO_ENGINE_BROWSER.CONTEXT_FAILED`.
|
||||
|
||||
### `setTracks(tracks)`, `addTrack(track)`, `removeTrack(trackId)`, `updateTrack(trackId, track)`
|
||||
|
||||
Maintain the track table; the first track's first clip becomes the
|
||||
element's `src`.
|
||||
|
||||
### `play(time?)`, `pause(time?)`, `stop(time?)`, `seek(time)`, `getCurrentTime()`
|
||||
|
||||
Drive the underlying element.
|
||||
|
||||
### `setLoop(start, end, enabled)`, `setMasterVolume(volume)`
|
||||
|
||||
Honoured operations.
|
||||
|
||||
### `setTrackVolume(trackId, _)`, `setTrackMute`, `setTrackSolo`, `setTrackPan`, `setTempo`
|
||||
|
||||
Defined no-ops with track-id validation.
|
||||
|
||||
### `dispose() → void`
|
||||
|
||||
Idempotent. Pauses the element and marks the fallback disposed.
|
||||
|
||||
### `getLoopWindow() → { start, end, enabled }`
|
||||
|
||||
Returns the configured loop window for tests / debug.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The fallback chooses the first clip of the first track as `src`. A
|
||||
full multi-clip mix is out of scope — `<audio>` is single-source.
|
||||
- `setMasterVolume` clamps to `[0, 1]`; some platforms throw on
|
||||
out-of-range values, so the clamp is the caller-friendly behaviour.
|
||||
- The Promise returned by `HTMLAudioElement.play()` rejects on
|
||||
autoplay-policy violations; the rejection is swallowed because the
|
||||
spec contract is synchronous and reporting a rejection here would
|
||||
surface a different shape per platform.
|
||||
- `dispose()` swallows pause errors because some embedded `<audio>`
|
||||
shims throw after detachment.
|
||||
Reference in New Issue
Block a user