SoundState
The SoundStateInfo interface provides complete state information about a sound at a given moment. Use getSoundState() to retrieve the current state of any loaded sound.
Try it
Play, pause and stop the sound and watch every field of SoundStateInfo update live.
isPlaying: falseisPaused: falseisStopped: trueisPlaying, isPaused, and isStopped are mutually exclusive.export interface SoundStateInfo {
progress: number; // Playback position as a ratio from 0 to 1
startTime: number; // Context time the sound started, in seconds
currentTime: number; // Current position in seconds, adjusted for playback rate
elapsedTime: number; // Time played in seconds, adjusted for playback rate
adjustedElapsedTime: number; // Raw elapsed time, not adjusted for playback rate
duration: number; // Duration in seconds, adjusted for playback rate
rawDuration: number | null; // Duration of the buffer itself, in seconds
playbackRate: number | null; // Current playback rate
state: SoundState; // 'playing' | 'paused' | 'stopped'
volume: number; // Current volume, 0 to 1
pan: number; // Current stereo pan, -1 (left) to 1 (right)
panSpatialPosition: { x: number; y: number; z: number }; // Current 3D position
}
The state field uses the SoundState enum:
export enum SoundState {
Playing = "playing",
Paused = "paused",
Stopped = "stopped",
}
Several fields come in both forms because changing the playback rate changes how long a
sound takes to play. duration and currentTime are adjusted for the current rate, which
is what you want for a progress bar. rawDuration and adjustedElapsedTime are the
untouched buffer values. At the default rate of 1 they are identical.
Getting Sound State
const mySoundHub = new SoundHub();
// Load and play a sound
await mySoundHub.loadSound('piano', '/audio/piano.mp3');
mySoundHub.play('piano', { volume: 0.7, pan: -0.5 });
// Get the current state
const state = mySoundHub.getSoundState('piano');
console.log('State:', state.state); // 'playing'
console.log('Volume:', state.volume); // 0.7
console.log('Pan:', state.pan); // -0.5
console.log('Current time:', state.currentTime);
console.log('Duration:', state.duration);
console.log('Progress:', state.progress); // 0 to 1
Asking for a sound that is not loaded returns a neutral, stopped state rather than throwing, so it is safe to call from a render loop:
const state = mySoundHub.getSoundState('does-not-exist');
console.log(state.state); // 'stopped'
console.log(state.duration); // 0
Checking State with Helper Methods
SoundStateInfo carries a single state field rather than separate booleans. For simple
checks the dedicated methods read more clearly:
const mySoundHub = new SoundHub();
await mySoundHub.loadSound('sfx', '/audio/sfx.mp3');
if (mySoundHub.isPlaying('sfx')) {
console.log('Sound is currently playing');
}
if (mySoundHub.isPaused('sfx')) {
console.log('Sound is paused');
}
if (mySoundHub.isStopped('sfx')) {
console.log('Sound is stopped');
}
if (mySoundHub.isSoundLoaded('sfx')) {
console.log('Sound buffer is loaded');
}
Or compare against the enum directly:
import { SoundState } from 'soundhub';
const state = mySoundHub.getSoundState('sfx');
if (state.state === SoundState.Playing) {
console.log('Playing');
}
Building a Progress Bar
progress is already a 0 to 1 ratio, so it maps straight onto a bar:
const mySoundHub = new SoundHub();
await mySoundHub.loadSound('song', '/audio/song.mp3');
mySoundHub.play('song', { trackProgress: true });
function render() {
const state = mySoundHub.getSoundState('song');
bar.style.width = `${state.progress * 100}%`;
label.textContent = `${state.currentTime.toFixed(1)}s / ${state.duration.toFixed(1)}s`;
if (state.state === SoundState.Playing) {
requestAnimationFrame(render);
}
}
render();
Using SoundState with Multiple Instances
When using createNewInstance: true, each instance is registered under its own id and has
its own state:
const mySoundHub = new SoundHub();
await mySoundHub.loadSound('laser', '/audio/laser.mp3');
// Play multiple instances
mySoundHub.play('laser', { createNewInstance: true, volume: 0.5 });
mySoundHub.play('laser', { createNewInstance: true, volume: 1.0, pan: 0.8 });
// Instance ids look like 'laser:1', 'laser:2', ...
mySoundHub.getSoundIds().forEach(id => {
const state = mySoundHub.getSoundState(id);
console.log(`${id}: ${state.state}, volume=${state.volume}`);
});
Reacting to State Changes with Events
const mySoundHub = new SoundHub();
await mySoundHub.loadSound('ambient', '/audio/ambient.mp3');
mySoundHub.addEventListener(SoundEventsEnum.PLAYBACK_RATE_CHANGED, (event) => {
const state = mySoundHub.getSoundState(event.soundId!);
console.log('New playback rate:', state.playbackRate);
});
mySoundHub.addEventListener(SoundEventsEnum.VOLUME_CHANGED, (event) => {
const state = mySoundHub.getSoundState(event.soundId!);
console.log('New volume:', state.volume);
});
Related Methods
getSoundState- Get the full state of a soundisPlaying- Check if a sound is playingisPaused- Check if a sound is pausedisStopped- Check if a sound is stoppedisSoundLoaded- Check if a sound is loadedisSpatialAudioActive- Check if spatial audio is activeisStereoPanActive- Check if stereo pan is active