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:
@@ -1,6 +1,6 @@
|
||||
--- a/src/MacSrc/Shock.c
|
||||
+++ b/src/MacSrc/Shock.c
|
||||
@@ -162,6 +162,17 @@
|
||||
@@ -162,9 +162,26 @@
|
||||
void InitSDL() {
|
||||
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
@@ -12,9 +12,18 @@
|
||||
+ // src/audio/aaudio/SDL_aaudio.c). SDLSound.c opens two: one directly via
|
||||
+ // SDL_OpenAudioDevice() for cutscene audio, one via Mix_OpenAudio() for
|
||||
+ // SFX/MIDI. The older OpenSL ES backend has no such limitation, so force
|
||||
+ // it instead of AAudio.
|
||||
+ SDL_SetHint(SDL_HINT_AUDIODRIVER, "openslES");
|
||||
+ // it instead of AAudio. Uses SDL_HINT_OVERRIDE priority so nothing set
|
||||
+ // earlier (or by SDL/the platform) can take precedence over it.
|
||||
+ SDL_SetHintWithPriority(SDL_HINT_AUDIODRIVER, "openslES", SDL_HINT_OVERRIDE);
|
||||
+#endif
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) {
|
||||
DEBUG("%s: Init failed", __FUNCTION__);
|
||||
}
|
||||
+#ifdef __ANDROID__
|
||||
+ // Standing diagnostic: reports which audio driver SDL actually selected,
|
||||
+ // to confirm the openslES override above took effect on-device.
|
||||
+ INFO("SDL audio driver: %s", SDL_GetCurrentAudioDriver());
|
||||
+#endif
|
||||
|
||||
// TODO: figure out some universal set of settings that work...
|
||||
#ifdef __ANDROID__
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/src/MacSrc/SDLSound.c
|
||||
+++ b/src/MacSrc/SDLSound.c
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
|
||||
if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 2048) < 0) {
|
||||
- ERROR("%s: Couldn't open audio device", __FUNCTION__);
|
||||
+ ERROR("%s: Couldn't open audio device: %s", __FUNCTION__, SDL_GetError());
|
||||
}
|
||||
|
||||
Mix_AllocateChannels(SND_MAX_SAMPLES);
|
||||
@@ -0,0 +1,181 @@
|
||||
--- a/src/MacSrc/SDLSound.c
|
||||
+++ b/src/MacSrc/SDLSound.c
|
||||
@@ -19,24 +19,6 @@
|
||||
|
||||
// Startup the sound system
|
||||
|
||||
- SDL_AudioSpec spec, obtained;
|
||||
- spec.freq = 48000;
|
||||
- spec.format = AUDIO_S16SYS;
|
||||
- spec.channels = 2;
|
||||
- spec.samples = 2048;
|
||||
- spec.callback = AudioStreamCallback;
|
||||
- spec.userdata = (void *)&cutscene_audiostream;
|
||||
-
|
||||
- extern SDL_AudioDeviceID device;
|
||||
- device = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, 0);
|
||||
-
|
||||
- if (device == 0) {
|
||||
- ERROR("Could not open SDL audio: %s", SDL_GetError());
|
||||
- } else {
|
||||
- INFO("Opened Music Stream, deviceID %d, freq %d, size %d, format %d, channels %d, samples %d", device,
|
||||
- obtained.freq, obtained.size, obtained.format, obtained.channels, obtained.samples);
|
||||
- }
|
||||
-
|
||||
if (Mix_Init(MIX_INIT_MP3) < 0) {
|
||||
ERROR("%s: Init failed", __FUNCTION__);
|
||||
}
|
||||
@@ -50,10 +32,18 @@
|
||||
Mix_HookMusic(MusicCallback, (void *)&MusicDev);
|
||||
Mix_VolumeMusic(MIX_MAX_VOLUME); // use max volume for music stream
|
||||
|
||||
+ // Cutscene/audiolog playback (AudioStreamCallback, see cutsloop.c) used to
|
||||
+ // run through its own SDL_OpenAudioDevice() device, separate from this
|
||||
+ // one - SDL only ever allows one open non-capture device on Android's
|
||||
+ // audio backends, so that second open always failed with "Audio device
|
||||
+ // already open" and both SFX and MIDI music (both routed through this
|
||||
+ // Mix_OpenAudio device) silently never played. Mixed into this device's
|
||||
+ // post-mix stage instead, so there is only ever one device open.
|
||||
+ Mix_SetPostMix(AudioStreamCallback, (void *)&cutscene_audiostream);
|
||||
+
|
||||
InitReadXMI();
|
||||
|
||||
atexit(Mix_CloseAudio);
|
||||
- atexit(SDL_CloseAudio);
|
||||
|
||||
return OK;
|
||||
}
|
||||
--- a/src/MacSrc/Shock.c
|
||||
+++ b/src/MacSrc/Shock.c
|
||||
@@ -60,8 +60,6 @@
|
||||
SDL_Palette *sdlPalette;
|
||||
SDL_Renderer *renderer;
|
||||
|
||||
-SDL_AudioDeviceID device;
|
||||
-
|
||||
int num_args;
|
||||
char **arg_values;
|
||||
|
||||
--- a/src/GameSrc/cutsloop.c
|
||||
+++ b/src/GameSrc/cutsloop.c
|
||||
@@ -83,15 +83,25 @@
|
||||
extern char FrnSubtitle[256];
|
||||
extern char GerSubtitle[256];
|
||||
|
||||
-extern SDL_AudioDeviceID device;
|
||||
-
|
||||
-
|
||||
+// Runs as SDL_mixer's post-mix hook (see snd_start_digital() in
|
||||
+// SDLSound.c) rather than a dedicated SDL audio device's callback, so
|
||||
+// SFX/MIDI music sharing the same device are already mixed into `stream`
|
||||
+// by the time this runs - mix in rather than overwrite, so they aren't
|
||||
+// silenced whenever a cutscene or audiolog is playing.
|
||||
void AudioStreamCallback(void *userdata, unsigned char *stream, int len)
|
||||
{
|
||||
+ static Uint8 mixbuf[MOVIE_DEFAULT_BLOCKLEN * 4];
|
||||
SDL_AudioStream *as = *(SDL_AudioStream **)userdata;
|
||||
|
||||
- if (as != NULL && SDL_AudioStreamAvailable(as) > 0)
|
||||
- SDL_AudioStreamGet(as, stream, len);
|
||||
+ if (as == NULL || SDL_AudioStreamAvailable(as) <= 0)
|
||||
+ return;
|
||||
+
|
||||
+ if (len > (int)sizeof(mixbuf))
|
||||
+ len = (int)sizeof(mixbuf);
|
||||
+
|
||||
+ int got = SDL_AudioStreamGet(as, mixbuf, len);
|
||||
+ if (got > 0)
|
||||
+ SDL_MixAudioFormat(stream, mixbuf, AUDIO_S16SYS, got, SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,11 +163,13 @@
|
||||
|
||||
if (cutscene_audiostream != NULL)
|
||||
{
|
||||
- SDL_PauseAudioDevice(device, 1);
|
||||
- SDL_Delay(1);
|
||||
-
|
||||
+ // AudioStreamCallback (SDL_mixer's post-mix hook) runs continuously on
|
||||
+ // SDL_mixer's own audio thread and reads this pointer - lock around
|
||||
+ // freeing it so that thread can't be mid-read when it's freed.
|
||||
+ SDL_LockAudio();
|
||||
SDL_FreeAudioStream(cutscene_audiostream);
|
||||
cutscene_audiostream = NULL;
|
||||
+ SDL_UnlockAudio();
|
||||
|
||||
if (cutscene_audiobuffer)
|
||||
{
|
||||
@@ -184,8 +196,6 @@
|
||||
|
||||
if (cutscene_audiostream)
|
||||
{
|
||||
- SDL_PauseAudioDevice(device, 0);
|
||||
-
|
||||
if (cutscene_audiobuffer_size > 0)
|
||||
{
|
||||
// === adjust volume in buffer here ===
|
||||
@@ -325,10 +335,9 @@
|
||||
|
||||
if (sfx_on)
|
||||
{
|
||||
- SDL_PauseAudioDevice(device, 1);
|
||||
- SDL_Delay(1);
|
||||
-
|
||||
+ SDL_LockAudio();
|
||||
cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(amovie->a.sampleRate), AUDIO_S16SYS, 2, 48000);
|
||||
+ SDL_UnlockAudio();
|
||||
|
||||
cutscene_audiobuffer_pos = cutscene_audiobuffer;
|
||||
}
|
||||
--- a/src/GameSrc/audiolog.c
|
||||
+++ b/src/GameSrc/audiolog.c
|
||||
@@ -61,8 +61,6 @@
|
||||
extern uchar curr_alog_vol;
|
||||
extern char which_lang;
|
||||
|
||||
-extern SDL_AudioDeviceID device;
|
||||
-
|
||||
errtype audiolog_init(void) { return OK; }
|
||||
|
||||
errtype audiolog_play(int email_id) {
|
||||
@@ -114,10 +112,9 @@
|
||||
|
||||
DEBUG("%s: Playing email", __FUNCTION__);
|
||||
|
||||
- SDL_PauseAudioDevice(device, 1);
|
||||
- SDL_Delay(1);
|
||||
-
|
||||
+ SDL_LockAudio();
|
||||
cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(palog->a.sampleRate), AUDIO_S16SYS, 2, 48000);
|
||||
+ SDL_UnlockAudio();
|
||||
|
||||
audiolog_audiobuffer_pos = audiolog_audiobuffer;
|
||||
|
||||
@@ -150,11 +147,14 @@
|
||||
}
|
||||
|
||||
if (cutscene_audiostream != NULL) {
|
||||
- SDL_PauseAudioDevice(device, 1);
|
||||
- SDL_Delay(1);
|
||||
-
|
||||
+ // AudioStreamCallback (SDL_mixer's post-mix hook, see cutsloop.c)
|
||||
+ // runs continuously on SDL_mixer's own audio thread and reads this
|
||||
+ // pointer - lock around freeing it so that thread can't be
|
||||
+ // mid-read when it's freed.
|
||||
+ SDL_LockAudio();
|
||||
SDL_FreeAudioStream(cutscene_audiostream);
|
||||
cutscene_audiostream = NULL;
|
||||
+ SDL_UnlockAudio();
|
||||
|
||||
if (audiolog_audiobuffer) {
|
||||
free(audiolog_audiobuffer);
|
||||
@@ -177,8 +177,6 @@
|
||||
|
||||
errtype audiolog_loop_callback(void) {
|
||||
if (cutscene_audiostream) {
|
||||
- SDL_PauseAudioDevice(device, 0);
|
||||
-
|
||||
if (audiolog_audiobuffer_size > 0) {
|
||||
int i, vol = curr_alog_vol * 127 / 100; // convert from 0-100 to 0-127
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/src/MusicSrc/MusicDevice.c
|
||||
+++ b/src/MusicSrc/MusicDevice.c
|
||||
@@ -569,6 +569,14 @@
|
||||
// connected
|
||||
ndev->dev.isOpen = 1;
|
||||
ndev->dev.outputIndex = outputIndex;
|
||||
+#else
|
||||
+ // No native MIDI backend implemented for this platform (e.g. Android) -
|
||||
+ // report failure so InitDecXMI()'s caller falls back to another device
|
||||
+ // instead of treating an unopened device as successfully initialized.
|
||||
+ (void)outputIndex;
|
||||
+ (void)samplerate;
|
||||
+ WARN("NativeMidiInit(): native MIDI is not implemented on this platform");
|
||||
+ return -1;
|
||||
#endif
|
||||
// suppress compiler warnings
|
||||
(void)outputIndex;
|
||||
Reference in New Issue
Block a user