Skip to content

audio

This package implements a real-time audio system. It manages voice pooling, resource loading, and spatial audio calculations.

Features:

  • Automated asset pipeline: Similarly to sprites, it utilizes auto-generated audio enums (AudioName from bonsai:generated package) for fast access to all sound files.
  • Spatial audio: Built-in support for 2D positional audio via playSpatial. It automatically calculates volume and panning based on the distance and relative position between the source and the listener.
  • Voice management: Uses a fixed-size voice pool (MIXER_VOICE_CAPACITY) to recycle audio channels.
  • Playback control: Control over active sounds using functions like pause, resume, stop, rewind and seek.
  • Bus system: Grouping voices via Bus to control volumes for distinct categories.

Usage:

init :: proc() {
audio.playGlobal(.musicTheme, volume = 0.5, isLooped = true)
}
update :: proc() {
if potWalking {
handle := audio.playSpatial(.potWalkSfx, potPosition)
}
if potDead {
audio.stop(handle)
}
}

Bus :: enum {
Master,
SFX,
Music,
}

Audio bus allows to mix the audio and let users control the volume of each element of audio in the game.


DEFAULT_MAX_DISTANCE :: 360

Default distance at which the falloff for spatial audio reaches 0.0 (muted).


DEFAULT_MIN_DISTANCE :: 40

Default distance at which the falloff for spatial audio reaches 1.0 (max volume).


MIXER_VOICE_CAPACITY :: 64

Amount of voices. Describes how many audio clips can play at once.


Mixer :: struct {
lock: sync.Mutex,
voices: [MIXER_VOICE_CAPACITY]Voice,
sounds: map[SoundHandle]Sound,
nextId: SoundHandle,
listenerPosition: gmath.Vector2,
busVolumes: [Bus]f32,
}

Internal state container for the audio mixer.

Holds the thread lock, the pool of Voice, loaded Sound data, and listener configuration.


ParseResult :: struct {
samples: []f32,
channels: int,
sampleRate: int,
}

Container for decoded PCM audio data.

Contains the raw samples converted to f32 (ranging from -1.0 to +1.0) and format metadata. The caller is responsible for freeing the samples slice.


Sound :: struct {
samples: []f32,
channels: int,
sampleRate: int,
}

Structure definition for the “sound”, being an audio clip container.

Holds the raw sample data and format information.


SoundHandle :: distinct u64

ID for a Sound object.


Voice :: struct {
id: SoundHandle,
cursor: int,
position: gmath.Vector2,
volume: f32,
panning: f32, // Values range from -1.0 to +1.0
minDistance: f32,
maxDistance: f32,
bus: Bus,
isActive: bool,
isLooped: bool,
isSpatial: bool,
isPaused: bool,
}

Structure definition for the “voice”, which is essentialy the entity equivalent of the audio system.

It defines all active state variables needed to output a specific instance of Sound.


VoiceHandle :: distinct u64

ID for a Voice object.


getListenerPosition :: proc () -> gmath.Vector2

Returns the current position of the audio listener.


getMixer :: proc () -> ^Mixer

Returns a pointer to the mixer state.

Example:

onVolumeSet :: proc(bus: audio.Bus, volume: f32) {
mixer := audio.getMixer()
mixer.busVolumes[bus] = volume
}

isPlaying :: proc (id: VoiceHandle) -> bool

Returns true if the voice is currently valid and not paused.


load :: proc (name: generated.AudioName) -> SoundHandle

Loads an audio asset from the disk based on the provided AudioName enum.

This function handles reading the file, parsing the audio data (WAV file format), and registering it with the mixer.

Returns 0 if loading or parsing fails.


parseFromBytes :: proc (data: []byte) -> (result: ParseResult, success: bool)

Decodes raw WAV file bytes into a usable audio buffer.

Supports 8-bit (unsigned), 16-bit (signed), and 32-bit (float) WAV formats.

Returns:

  • result: The parsed sample data and metadata.
  • success: False if the header is invalid or the format is unsupported.

pause :: proc (id: VoiceHandle)

Pauses the specific voice. The voice retains its cursor position.


play :: proc {
playGlobal,
playSpatial,
}

Main entry point for playing sounds.

Use playGlobal for UI/Music and playSpatial for in-world sound effects.


playGlobal :: proc (
id: SoundHandle,
volume: f32 = 1.0,
bus: Bus = Bus.Master,
isLooped: bool = false,
panning: f32 = 0.0,
) -> VoiceHandle

Plays a sound in global mode (no spatial positioning).

Ideal for UI sounds, background music, or narration.


playSpatial :: proc (
id: SoundHandle,
volume: f32 = 1.0,
position: gmath.Vector2,
bus: Bus = Bus.Master,
minDistance: f32 = DEFAULT_MIN_DISTANCE,
maxDistance: f32 = DEFAULT_MAX_DISTANCE,
isLooped: bool = false,
) -> VoiceHandle

Plays a sound at a specific position in the world.

Volume and panning are automatically calculated based on the listener’s position.


resume :: proc (id: VoiceHandle)

Resumes a paused voice.


rewind :: proc (id: VoiceHandle)

Resets the playback position of Voice to the beginning.


seek :: proc (id: VoiceHandle, timeSeconds: f32)

Sets the playback position of Voice to a specific time in seconds. If the time is out of bounds, it will be clamped.


setListenerPosition :: proc (position: gmath.Vector2)

Updates the position of the “listener” (usually the camera or player) for spatial audio calculations.


stop :: proc (id: VoiceHandle)

Immediately stops a specific Voice from playing.