Skip to content

DirectAudio v1.2.1 - recover from a dead AAudio data callback

Choose a tag to compare

@The412Banner The412Banner released this 14 Aug 00:28
· 29 commits to main since this release

Fixes permanent audio loss — the game keeps running, but sound never returns until you relaunch it.

Reported by JT against GameNative, reproduced on Bannerlator with v1.2, so it is driver-side, not app-specific.

The trigger is rapid background/foreground of the app — not pause/resume. The in-drawer Pause/Resume does not reproduce it.

What was happening

Backgrounding freezes the guest, so the in-process AAudio stream starves. AudioTrack disables itself after the underruns and auto-restarts, but the data callback never resumes — and this raises no error, so the driver's only stream-rebuild path never ran. It kept reporting playing=1 while nothing reached the speaker.

Fixed

  • Stall watchdog — if the AAudio callback goes silent for 1 s with audio playing, the stream is rebuilt. A disabled AAudio stream can't be restarted, so recreate is the only recovery.
  • Liveness countercb_count was only incremented inside a TRACE_ON() test, so with tracing off a dead callback looked identical to a live one.
  • Adaptive buffer — no longer treats a frozen guest as timing pressure. It was inflating the buffer permanently (192 → 5568 frames, 4 ms → 116 ms).
  • Rebuilds are now logged in the release buildDirectAudio: reopen: <reason>. A few lines per session, no diagnostics build needed.

Verified on device (DiRT 3)

before after
audio after bg/fg abuse lost permanently no dropout at all
buffer 192 → 5568 fr, never shrinks back to 192 fr each rebuild
recovery relaunch required 5 rebuilds, none audible
5 min normal play 0 false rebuilds, buffer flat at 8 ms

Recovery is inaudible because the new stream is started before the old one is destroyed — a ~77 ms swap the guest's ring buffer covers.

Downloads

  • directaudio-v1.2.1-sdk28/35.zip — release build. Use this one.
  • directaudio-v1.2.1-diagnostics-sdk28/35.zip — same fix plus 23 logcat probes, for debugging a specific title.

Each contains winedirectaudio.drv and winedirectaudio.so for lib/wine/aarch64-windows/ and lib/wine/aarch64-unix/ of a Proton 11.0 layer.

Includes everything from v1.2 (the mmdevapi MIDI thread that spun a CPU core all session). Wine 11 only — the vtable differs on Wine 10, so one build cannot span Proton 10 and 11.

Tested on one device (Adreno 750, Android 14). Mali untested.


Defaults in this release

With no host configuration, v1.2.1 runs:

v1.2.1 default total latency
buffer 62.5 ms (3000 frames) 83 ms
growth ceiling 250 ms (device capacity)
performance mode LOW_LATENCY
adaptive growth on
device period 10 ms (min 5 ms)
sharing mode SHARED
callback watchdog on, 1 s

Latency figures are totals — the driver's buffer plus Android's fixed 21 ms
mixer/HAL cost. A 4 ms buffer is 25 ms total, and that 25 ms is the floor no
application on Android can go below. Hosts that select a preset (Bannerlator
does) override the buffer default; hosts that bundle the driver and set nothing
get the values above.

Environment variables recognised by this release:

variable meaning
BANNER_AUDIO_DIRECT_PERF 0 NONE · 1 LOW_LATENCY (default) · 2 POWER_SAVING
BANNER_AUDIO_DIRECT_ADAPTIVE 1 grow on underrun (default) · 0 fixed buffer
BANNER_AUDIO_DIRECT_BF initial buffer in frames (0 = default)
BANNER_AUDIO_DIRECT_MBF cap for adaptive growth (0 = device capacity)

Buffer values here are in frames: 48 frames = 1 ms. Millisecond knobs
(_MS / _MAXMS), buffer decay, the tuning and logging knobs, and a lower
12 ms / 33 ms default are not in this release — they are in development.