Fix SFX and MIDI music not playing on Android

snd_start_digital() opened two separate SDL audio devices - a raw one
for cutscene/audiolog audio and a second via Mix_OpenAudio() for
SFX/MIDI - but Android's audio backends only ever allow one open
non-capture device, so the second open always failed silently.

- Consolidate onto a single Mix_OpenAudio() device; cutscene/audiolog
  PCM is now mixed into its output via Mix_SetPostMix() instead of a
  dedicated device (16-android-audio-single-device.patch)
- Add SDL_GetError() to the failure log so a future open failure is
  actually diagnosable (15-android-audio-error-detail.patch)
- Make NativeMidiInit() report failure on platforms with no real
  implementation (Android included), instead of silently claiming
  success without opening anything (17-android-native-midi-unsupported.patch)
- Strengthen the OpenSL ES driver hint to SDL_HINT_OVERRIDE and log the
  active audio driver as a standing diagnostic (05-android-audio-driver.patch)
This commit is contained in:
ml
2026-08-30 07:09:16 +02:00
parent d6e438a541
commit 504ed3e1cb
5 changed files with 307 additions and 11 deletions
+86 -8
View File
@@ -383,10 +383,14 @@ each one does:
emulation) for real GLES-native equivalents: a custom `pointSize`
uniform, a shader-side alpha discard, and native point-sprite
rasterization.
- **`05-android-audio-driver.patch`** - forces SDL2's older OpenSL ES
audio backend instead of AAudio, because AAudio only allows one open
playback device and the engine opens two (cutscene audio plus
`Mix_OpenAudio` for SFX/MIDI).
- **`05-android-audio-driver.patch`** - forces SDL2's OpenSL ES audio
backend instead of AAudio via
`SDL_SetHintWithPriority(..., SDL_HINT_OVERRIDE)` (so nothing else can
take precedence and silently leave AAudio selected), and logs the
actual `SDL_GetCurrentAudioDriver()` result right after `SDL_Init()`
as a standing on-device diagnostic. Does not by itself fix SFX/MIDI
playback - see `16-android-audio-single-device.patch` and README 5.5.3
for the actual cause and fix.
- **`06-android-resize-event.patch`** - makes the engine react to
`SDL_WINDOWEVENT_RESIZED`, not just `SIZE_CHANGED`, since Android's SDL
backend only ever sends the former for surface-driven resizes.
@@ -434,6 +438,32 @@ each one does:
inherited non-default unpack state left behind elsewhere in the frame
and misread every row. Fixed by resetting both to the packed defaults
immediately before this upload.
- **`15-android-audio-error-detail.patch`** - `snd_start_digital()`'s
`Mix_OpenAudio()` failure only logged a fixed string, not the actual
reason. Adds `SDL_GetError()` to the log line so the next on-device
failure reports the real underlying error.
- **`16-android-audio-single-device.patch`** - fixes SFX/MIDI music not
playing (see README 5.5.3). `snd_start_digital()` opened two SDL audio
devices: a raw `SDL_OpenAudioDevice()` for cutscene/audiolog audio and
a second one via `Mix_OpenAudio()` for SFX/MIDI - SDL's Android audio
backends only ever allow one open non-capture device, so the second
open always failed. Removes the raw device entirely and feeds cutscene/
audiolog PCM into the `Mix_OpenAudio()` device's output instead, via
`Mix_SetPostMix()` (mixing in rather than overwriting, so it doesn't
silence any SFX/MIDI music playing at the same time). `cutsloop.c`'s
and `audiolog.c`'s `SDL_PauseAudioDevice()` calls (no longer meaningful
without a dedicated device) are removed; the cutscene-audio-stream
pointer swaps they guarded are now protected by `SDL_LockAudio()`/
`SDL_UnlockAudio()` instead, since SDL_mixer's audio thread now reads
that pointer continuously via the post-mix hook.
- **`17-android-native-midi-unsupported.patch`** - `NativeMidiInit()` in
`MusicDevice.c` has real implementations only under `#ifdef WIN32` and
`#elif defined(USE_ALSA)`; on every other platform (Android included)
it fell through to `return 0` (success) without ever opening a device,
so a `midi-backend` preference of "Native" (see README 5.5.4) played
silently forever instead of falling back. Adds an `#else` branch that
returns failure and logs a `WARN`, so `InitDecXMI()`'s existing
fallback-to-dummy-driver logic actually triggers.
`android/gl4es-patches/`:
@@ -517,10 +547,58 @@ left behind elsewhere in the frame and misreads every row - a diagonal
shear/banding pattern. Menus never trigger it because nothing in the
menu-only code paths sets a custom unpack stride first.
**Fix** (`14-android-quad-unpack-state.patch`): explicitly reset both
`GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` to their tightly-packed
values immediately before this `glTexImage2D` call, removing the
dependency on whatever GL state preceded it.
#### 5.5.3. In-game SFX and MIDI music didn't play
Intro cutscene audio played correctly, but in-game SFX and MIDI/background
music never played, on-device.
**Root cause:** `snd_start_digital()` in `engine/src/MacSrc/SDLSound.c`
opened two separate SDL audio devices - a raw `SDL_OpenAudioDevice()`
call (used for cutscene/movie audio, always succeeded) and a second
device via `Mix_OpenAudio()` (used for SFX and, via `Mix_HookMusic`,
MIDI music - always failed, `SDL_GetError()` reporting "Audio device
already open"). SDL's Android audio backends (both AAudio and OpenSL ES)
only ever allow one open non-capture device - forcing OpenSL ES instead
of AAudio (`05-android-audio-driver.patch`) doesn't change this, since
the restriction isn't backend-specific.
**Fix (`16-android-audio-single-device.patch`):** removed the raw
`SDL_OpenAudioDevice()` call entirely. Cutscene/audiolog PCM (previously
delivered via that device's own callback) is now mixed into the single
`Mix_OpenAudio()` device's output via `Mix_SetPostMix()`, additively
(`SDL_MixAudioFormat`) rather than overwritten, so SFX/MIDI music
already mixed into the buffer isn't silenced whenever a cutscene or
audiolog plays. The stream-pointer swaps in `cutsloop.c`/`audiolog.c`
that used to be guarded by pausing the now-removed device are guarded by
`SDL_LockAudio()`/`SDL_UnlockAudio()` instead, since the post-mix hook
runs continuously on SDL_mixer's own audio thread.
#### 5.5.4. MIDI music silent even after audio otherwise worked
After fixing 5.5.3, SFX and cutscene audio played correctly, but MIDI
background music was still silent, with logcat showing `Creating native
MIDI device` instead of `Creating FluidSynth MIDI device`.
**Root cause:** the engine's `midi-backend` preference (`0` = ADLMIDI,
`1` = Native, `2` = FluidSynth) is persisted in a `prefs.txt` file in the
app's private storage, which Android retains across every APK
reinstall/upgrade (only a full uninstall clears it). A stale value of
`1` (Native) from earlier testing was carried forward through every
rebuild this whole session. `SetDefaultPrefs()` (`Prefs.c`) does default
to `2` (FluidSynth) whenever `USE_FLUIDSYNTH` is defined, which it
always has been for this build - the default was never wrong, only the
persisted override. `NativeMidiInit()` has no real implementation on
Android (see `17-android-native-midi-unsupported.patch`), so it silently
reported success without opening anything.
**Fix:** a full uninstall (not just installing a new build over the old
one) clears the stale preference, after which MIDI plays correctly.
`17-android-native-midi-unsupported.patch` additionally makes
`NativeMidiInit()` report failure on unsupported platforms, so a stale
"Native" preference falls back to `InitDecXMI()`'s existing dummy-driver
path (still silent, but with a clear `WARN` in logcat) instead of
silently reporting success while never opening anything - a much faster
diagnosis if this ever recurs.
## 6. License