Startup and load
InitAudioDevice()
├─ ma_context_init()
├─ ma_device_init(..., OnSendAudioDataToDevice)
└─ ma_device_start()
Sound s = LoadSound("hit.wav")
├─ LoadWave() → dr_wav / stb_vorbis / dr_mp3 / …
├─ LoadSoundFromWave()
│ ma_convert_frames() → device format (float32 stereo)
│ LoadAudioBuffer() → linked list AUDIO.Buffer
└─ UnloadWave()
PlaySound(s) → PlayAudioBuffer()
Mixing callback (each audio buffer period)
OnSendAudioDataToDevice(pDevice, pFramesOut, ..., frameCount)
├─ memset output to silence
├─ ma_mutex_lock(AUDIO.System.lock)
├─ for each AudioBuffer in linked list:
│ if playing && !paused:
│ read frames (static sound or stream)
│ MixAudioFrames(out, in, volume, pitch, pan)
└─ ma_mutex_unlock()
Sound vs Music vs AudioStream
- Sound — fully decoded static buffer in RAM, low latency replay
- Music — streaming decoder (OGG, MP3, XM, MOD) refills buffer over time
- AudioStream — raw PCM you push with
UpdateAudioStream
Format conversion at load time
Sounds are converted to the device format at load (ma_convert_frames) rather than during mixing — simpler mixer, more memory for 8/16-bit sources.
Threading model
Mixing runs on miniaudio's audio thread. A mutex protects the buffer list — documented as not real-time safe but acceptable for games. Main thread calls PlaySound, StopSound, etc.
Compile without audio
-DSUPPORT_MODULE_RAUDIO=0 or CMake -DCUSTOMIZE_BUILD=ON -DSUPPORT_MODULE_RAUDIO=OFF removes raudio.c from the build entirely.