Quick answer: ma_device_init can fail when there’s no output device (CI machines, fresh VMs, unplugged DAC). Handle the failure, run audio-less, and re-init on device change.
A game crashes on startup in CI and on some user machines — ma_device_init returns an error and the code dereferences an uninitialized device.
Always Check the Result
ma_result r = ma_device_init(NULL, &config, &device);
if (r != MA_SUCCESS) {
log_warn("Audio device init failed: %d — running muted", r);
g_audioAvailable = false;
return; // don't touch `device`
}
g_audioAvailable = true;
A headless CI box or a VM with no sound card legitimately has no default device. Don’t treat that as fatal.
Run Audio-Less Gracefully
Gate every play call behind g_audioAvailable. The game runs silently — correct behavior for automated tests and headless servers.
Handle Device Changes
A device can vanish at runtime (USB headset unplugged). Register a notification callback (ma_device_notification_proc) and re-init when the default device changes — otherwise audio dies silently after a hot-unplug.
Pick a Backend Explicitly If Needed
If a specific backend (WASAPI, CoreAudio, ALSA) is unreliable in your environment, set the backend priority list in the context config rather than letting miniaudio auto-pick.
Verifying
The game launches on a headless CI machine without crashing (muted). On a desktop, audio works; unplugging a device and replugging recovers playback.
“No audio device is a valid state. Check the result, run muted, re-init on device change.”
‘Audio is optional’ should be a design assumption — it makes CI, servers, and accessibility all easier.