Skip to content

feat(bgfx): device-less audio mixer on Android (#306) - #308

Merged
apotema merged 1 commit into
mainfrom
feat/bgfx-android-audio-deviceless
Jun 13, 2026
Merged

feat(bgfx): device-less audio mixer on Android (#306)#308
apotema merged 1 commit into
mainfrom
feat/bgfx-android-audio-deviceless

Conversation

@apotema

@apotema apotema commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Closes #306. Prerequisite for phase 4 (#303) of the bgfx-Android bring-up (#296).

What

Expose the bgfx backend's audio module on Android as a device-less mixer. Phases 1–3 skipped the audio module on Android (it @cImports miniaudio + opens an ma_device), so a bgfx-Android consumer doing backend_dep.module("audio") would panic. Phase 4's generated backend_bgfx_android build 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"), the ma_device, device_initialized, frames_mixed, the callconv(.c) deviceDataCallback, and lifecycle. Exposes ensureStarted(mix: MixFn) / stop() / framesMixed(). The mixer is passed in as a *const fn([]i16, u32) void, so the device never imports audio.zig back.
  • backends/bgfx/src/audio.zigconst device_backend = if (is_android) NoopDevice else @import("audio_device.zig") (comptime, so miniaudio.h is never analyzed on Android). NoopDevice is an inline stub with the same ensureStarted/stop/framesMixed surface. 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 — the audio module is now registered on every target; Android omits wireMiniaudio (no miniaudio C TU / frameworks), desktop keeps it byte-for-byte. Added an Android-only audio compile-check (object emission, no run).

Verification (independent, macOS arm64, Zig 0.16.0)

  • Android (goal): 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.
  • Desktop unchanged: host zig build test exit 0 (spinlock/mixer/WAV/unload tests pass with the real miniaudio device); example zig build links and bgfx-example still logs audio: miniaudio playback device started + the callback firing — desktop audio path exercised end-to-end, no regression.

Scope

Device-less = silent on Android: ensureInit is a no-op, nothing pumps mixAudio, 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.

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).
@cursor

cursor Bot commented Jun 13, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Target-gated refactor with desktop behavior moved to audio_device.zig; mixer and locking logic unchanged, Android path is intentionally silent.

Overview
Registers the bgfx audio module on Android so backend_dep.module("audio") works for phase-4 Android builds. Android skips wireMiniaudio; desktop still wires miniaudio the same way.

Splits playback from the mixer: new audio_device.zig holds miniaudio (ma_device, callback, lifecycle). audio.zig picks NoopDevice on Android or audio_device.zig on desktop at comptime, so miniaudio.h is never analyzed on Android. ensureInit / deinit delegate to ensureStarted(&mixAudio) and stop(); decode, slots, spinlock, and tests stay in audio.zig.

On Android the backend is device-less (silent): no callback pumps mixAudio until AAudio lands later. build.zig adds an Android-only compile-check test for audio_mod alongside gfx/window/input.

Reviewed by Cursor Bugbot for commit 1e9c800. Bugbot is set up for automated code reviews on this repo. Configure here.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@apotema
apotema merged commit 845cda3 into main Jun 13, 2026
5 checks passed
@apotema
apotema deleted the feat/bgfx-android-audio-deviceless branch June 13, 2026 00:52
apotema added a commit that referenced this pull request Jun 13, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bgfx Android: expose a device-less audio module (phase-4 prerequisite)

1 participant