4.2 KiB
Html5AudioFallback
(no submodules)
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 injectableHTMLAudioElement-shaped value. setMasterVolumeclamped to[0, 1].setLoophonours theenabledflag by toggling the element'sloopproperty and records the window forgetLoopWindow()debugging.- Per-track ops validate the track id and then no-op.
dispose()is idempotent.- Methods after
dispose()raiseAUDIO_ENGINE_BROWSER.ALREADY_DISPOSED.
Usage: Basic Example
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
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-constructedHTMLAudioElement(defaultnew Audio()).options.volume— initial master volume.options.loop— initial loop flag.options.lookAhead/options.ppqn— readonly properties surfaced forTimelineAdapterparity.
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. setMasterVolumeclamps 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.