loadStream
loadStream(id: string, url: string, options?: StreamOptions): Promise<void>;
Load a long audio file as a stream instead of decoding it into memory.
loadSound fetches the whole file and decodes it into an
AudioBuffer. That is what sprites, overlapping instances and sample-accurate
scheduling need, and it is the wrong shape for a podcast. An hour of stereo
audio at 44.1 kHz costs roughly 600 MB once decoded, and nothing plays until the
download and the decode have both finished.
loadStream takes the other route. An HTMLAudioElement fetches as it plays,
and a MediaElementAudioSourceNode drops the result into the same audio graph
your buffered sounds use, so master volume, panning and the master limiter still
apply. Only the metadata is read up front, so playback starts within a second on
a file of any length.
| Parameter | Type | Description |
|---|---|---|
id | string | The id you will address this stream by, like any other sound |
url | string | URL of the audio file |
options | StreamOptions (optional) | Initial volume, pan, loop, playback rate, start time, progress tracking and preload strategy |
StreamOptionsโ
| Property | Type | Default | Description |
|---|---|---|---|
volume | number | config defaultVolume | 0 to 1 |
pan | number | config defaultPan | -1 (left) to 1 (right) |
loop | boolean | config loopSounds | Start over at the end |
playbackRate | number | config defaultPlaybackRate | 0.5 to 4 |
startTime | number | config defaultStartTime | Second to start from |
trackProgress | boolean | config trackProgress | Dispatch progress events while playing |
preload | 'none' | 'metadata' | 'auto' | 'metadata' | How much the browser fetches before playback. Leave it on metadata for long files |
Exampleโ
import { SoundHub, SoundEventsEnum } from 'soundhub';
const soundHub = new SoundHub();
await soundHub.loadStream('episode-42', '/audio/episode-42.mp3', {
volume: 0.8,
trackProgress: true,
});
soundHub.play('episode-42');
// Everything you already know works the same way
soundHub.setPlaybackRate('episode-42', 1.5);
soundHub.seek('episode-42', 1800); // half an hour in
soundHub.setSoundVolume('episode-42', 0.4);
soundHub.fadeIn('episode-42', 2);
soundHub.addEventListener(SoundEventsEnum.PROGRESS, (event) => {
progressBar.value = event.progressInfo!.progress;
}, { soundId: 'episode-42' });
What a stream cannot doโ
Anything that needs random access to the samples, because they are never all in memory at once:
| Feature | On a stream |
|---|---|
setSoundSprite and playSprite | Throws a clear error. Use loadSound for sprite sheets |
createNewInstance | Not available. One id is one playing stream |
seamlessLoop | Not available. The browser handles looping |
loop_completed event | Never fires, and maxLoops has nothing to count |
The rest behaves exactly as it does for a buffered sound: playback, seeking,
volume, mute, fades, panning, spatial position, playback rate, looping,
getSoundState, progress events and groups.
Drawing a loading barโ
getStreamElement hands you the media element, which
is what knows how much of the file has actually arrived:
const element = soundHub.getStreamElement('episode-42');
if (element?.buffered.length) {
const loadedUpTo = element.buffered.end(element.buffered.length - 1);
bufferBar.style.width = `${(loadedUpTo / soundHub.getDuration('episode-42')) * 100}%`;
}
Cross-origin filesโ
The media element is created with crossOrigin set from your
SoundHubConfig, which defaults to 'anonymous'. A file
served from another domain has to send Access-Control-Allow-Origin. Without it
the browser refuses to route the audio through Web Audio, and you get silence
rather than an error. Files from your own origin need nothing.
Try itโ
The demo below streams a short track so the page stays quick to load, but the code path is the same for a two-hour recording. Watch the darker bar behind the playhead. That is how much of the file has been downloaded so far.