feat(bgfx): device-less audio mixer on Android (#306) - #308
Conversation
Extract the desktop miniaudio playback device out of audio.zig into a new audio_device.zig, and comptime-select it via `device_backend = if (is_android) NoopDevice else @import("audio_device.zig")`. This keeps every `miniaudio.h` / `ma_device` reference out of the Android build's semantic analysis, so audio.zig compiles for aarch64-linux-android as a pure-Zig, device-less mixer (decode/load/mix unchanged; no device pumps mixAudio yet — on-device AAudio output is a later task). Register the `audio` module on EVERY target so the backend module contract holds for the generated backend_bgfx_android build (phase 4, #303). Android omits wireMiniaudio (no miniaudio C TU, no audio frameworks); desktop wiring is unchanged. Add an Android compile-check off the audio module to the test step. Desktop device path is byte-for-byte equivalent (verified: example still opens the CoreAudio device and the callback fires).
PR SummaryLow Risk Overview Splits playback from the mixer: new On Android the backend is device-less (silent): no callback pumps Reviewed by Cursor Bugbot for commit 1e9c800. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Code Review
This pull request refactors the bgfx audio backend to support Android by separating the pure-Zig mixer/decoder from the desktop-specific miniaudio playback device. The miniaudio device code is moved to a new file audio_device.zig, while audio.zig now conditionally selects between audio_device.zig (for desktop) and a new NoopDevice stub (for Android) at compile time. Additionally, build.zig is updated to register the audio module on all targets and compile-check the audio module on Android. I have no feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Backend work since v0.39.1: - wgpu: macOS Metal surface + textured sprite rendering (#290, #291) - bgfx: macOS bring-up to on-device Android (#296 epic, #304/#305/#307/#308/#309) - #310 AndroidBackendContext adapters: sokol (#312) + bgfx (#313) register the core seam; bgfx-Android gamepad via the shared android_gamepad sub-package - bgfx desktop gamepad: GLFW (#315) + SDL HIDAPI / Switch-pad support (#318) - cached bgfx CI job (#295) Android codegen now calls core.registerAndroidBackend → requires labelle-core >= v1.17.0 + labelle-engine >= v1.50.0.
Closes #306. Prerequisite for phase 4 (#303) of the bgfx-Android bring-up (#296).
What
Expose the bgfx backend's
audiomodule on Android as a device-less mixer. Phases 1–3 skipped the audio module on Android (it@cImports miniaudio + opens anma_device), so a bgfx-Android consumer doingbackend_dep.module("audio")would panic. Phase 4's generatedbackend_bgfx_androidbuild does exactly that, so the module must exist + compile for Android.Approach: extract the device
backends/bgfx/src/audio_device.zig(new) — owns the entire miniaudio half: the@cImport("miniaudio.h"), thema_device,device_initialized,frames_mixed, thecallconv(.c)deviceDataCallback, and lifecycle. ExposesensureStarted(mix: MixFn)/stop()/framesMixed(). The mixer is passed in as a*const fn([]i16, u32) void, so the device never importsaudio.zigback.backends/bgfx/src/audio.zig—const device_backend = if (is_android) NoopDevice else @import("audio_device.zig")(comptime, sominiaudio.his never analyzed on Android).NoopDeviceis an inline stub with the sameensureStarted/stop/framesMixedsurface.ensureInit()→device_backend.ensureStarted(&mixAudio);deinit()→device_backend.stop()then free PCM. The mixer, atomic spinlock, WAV decode, and load/play/unload + their tests are untouched.backends/bgfx/build.zig— theaudiomodule is now registered on every target; Android omitswireMiniaudio(no miniaudio C TU / frameworks), desktop keeps it byte-for-byte. Added an Android-onlyaudiocompile-check (object emission, no run).Verification (independent, macOS arm64, Zig 0.16.0)
ANDROID_NDK_HOME=.../27.0.12077973 zig build test -Dtarget=aarch64-linux-android→ exit 0; covers gfx/window/input/android_app (phases 1–3) plus the new device-less audio compile-check.zig build testexit 0 (spinlock/mixer/WAV/unload tests pass with the real miniaudio device);examplezig buildlinks andbgfx-examplestill logsaudio: miniaudio playback device started+ the callback firing — desktop audio path exercised end-to-end, no regression.Scope
Device-less = silent on Android:
ensureInitis a no-op, nothing pumpsmixAudio,framesMixed()is always 0. The module exists + compiles (satisfies the phase-4 module contract); actual on-device sound (an AAudio output device — miniaudio supports it) is a later task, not this PR.