Audio
SDL_mixer setup, sound and music nodes, mix buses, bus effects, level meters and adaptive music.
Files: audio.odin, audio_nodes.odin, audio_effects.odin, audio_meter.odin, music_layers.odin.
File overviews
Section titled “File overviews”audio.odin
Section titled “audio.odin”Audio: SDL3_mixer, playing on the default device. The same on the desktop and in the browser, where the web export links an SDL3_mixer built with Emscripten (WAV, OGG, MP3, FLAC, AIFF, VOC). Browsers only start sound after the player clicks or presses a key on the page; SDL resumes it then by itself.
Play sounds with Game_PlaySound, or with SDL3_mixer directly (game.mixer, and
game.resourceManager->loadAudio for cached sounds).
audio_nodes.odin
Section titled “audio_nodes.odin”Sound nodes and mix buses, on SDL3_mixer.
AudioPlayerplays a sound: volume, pitch (playback speed), pan, looping, starting by itself.AudioPlayer2Dis one in the world: panned by where it is relative to the listener (the scene’s camera, or the middle of the game area), and quieter with distance (silent pastmaxDistance;attenuationshapes the curve).MusicPlayerstreams music (a Music asset: decoded as it plays, not loaded whole), with fades, seeking and the same volume, pitch and pan.
Every player goes to a bus (player.bus; “” is Master), and every bus to Master.
A bus has a volume, a pan and a mute; what a player sounds like is its own settings times its
bus’s times Master’s (Audio_Output), so changing a bus changes everything on it at once. The
player’s and its bus’s gains go on its track (every frame); Master’s go on the final mix
(audio_meter.odin), so each bus’s level meter reads it after its fader, Master’s the output.
Projects list their buses in project.xml (<bus name="Music" volume="0.8"/>, the editor’s Mixer
tab); code can add more (Audio_AddBus). Each bus has an SDL3_mixer group its
players’ tracks join, where effects will go.
Players in a scene that the game switches away from stop; ones that start by themselves start again when it’s back. Without an audio device everything still works, silently.
Example
jump := cast(^e.AudioPlayer)e.Scene_FindNode(scene, "JumpSound")e.AudioPlayer_Play(jump)e.Audio_SetBusVolume("Music", 0.5)music := cast(^e.MusicPlayer)e.Scene_FindNode(scene, "Theme")e.MusicPlayer_FadeOut(music, 2)audio_effects.odin
Section titled “audio_effects.odin”Audio effects on mix buses: what goes through a bus can be filtered (low-pass for muffled sound under water or behind a wall, high-pass for a thin radio voice), echoed, or given a room’s reverb.
A bus has a chain of up to AUDIO_MAX_EFFECTS effects (AudioEffect: plain settings), run in
order on everything the bus mixed, before its meter; Master’s run on the final mix, before
Master’s volume and pan. In project.xml they’re <effect> children of a <bus>:
<bus name="Music" volume="0.8"> <effect kind="LowPass" cutoff="900"/> <effect kind="Reverb" roomSize="0.7" mix="0.25"/></bus>From code, Audio_SetBusEffects replaces a bus’s chain and Audio_SetBusEffect changes one
effect (a low-pass sweeping down as the player dives, say); Audio_GetBusEffects reads them.
The effects run on the audio thread, in the bus group’s post-mix callback (audio_meter.odin). A chain’s settings are changed under its lock, which the audio thread only tries to take: when it’s busy, that block plays with the previous settings, so the game never waits for audio and audio never waits for the game. Filters, delay lines and reverb tanks keep their state across blocks, sized for the stereo channels (other channels pass through untouched).
audio_meter.odin
Section titled “audio_meter.odin”Level meters and output stages for mix buses, on SDL3_mixer’s mix callbacks (they run on the audio thread; everything here is shared through atomics).
A meter attached to a group (AudioMeter_AttachGroup) measures what the group’s tracks mixed
together; one attached to a mixer (AudioMeter_AttachMixer) is the final stage: it applies its
gains (AudioMeter_SetGains) to the whole mix, then measures it. The game’s buses use both:
each bus’s tracks carry the player’s and the bus’s gains, so its group’s meter reads the bus
after its fader; Master’s gain and pan are applied to the final mix, which Master’s meter reads.
Editors use the same pieces for their own playback.
Example
left, right := e.Audio_GetBusLevel("Music")db := e.AudioLevel_ToDecibels(max(left, right))music_layers.odin
Section titled “music_layers.odin”Music that changes with the game: crossfades between songs, and adaptive layers.
- Crossfades (
MusicPlayer_CrossfadeTo): the song playing fades out while the next fades in, both at once, on the player’s bus. The old song keeps playing until its fade ends, then is freed. - Layers (
MusicPlayer_AddLayer, a MusicPlayer’s<layer>children in scene files): extra music files played in step with the main one (stems of one song: drums, strings, a danger theme), each at its own volume. They start on the same sample as the main music (SDL3_mixer’s tags) and loop with it.MusicPlayer_SetLayerVolumefades a layer in or out (combat starts: the drums come in over two seconds). Each layer goes through the player’s volume, pan and bus, times its own volume.
Layers’ files should be as long as the main one, so they loop together. A crossfade fades the layers out with the old song (the new song starts without them: add its own).
Example
// Given: music: ^e.MusicPlayere.MusicPlayer_AddLayer(music, "drums", "music/battle_drums.ogg", volume = 0)e.AudioPlayer_Play(music)// Later, in a fight:e.MusicPlayer_SetLayerVolume(music, "drums", 1, seconds = 2)AudioOutputSettings
Section titled “AudioOutputSettings”AudioOutputSettings :: struct { // Samples per second (44100, 48000...); 0 for the device's own frequency: int, // 1 mono, 2 stereo, up to 8; 0 for the device's own channels: int, // The playback device, by its name (`Audio_PlaybackDevices`); "" for the system's default device: string,}How a game’s sound output is opened: the format the mixer asks the device for (SDL converts it to
whatever the device really is), and which device. Projects set the format (project.xml’s
audioFrequency, audioChannels); the device is the machine’s choice (the player’s
--audio-device, the editor’s Settings for Play).
Asking for the device’s own format (0 for both) was the old way, and failed on outputs whose channel layout SDL doesn’t take (“Unsupported audio channels”: multichannel interfaces, some Bluetooth and HDMI outputs on macOS).
struct. Source: audio.odin:30
AudioBus
Section titled “AudioBus”AudioBus :: struct { // Its name (MASTER_BUS for Master), in the game's memory name: string, // 0 (silent) to 1 (unchanged); up to 2 boosts volume: f32, // -1 left, 0 center, 1 right pan: f32, // Silences it (and everything on it), keeping its volume mute: bool, // Its SDL3_mixer group (nil without a device) group: ^mix.Group, // Its level (`Audio_GetBusLevel`); Master's is the final mix, where its gain and pan apply meter: ^AudioMeter, // Its effects (audio_effects.odin), run on what it mixed before its meter effects: ^AudioEffectChain,}A mix bus: what goes through it is scaled by its volume, panned by its pan, silenced by mute
struct. Source: audio_nodes.odin:43
AudioBusData
Section titled “AudioBusData”AudioBusData :: struct { // The bus's name ("Master" sets Master's own values) name: string, // 0 to 1, up to 2 boosts volume: f32, // -1 left, 0 center, 1 right pan: f32, // Starts muted mute: bool, // Its effects, in order (`<effect>` children: audio_effects.odin) effects: [dynamic]AudioEffect,}A bus in project.xml: <bus name="Music" volume="0.8" pan="0" mute="false"/> (format 10)
struct. Source: audio_nodes.odin:61
AudioSettings
Section titled “AudioSettings”AudioSettings :: struct { // 0 to 1 (up to 2 boosts) volume: f32, // Playback speed: 1 is normal, 2 an octave up and twice as fast, 0.5 an octave down pitch: f32, // -1 left, 0 center, 1 right pan: f32, // Plays again from the start when it ends, until stopped loop: bool, // Starts when its scene first updates (and again each time the game comes back to it) autostart: bool, // AudioPlayer2D: silent this far (px) from the listener (default 1000) maxDistance: f32, // AudioPlayer2D: how the volume falls with distance (1 linear, 2 faster; `Audio_Positional`) attenuation: f32, // MusicPlayer: seconds to fade in when it starts (0: no fade) fadeIn: f32, // MusicPlayer: seconds to fade out when `AudioPlayer_Stop` stops it (0: no fade) fadeOut: f32,}A sound node’s settings (scene files store those that differ from DEFAULT_AUDIO_SETTINGS)
struct. Source: audio_nodes.odin:75
AudioPlayerKind
Section titled “AudioPlayerKind”AudioPlayerKind :: enum { // A sound, not placed Sound, // A sound in the world: panned and fading with distance from the listener Positional, // Music, streamed Music,}Which kind of sound node
enum. Source: audio_nodes.odin:105
AudioPlayer
Section titled “AudioPlayer”AudioPlayer :: struct { // The node part: transform (a 2D player's position), hierarchy, hooks using node: Node, // Sound, Positional or Music (set by `CreateAudioPlayer`) kind: AudioPlayerKind, // Volume, pitch, pan, looping... (read every frame) settings: AudioSettings, // The bus it goes to ("" or "Master": straight to Master); in the node's memory bus: string, // The sound or music file, relative to the project folder (`AudioPlayer_SetSound`) sound: string, // The loaded sound or music (nil: none, or no audio device) audio: ^mix.Audio, // Music is the node's own (streamed); sounds are the resource manager's (shared) ownsAudio: bool, // Its SDL3_mixer track (made by the first Play; nil without a device) track: ^mix.Track, // Playing (or paused) since the last Play, until it stops or ends playing: bool, // Paused by `AudioPlayer_Pause` paused: bool, // Autostart happened this time its scene is current started: bool, // MusicPlayer: layers played in step with it, and songs fading out after a crossfade // (music_layers.odin) layers: [dynamic]MusicLayer, fading: [dynamic]FadingTrack, // What it played ended on its own (not looping, not stopped); noticed in its update onFinished: EventSubject `event:"What it played ended on its own (not when stopped; a looping sound never ends)"`,}A node that plays a sound (AudioPlayer, AudioPlayer2D) or music (MusicPlayer: the same struct,
kind says which). Change settings at any time: they apply the next frame.
struct. Source: audio_nodes.odin:118
AudioPlayer2D
Section titled “AudioPlayer2D”AudioPlayer2D :: AudioPlayerA positional sound player; the same struct as AudioPlayer
type. Source: audio_nodes.odin:150
MusicPlayer
Section titled “MusicPlayer”MusicPlayer :: AudioPlayerA music player; the same struct as AudioPlayer
type. Source: audio_nodes.odin:152
AudioEffectKind
Section titled “AudioEffectKind”AudioEffectKind :: enum { // Lets lows through, cuts above `cutoff`: muffled, far, under water LowPass, // Lets highs through, cuts below `cutoff`: thin, a radio, a phone HighPass, // Repeats the sound after `delay` seconds, each repeat `feedback` as loud Echo, // A room's reflections: `roomSize` (small to hall), `damping` (bright to dull) Reverb,}What an effect does
enum. Source: audio_effects.odin:33
AudioEffect
Section titled “AudioEffect”AudioEffect :: struct { // What it does kind: AudioEffectKind, // Skipped (kept in the chain, doing nothing) bypass: bool, // LowPass, HighPass: the corner frequency in Hz (20 to 20000) cutoff: f32 `min:"20" max:"20000"`, // LowPass, HighPass: how much the corner rings (0.5 soft, 0.707 flat, up to 10 a sharp peak) resonance: f32 `min:"0.3" max:"10"`, // Echo: seconds between repeats (0.01 to 2) delay: f32 `min:"0.01" max:"2"`, // Echo: how loud each repeat is, relative to the one before (0 to 0.95) feedback: f32 `min:"0" max:"0.95"`, // Reverb: 0 a small room to 1 a hall roomSize: f32 `min:"0" max:"1"`, // Reverb: 0 bright reflections to 1 dull ones damping: f32 `min:"0" max:"1"`, // Echo, Reverb: how much of the effect is heard (0 none, 1 only the effect) mix: f32 `min:"0" max:"1"`,}One effect on a bus (scene and project files store those that differ from
DEFAULT_AUDIO_EFFECT).
struct. Source: audio_effects.odin:48
AudioEffectChain
Section titled “AudioEffectChain”AudioEffectChain :: struct { lock: sync.Atomic_Mutex, // The effects, `count` of them effects: [AUDIO_MAX_EFFECTS]AudioEffect, count: int, // Each effect's state (heap, made by AudioEffects_Set for its kind, freed when replaced), and // the sample rate they were set up for states: [AUDIO_MAX_EFFECTS]^EffectState, rate: f32,}A bus’s effect chain and the state its effects keep between blocks (filters’ memory, the echo’s
delay line, the reverb’s tanks). Shared with the audio thread: change it through
AudioEffects_Set only.
struct. Source: audio_effects.odin:84
AudioMeter
Section titled “AudioMeter”AudioMeter :: struct { // The loudest sample per side since the last read: f32 bits, atomic peakLeft, peakRight: u32, // A final stage's gain per side: f32 bits, atomic gainLeft, gainRight: u32, // Whether the gains are applied (only a mixer's meter) applyGains: bool, // Effects run on the block before it's measured (a bus's chain: audio_effects.odin; nil none) effects: ^AudioEffectChain,}A level meter: a stage’s peak levels since they were last read, and (a final stage) the gains it applies. Shared with the audio thread: read and set it through the procedures, never the fields. Zero-initialized is ready to attach.
struct. Source: audio_meter.odin:30
MusicLayer
Section titled “MusicLayer”MusicLayer :: struct { // Its name (to set its volume), in the node's memory name: string, // Its music file, relative to the project folder, in the node's memory sound: string, // The loaded music (the player's own; nil without a device or when it couldn't be read) audio: ^mix.Audio, // Its SDL3_mixer track (made when added; nil without a device) track: ^mix.Track, // Its volume now (0 to 1, up to 2 boosts), the volume it's fading to, and how fast (per second) volume, target, speed: f32,}A layer of a MusicPlayer: a music file played in step with the main one
struct. Source: music_layers.odin:34
MusicLayerData
Section titled “MusicLayerData”MusicLayerData :: struct { name: string, sound: string, volume: f32,}A layer in a scene file: <layer name="drums" sound="music/drums.ogg" volume="0"/> (format 13)
struct. Source: music_layers.odin:48
Procedures
Section titled “Procedures”Audio_Init
Section titled “Audio_Init”Audio_Init :: proc(output := DEFAULT_AUDIO_OUTPUT) -> (^mix.Mixer, bool)Starts SDL3_mixer and opens a playback device (Audio_OpenMixer). A machine without one (or a
browser that refuses) isn’t an error: the game runs silently.
Inputs
- output: The format and device to ask for
Returns
- The mixer (nil when silent), and false only if SDL3_mixer itself couldn’t start
procedure. Source: audio.odin:52
Audio_OpenMixer
Section titled “Audio_OpenMixer”Audio_OpenMixer :: proc(output := DEFAULT_AUDIO_OUTPUT) -> ^mix.MixerOpens a playback device for SDL3_mixer, trying until one opens: the asked device (else the
default) with the asked format, then stereo at 48 and 44.1 kHz, mono, 16-bit stereo, the
device’s own format; then the default device; then every other device in stereo. Logs the device
and format it got (“Audio: MacBook Speakers, 48000 Hz, 2 channels”), and what failed before it.
SDL3_mixer must be started (mix.Init; Audio_Init does it).
Inputs
- output: The format and device to ask for
Returns
- The mixer, or nil (logged) when no device opens: the game runs silently
Example
// Given: mixer: ^mix.Mixer (an editor's own previews, apart from any game)mixer = e.Audio_OpenMixer({frequency = 44100, channels = 2})procedure. Source: audio.odin:77
Audio_PlaybackDevices
Section titled “Audio_PlaybackDevices”Audio_PlaybackDevices :: proc(allocator := context.allocator) -> []stringThe names of the playback devices SDL sees now (for picking one: AudioOutputSettings.device, the
player’s --audio-device). SDL’s audio must be started (SDL_Init’s .AUDIO).
Inputs
- allocator: For the list and the names
Returns
- The names; empty when there’s no device (or no audio)
procedure. Source: audio.odin:137
Audio_Quit
Section titled “Audio_Quit”Audio_Quit :: proc(mixer: ^mix.Mixer)Closes the playback device and SDL3_mixer. Application_Cleanup calls it.
Inputs
- mixer: The mixer from
Audio_Init(nil when silent)
procedure. Source: audio.odin:153
Audio_Load
Section titled “Audio_Load”Audio_Load :: proc(mixer: ^mix.Mixer, path: string, predecode: bool) -> ^mix.AudioLoads a sound file, from the data pack if it’s there, else from disk. Most code loads through
ResourceManager (loadAudio), which caches the sound.
Inputs
- mixer: The mixer to load it for
- path: The file’s path
- predecode: Decode it all now (short effects); false streams it while it plays (music)
Returns
- The sound (free it with
Audio_Destroy), or nil if it can’t be read (SDL’s error says why)
procedure. Source: audio.odin:170
Audio_Destroy
Section titled “Audio_Destroy”Audio_Destroy :: proc(audio: ^mix.Audio)Frees a sound loaded with Audio_Load. Sounds in the resource manager are freed by it instead.
Inputs
- audio: The sound
procedure. Source: audio.odin:184
Game_PlaySoundFile
Section titled “Game_PlaySoundFile”Game_PlaySoundFile :: proc(game: ^Game, path: string) -> boolPlays a sound file once, loading it the first time (it stays cached in the resource manager).
Game_PlaySound with a path.
Inputs
- game: The game
- path: The sound file, relative to the project folder (
game.projectDir) like other assets
Returns
- false if there’s no audio device, or (logged) it couldn’t be loaded or played
Example
e.Game_PlaySound(game, "assets/laser.wav") // or a SoundAsset (assets.odin)procedure. Source: audio.odin:202
CreateAudioPlayer
Section titled “CreateAudioPlayer”CreateAudioPlayer :: proc(scene: ^Scene, kind := AudioPlayerKind.Sound, settings := DEFAULT_AUDIO_SETTINGS) -> ^AudioPlayerCreates a sound node of a kind, on Master. Set its sound with AudioPlayer_SetSound (after it’s
in a scene of a game) and its bus with AudioPlayer_SetBus.
Inputs
- scene: The scene
- kind: Sound, Positional (AudioPlayer2D) or Music (MusicPlayer)
- settings: Volume, pitch, pan… (DEFAULT_AUDIO_SETTINGS unless given)
Returns
- The player
Example
music := e.CreateAudioPlayer(scene, .Music)music.settings.loop = truemusic.settings.fadeIn = 1.5e.AudioPlayer_SetSound(music, "music/theme.ogg")e.AudioPlayer_SetBus(music, "Music")e.AudioPlayer_Play(music)procedure. Source: audio_nodes.odin:174
AudioPlayer_SetSound
Section titled “AudioPlayer_SetSound”AudioPlayer_SetSound :: proc(player: ^AudioPlayer, path: string)Sets what a player plays, stopping what played (no fade). Sounds are loaded once by the resource manager and shared; music streams from its file and is the node’s own. Nothing is loaded when the scene has no game or the game has no audio device (a warning when music can’t be opened).
Inputs
- player: The player
- path: A sound or music file, relative to the project folder (“” for none)
procedure. Source: audio_nodes.odin:197
AudioPlayer_SetBus
Section titled “AudioPlayer_SetBus”AudioPlayer_SetBus :: proc(player: ^AudioPlayer, bus: string)Sets the bus a player goes to; it takes effect at once, also while playing.
Inputs
- player: The player
- bus: The bus’s name (“” or “Master” for Master; an unknown bus is Master too)
procedure. Source: audio_nodes.odin:222
AudioPlayer_Play
Section titled “AudioPlayer_Play”AudioPlayer_Play :: proc(player: ^AudioPlayer)Plays from the start (again, if it was playing), looping if settings.loop. Music fades in over
settings.fadeIn. Without a sound or a device, it only marks the player as playing.
Inputs
- player: The player
procedure. Source: audio_nodes.odin:234
AudioPlayer_Stop
Section titled “AudioPlayer_Stop”AudioPlayer_Stop :: proc(player: ^AudioPlayer, fade := true)Stops playing. Music fades out over settings.fadeOut unless fade is false.
Inputs
- player: The player
- fade: Fade music out (sounds stop at once either way)
procedure. Source: audio_nodes.odin:263
AudioPlayer_Pause
Section titled “AudioPlayer_Pause”AudioPlayer_Pause :: proc(player: ^AudioPlayer)Pauses where it is (AudioPlayer_Resume carries on). Does nothing if it isn’t playing.
Inputs
- player: The player
procedure. Source: audio_nodes.odin:280
AudioPlayer_Resume
Section titled “AudioPlayer_Resume”AudioPlayer_Resume :: proc(player: ^AudioPlayer)Carries on from where AudioPlayer_Pause paused it. Does nothing if it isn’t paused.
Inputs
- player: The player
procedure. Source: audio_nodes.odin:293
AudioPlayer_IsPlaying
Section titled “AudioPlayer_IsPlaying”AudioPlayer_IsPlaying :: proc(player: ^AudioPlayer) -> boolWhether a player is playing.
Inputs
- player: The player
Returns
- true if it was played and isn’t paused, stopped or finished (a finished sound is noticed on its next update)
procedure. Source: audio_nodes.odin:310
MusicPlayer_FadeOut
Section titled “MusicPlayer_FadeOut”MusicPlayer_FadeOut :: proc(player: ^MusicPlayer, seconds: f32)Fades music out and stops it, whatever its settings.fadeOut.
Inputs
- player: The music player
- seconds: How long the fade takes (0: stops at once)
procedure. Source: audio_nodes.odin:321
MusicPlayer_GetPosition
Section titled “MusicPlayer_GetPosition”MusicPlayer_GetPosition :: proc(player: ^MusicPlayer) -> f32Where the music is.
Inputs
- player: The music player
Returns
- Seconds from its start (0 before it has played)
procedure. Source: audio_nodes.odin:338
MusicPlayer_GetDuration
Section titled “MusicPlayer_GetDuration”MusicPlayer_GetDuration :: proc(player: ^MusicPlayer) -> f32How long the music is.
Inputs
- player: The music player
Returns
- Seconds (0 if unknown, as for some streams, or with no music loaded)
procedure. Source: audio_nodes.odin:352
MusicPlayer_Seek
Section titled “MusicPlayer_Seek”MusicPlayer_Seek :: proc(player: ^MusicPlayer, seconds: f32)Jumps to a time in the music. Does nothing before it has played.
Inputs
- player: The music player
- seconds: From the start (negative is 0)
procedure. Source: audio_nodes.odin:366
Audio_SetBuses
Section titled “Audio_SetBuses”Audio_SetBuses :: proc(game: ^Game, buses: []AudioBusData)Sets the game’s buses, replacing any: Game_LoadProject does it with the project’s. Master is
always there (the first), whether listed or not. The buses live in the game’s memory.
Inputs
- game: The game
- buses: The buses (a “Master” entry sets Master’s volume, pan and mute)
procedure. Source: audio_nodes.odin:383
Audio_AddBus
Section titled “Audio_AddBus”Audio_AddBus :: proc(name: string, volume: f32 = 1, game: ^Game = nil) -> ^AudioBusAdds a bus from code, going to Master. If one by that name already exists, it’s kept as it is.
Inputs
- name: The bus’s name
- volume: Its volume (0 to 1, up to 2 boosts)
- game: The game (nil: the current game)
Returns
- The bus (nil without a game). The pointer is valid until the next bus is added
Example
e.Audio_AddBus("Voices", 0.9)e.Audio_SetBusMute("Voices", true)procedure. Source: audio_nodes.odin:438
Audio_GetBus
Section titled “Audio_GetBus”Audio_GetBus :: proc(name: string, game: ^Game = nil) -> ^AudioBusA bus by name. A game with no buses yet gets Master.
Inputs
- name: The bus’s name (“” is Master)
- game: The game (nil: the current game)
Returns
- The bus, or nil if there’s none by that name. The pointer is valid until a bus is added
procedure. Source: audio_nodes.odin:466
Audio_SetBusVolume
Section titled “Audio_SetBusVolume”Audio_SetBusVolume :: proc(name: string, volume: f32, game: ^Game = nil)Sets a bus’s volume (everything on it changes from the next frame). Unknown buses are ignored.
Inputs
- name: The bus’s name (“” is Master)
- volume: 0 (silent) to 1 (unchanged), up to 2 boosts; negative is 0
- game: The game (nil: the current game)
Example
e.Audio_SetBusVolume("Music", 0.5)procedure. Source: audio_nodes.odin:486
Audio_SetBusPan
Section titled “Audio_SetBusPan”Audio_SetBusPan :: proc(name: string, pan: f32, game: ^Game = nil)Sets a bus’s pan (a balance). Unknown buses are ignored.
Inputs
- name: The bus’s name (“” is Master)
- pan: -1 left, 0 center, 1 right (clamped)
- game: The game (nil: the current game)
procedure. Source: audio_nodes.odin:498
Audio_SetBusMute
Section titled “Audio_SetBusMute”Audio_SetBusMute :: proc(name: string, mute: bool, game: ^Game = nil)Mutes or unmutes a bus, keeping its volume. Unknown buses are ignored.
Inputs
- name: The bus’s name (“” is Master)
- mute: Silence it
- game: The game (nil: the current game)
procedure. Source: audio_nodes.odin:510
Audio_Output
Section titled “Audio_Output”Audio_Output :: proc(game: ^Game, bus: string, volume, pan: f32, master := true) -> (gain, left, right: f32)What a player on a bus sounds like: its volume times its bus’s and Master’s (0 if either is muted), and its stereo gains, its pan’s times theirs. A pan is a balance: centered changes nothing, fully right silences the left side. An unknown bus is Master.
Inputs
- game: The game (nil: the current game)
- bus: The player’s bus (“” is Master)
- volume, pan: The player’s own (positional players: with distance and position applied)
- master: Include Master’s stage (false: the track’s part; the final mix applies Master’s)
Returns
- The track gain, and the left and right gains
procedure. Source: audio_nodes.odin:528
Audio_Positional
Section titled “Audio_Positional”Audio_Positional :: proc(position, listener: [2]f32, settings: AudioSettings, viewWidth: f32) -> (volume, pan: f32)A positional sound’s volume factor and pan, from its world position and the listener’s (the
scene’s camera, or the middle of the game area). Silent at maxDistance; between, the volume
falls as (1 - distance / maxDistance) ^ attenuation. Pan follows the horizontal offset, full at
half the view’s width.
Inputs
- position: The sound’s world position (pixels)
- listener: The listener’s world position
- settings: The player’s settings (
maxDistance,attenuation) - viewWidth: The width the listener hears across, in pixels (the game’s width)
Returns
- volume: 0 to 1, to multiply the player’s volume by
- pan: -1 to 1, to add to the player’s pan
procedure. Source: audio_nodes.odin:561
Audio_SetBusEffects
Section titled “Audio_SetBusEffects”Audio_SetBusEffects :: proc(name: string, effects: []AudioEffect, game: ^Game = nil)Replaces a bus’s effects (audio_effects.odin): they run on what it mixes from its next block.
Inputs
- name: The bus (“” is Master)
- effects: Up to AUDIO_MAX_EFFECTS, in order
- game: The game (nil: the current game)
Example
// Under water: everything mufflede.Audio_SetBusEffects("", {e.AudioEffect_Make(.LowPass)})procedure. Source: audio_nodes.odin:605
Audio_SetBusEffect
Section titled “Audio_SetBusEffect”Audio_SetBusEffect :: proc(name: string, index: int, effect: AudioEffect, game: ^Game = nil)Changes one of a bus’s effects (a filter swept while the game plays keeps sounding smooth).
Example
// Given: depth: f32muffle := e.AudioEffect_Make(.LowPass)muffle.cutoff = 4000 - depth * 30e.Audio_SetBusEffect("Music", 0, muffle)procedure. Source: audio_nodes.odin:618
Audio_GetBusEffects
Section titled “Audio_GetBusEffects”Audio_GetBusEffects :: proc(name: string, game: ^Game = nil) -> []AudioEffectA bus’s effects (a temp copy; none for a bus that doesn’t exist)
procedure. Source: audio_nodes.odin:623
AudioEffect_Make
Section titled “AudioEffect_Make”AudioEffect_Make :: proc(kind: AudioEffectKind) -> AudioEffectAn effect of a kind with the default settings
procedure. Source: audio_effects.odin:73
AudioEffects_Set
Section titled “AudioEffects_Set”AudioEffects_Set :: proc(chain: ^AudioEffectChain, effects: []AudioEffect)Replaces a chain’s effects (the audio thread picks them up on its next block; their state starts over). At most AUDIO_MAX_EFFECTS are kept.
procedure. Source: audio_effects.odin:104
AudioEffects_Destroy
Section titled “AudioEffects_Destroy”AudioEffects_Destroy :: proc(chain: ^AudioEffectChain)Frees a chain’s effects’ state (the chain must no longer be attached: the bus’s group destroyed)
procedure. Source: audio_effects.odin:121
AudioEffects_Get
Section titled “AudioEffects_Get”AudioEffects_Get :: proc(chain: ^AudioEffectChain) -> []AudioEffectA copy of a chain’s effects (temp allocated)
procedure. Source: audio_effects.odin:129
AudioEffects_SetOne
Section titled “AudioEffects_SetOne”AudioEffects_SetOne :: proc(chain: ^AudioEffectChain, index: int, effect: AudioEffect)Changes one effect’s settings, keeping its state when its kind stays (a filter swept while it plays doesn’t click).
procedure. Source: audio_effects.odin:142
AudioEffects_Process
Section titled “AudioEffects_Process”AudioEffects_Process :: proc "contextless" (chain: ^AudioEffectChain, pcm: [^]f32, samples, channels: int, rate: f32)Runs a chain over a block of interleaved samples, in place. Called on the audio thread (from a post-mix callback); when the chain is being changed, the block passes through unchanged.
Inputs
- chain: The chain (nil does nothing)
- pcm: Interleaved samples,
samplesof them - channels: Channels per frame
- rate: Frames per second
procedure. Source: audio_effects.odin:170
AudioMeter_Read
Section titled “AudioMeter_Read”AudioMeter_Read :: proc(meter: ^AudioMeter) -> (left, right: f32)The loudest samples per side since the last read, then starts over. A meter shows the largest level it read and lets it fall; reading every frame suits that.
Inputs
- meter: The meter (nil reads silence)
Returns
- left, right: 0 (silence) to 1 (full scale); more when the mix clips
procedure. Source: audio_meter.odin:51
AudioMeter_SetGains
Section titled “AudioMeter_SetGains”AudioMeter_SetGains :: proc(meter: ^AudioMeter, left, right: f32)Sets the gains a final stage (AudioMeter_AttachMixer) applies to the mix, from the next mixed
block. A group’s meter ignores them.
Inputs
- meter: The meter (nil does nothing)
- left, right: Gains per side (1 unchanged, 0 silences that side; negative is 0)
procedure. Source: audio_meter.odin:66
AudioMeter_AttachGroup
Section titled “AudioMeter_AttachGroup”AudioMeter_AttachGroup :: proc(meter: ^AudioMeter, group: ^mix.Group)Measures a group’s mix: the meter becomes the group’s post-mix callback (replacing any). The meter must outlive the attachment (until the group is destroyed).
Inputs
- meter: The meter
- group: The SDL3_mixer group (nil does nothing)
procedure. Source: audio_meter.odin:80
AudioMeter_AttachMixer
Section titled “AudioMeter_AttachMixer”AudioMeter_AttachMixer :: proc(meter: ^AudioMeter, mixer: ^mix.Mixer)Makes the meter a mixer’s final stage (its post-mix callback, replacing any): its gains are applied to the whole mix, then it’s measured. A meter whose gains are both 0 starts at 1, 1. The meter must outlive the attachment.
Inputs
- meter: The meter
- mixer: The SDL3_mixer mixer (nil does nothing)
procedure. Source: audio_meter.odin:94
Audio_GetBusLevel
Section titled “Audio_GetBusLevel”Audio_GetBusLevel :: proc(name: string, game: ^Game = nil) -> (left, right: f32)A bus’s level since the last call (for Master: the final mix). Reading it starts the peaks over, so one reader per bus (a mixer view reading every frame).
Inputs
- name: The bus’s name (“” is Master)
- game: The game (nil: the current game)
Returns
- left, right: 0 to 1 (0 without an audio device, or for a bus that doesn’t exist)
Example
left, right := e.Audio_GetBusLevel("Music")db := e.AudioLevel_ToDecibels(max(left, right))// A bar over the top 60 dBbar := sdl.FRect{20, 20, 200 * max(1 + db / 60, 0), 8}e.DrawRectangle(renderer, &bar, {80, 220, 120, 255})procedure. Source: audio_meter.odin:119
AudioLevel_ToDecibels
Section titled “AudioLevel_ToDecibels”AudioLevel_ToDecibels :: proc(level: f32) -> f32A level in decibels relative to full scale.
Inputs
- level: 0 to 1 (more when clipping)
Returns
- dBFS: 0 at full scale, negative below it, never under
AUDIO_SILENCE_DB(silence)
procedure. Source: audio_meter.odin:133
MusicPlayer_AddLayer
Section titled “MusicPlayer_AddLayer”MusicPlayer_AddLayer :: proc(player: ^MusicPlayer, name, path: string, volume: f32 = 1) -> ^MusicLayerAdds a layer to a music player. When the player is playing, the layer starts at once, in step.
Inputs
- player: The music player
- name: The layer’s name (a name already used replaces that layer)
- path: Its music file, relative to the project folder
- volume: Its volume (0 silent: fade it in later with MusicPlayer_SetLayerVolume)
Returns
- The layer (nil when the player isn’t a MusicPlayer); valid until a layer is added or removed
procedure. Source: music_layers.odin:73
MusicPlayer_RemoveLayer
Section titled “MusicPlayer_RemoveLayer”MusicPlayer_RemoveLayer :: proc(player: ^MusicPlayer, name: string) -> boolRemoves a layer (stopping it). Returns whether there was one by that name
procedure. Source: music_layers.odin:109
MusicPlayer_SetLayerVolume
Section titled “MusicPlayer_SetLayerVolume”MusicPlayer_SetLayerVolume :: proc(player: ^MusicPlayer, name: string, volume: f32, seconds: f32 = 0) -> boolFades a layer to a volume.
Inputs
- player: The music player
- name: The layer
- volume: 0 silent, 1 full (up to 2 boosts)
- seconds: How long the fade takes (0: at once)
Returns
- Whether there was a layer by that name
procedure. Source: music_layers.odin:132
MusicPlayer_GetLayer
Section titled “MusicPlayer_GetLayer”MusicPlayer_GetLayer :: proc(player: ^MusicPlayer, name: string) -> ^MusicLayerA layer by name (nil: none)
procedure. Source: music_layers.odin:147
MusicPlayer_CrossfadeTo
Section titled “MusicPlayer_CrossfadeTo”MusicPlayer_CrossfadeTo :: proc(player: ^MusicPlayer, path: string, seconds: f32)Fades from the song playing to another: the old one fades out while the new one fades in, over
seconds. Not playing: it plays the new one, fading in. The old song’s layers fade out with it.
Inputs
- player: The music player
- path: The next song, relative to the project folder
- seconds: How long the crossfade takes
Example
// Given: music: ^e.MusicPlayere.MusicPlayer_CrossfadeTo(music, "music/boss.ogg", 3)procedure. Source: music_layers.odin:166
Constants and variables
Section titled “Constants and variables”DEFAULT_AUDIO_OUTPUT
Section titled “DEFAULT_AUDIO_OUTPUT”DEFAULT_AUDIO_OUTPUT :: AudioOutputSettings{frequency = 48000, channels = 2}Stereo at 48 kHz: what every device can be given (a project’s output without settings)
constant. Source: audio.odin:40
MASTER_BUS
Section titled “MASTER_BUS”MASTER_BUS :: "Master"The bus every player and bus ends in
constant. Source: audio_nodes.odin:40
DEFAULT_AUDIO_SETTINGS
Section titled “DEFAULT_AUDIO_SETTINGS”DEFAULT_AUDIO_SETTINGS :: AudioSettings{ volume = 1, pitch = 1, maxDistance = 1000, attenuation = 1,}A new sound node’s settings: full volume, normal pitch, centered, 1000 px reach, linear falloff
constant. Source: audio_nodes.odin:97
AUDIO_MAX_EFFECTS
Section titled “AUDIO_MAX_EFFECTS”AUDIO_MAX_EFFECTS :: 8The most effects a bus has
constant. Source: audio_effects.odin:30
DEFAULT_AUDIO_EFFECT
Section titled “DEFAULT_AUDIO_EFFECT”DEFAULT_AUDIO_EFFECT :: AudioEffect{cutoff = 1000, resonance = 0.707, delay = 0.3, feedback = 0.4, roomSize = 0.5, damping = 0.5, mix = 0.3}A new effect’s settings
constant. Source: audio_effects.odin:70
AUDIO_MAX_ECHO
Section titled “AUDIO_MAX_ECHO”AUDIO_MAX_ECHO :: 2The longest echo (seconds)
constant. Source: audio_effects.odin:96
AUDIO_MAX_RATE
Section titled “AUDIO_MAX_RATE”AUDIO_MAX_RATE :: 96000The highest sample rate the delay lines are sized for
constant. Source: audio_effects.odin:98
AUDIO_SILENCE_DB
Section titled “AUDIO_SILENCE_DB”AUDIO_SILENCE_DB :: f32(-100)Quieter than this is silence, for meters and faders
constant. Source: audio_meter.odin:139
