libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.36k stars 1.74k forks source link

SDL 2.30.2 Android 14 has no audio output? #9577

Open superfury opened 4 months ago

superfury commented 4 months ago

I use my app with SDL 2.30.2, but I seem to no longer get any audio played back?

For reference, the current audio handling is at: https://bitbucket.org/superfury/commonemuframework/src/ccd3a558e76ca70ae5b867455d7d97f463a09e79/emu/io/sound.c

It's using normal 1.2 originated callback-based audio generation (and is compatible with SDL 1.2, SDL2 and SDL3 libraries so far), with newer features (like connect/disconnect) added on SDL2 and newer.

When I enable recording of the generated audio, I still get the audio it's generating properly (so it's actually generating the sounds). But the Android device (Samsung Galaxy A52s 5G running Android 14) doesn't seem to output any sound at all?

superfury commented 4 months ago

I've added some extra logging to my app to report fails of the playback device as well (before it was just the recording device).

Both openaudio calls (playback and record) report in SDL_getError(): "aaudio_OpenDevice : AAUDIO_ERROR_INTERNAL"

icculus commented 4 months ago

It's almost certainly this change right here:

https://github.com/libsdl-org/SDL/commit/1e016fd5eaa3b78248fc9e14a5958c0a884b76b8

Are you able to rebuild SDL and just comment out that AAudioStreamBuilder_setPerformanceMode call to see if it fixes things?

superfury commented 4 months ago

It's almost certainly this change right here:

1e016fd

Are you able to rebuild SDL and just comment out that AAudioStreamBuilder_setPerformanceMode call to see if it fixes things?

I tried it again with that function call (in that openaudio function) and it still errored out. Now trying with all calls to it disabled (there's 2 of them inside SDL_aaudio.c)... ... And it's failing in exactly the same way!

So commenting out those 2 calls inside SDL_aaudio.c doesn't seem to fix the problem.

icculus commented 4 months ago

Hmm, what version were you on before 2.30.2, when it worked? The only other thing we've done recently that is a possible culprit is https://github.com/libsdl-org/SDL/commit/ec25d6b1e860e1689044c1d145cbbcbe1aa5011f, but that would have been in 2.30.0, I think.

Also, I hate asking this but...have you rebooted the Android device recently? I know, I know...

superfury commented 4 months ago

I last rebooted it yesterday or so I think. That was because of Android needing to reboot to update itself to the latest version.

Found the last version on my other dev machine (got 2, one PC for normal use and one laptop for travelling). It's compiling against 2.24.2 according to SDL_version.h. It also used SDL_net 2.0.1.

Tried older versions that my app supports (down to 2.0.14 which is my app's minimum due to background audio playback requirements), but I noticed that 2.0.14 and 2.0.16 (version before and at AAudio being implemented in SDL2) fail to run at all (using it immediately crashes the app when starting it up). They also seem to have issues building at all (with some warning in one java line needing something to specify thread affinity or something like that (I remember that newer SDL java code has some kind of API version check to specify that final 0 parameter using a dynamic API-depending input (some kind of manual define of it's value combined with SDK check is used on the latest SDL2 for that, the issue is somewhere in the USBmanager module if I remember correctly). Simply backported that to get it to compile correctly (copy-paste and adjusting the offending line to match the latest SDL2 behaviour to get it to compile).

Edit: Just confirmed SDL2 2.24.2 + SDL_net 2.0.1 to compile and properly use sound without issues (no AAudio errors at all!).

Edit: 2.26.5 doesn't compile: (running the latest Gradle version and UniPCemu commits with the official release builds now to determine which SDL2 version was the last to compile and run properly with AAudio)

D:\projects\unipcemu\android-studio\app\src\main\java\org\libsdl\app\SDLActivity.java:212: error: cannot find symbol
    protected static SDLSurface mSurface;
                     ^
  symbol:   class SDLSurface
  location: class SDLActivity
D:\projects\unipcemu\android-studio\app\src\main\java\org\libsdl\app\SDLActivity.java:315: error: cannot find symbol
    protected SDLSurface createSDLSurface(Context context) {
              ^
  symbol:   class SDLSurface
  location: class SDLActivity
D:\projects\unipcemu\android-studio\app\src\main\java\org\libsdl\app\SDLActivity.java:316: error: cannot find symbol
        return new SDLSurface(context);
                   ^
  symbol:   class SDLSurface
  location: class SDLActivity
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
3 errors

2.26.4 too,,,

So, so far: 2.30.2: compiled and can't open audio devices at all (AAudio fails). 2.28.0: compiled. crashes just like 2.30.2. Checked this version again with reinstalled Android Studio to be sure this version is the cause. It still crashes on both audio devices. So this version of SDL2 introduced the issue that's causing it. 2.26.5: can't build (so far). Edit: Tried again after full reinstall. Runs without issues. 2.26.4: can't build (so far). Edit: Might run again after the full reinstall, but not needed (since 2.26.5 runs now anyways). 2.26.1: Runs properly by default (see below for 2.26.0's issue). 2.26.0: (see below for the exact issue). With fix it's built properly. compiles and runs properly with sound (no errors logged). 2.24.2: compiles and runs properly.

Edit: OK. With 2.26.0 (back on my normal desktop PC) I somehow managed to destroy Android Studio completely. Reinstalling broke things even more, it won't even start properly rn.

Edit: OK. Managed to somehow destroy Android Studio on my desktop PC completely. Reinstalled and can't run properly anymore rn. Edit: OK. Managed to fix it again afaik. Now trying to recompile (made an error copying the SDL2 files over last time (it's just the include/src directories and Android.mk file)). Then looked at all 2.26.x Android.mk files once again. 2.26.0 has an error preventing proper SDL headers to be used:

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)/include

Since LOCAL_C_INCLUDES already points to:

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include

That causes it to point to "$(LOCAL_PATH)/include/include", which is incorrect. With that fixed, it compiles, so I can check it now. 2.26.1 and up seem to fix this issue again.

superfury commented 4 months ago

Just checked the versions with a fresh install of Android Studio.

So it really looks like 2.26.5 and below don't have any audio issues (audio playback confirmed to be working correctly without any errors reported by SDL2. That's the same on recording as well).

But 2.28.0 (not the RC version but the normal one) has both audio devices crashing somehow. So obviously the Android audio issue is introduced with this SDL2 version release.

The 2.26.x (other than 2.26.0) issues seems to have been caused by a corrupted Android SDK (or Android Studio itself) installation (reinstalling the latest from scratch seems to have fixed those issues). All I've tested afterwards on those versions had no audio issues at all. Both recording and playback ran fine on my Android device.

2.28.0 however still doesn't run properly and keeps reporting the internal AAUDIO error.

Rebooted my Android device to be sure of that. Still no audio on 2.28.0. Went back to 2.26.5 and it was able to open the audio channel again.

So the issue is clearly on 2.28.0 and newer (introduced with said version)!

icculus commented 4 months ago

Cool, this is good information, thank you!

superfury commented 4 months ago

Looking further into the changes made (affecting this case, as I'm using the default (NULL) audio device or device number by the Android-specific SDL2 event (for dynamically connecting to either the default or connected audio devices).

I see both types trigger twice (so 2 times for output device followed by 2 times for input device (of those two one is the normal device and the latter is the default fallback (NULL device) my app is trying to use afaik)) after each other. So that would indicate that probably it's somehow mishandling device ID NULL(the 'Default device')?

Looking into what the code is doing it's passing it to Android_JNI_OpenAudioDevice before the change, but not from 2.28.0 and after that.

Looking at the original code that's calling the JNI function, I see: SDL_AndroidAudio.c:

sdl_aaudio.c:

Then, looking at the SDLAudioManager.java class:

That's pretty much all I can see that has changed between those two versions (other than a lot of lines being split with newlines).

Also, does Android really support only 1 audio playback and record device that's always 'available'? Users can revoke the privileges for recording from Android though? And Android 14 (which I'm using on my phone) seems to have multiple audio devices for for example phone itself and bluetooth headphones that are switchable using software exposing those settings (Sound Assistant exposes those settings. I can change apps, mixing them between internal speaker and bluetooth headphones for example. In fact, I remember messing with those BT/phone speaker toggles (and some other toggles on the mini control panel that's made visible by the volume buttons) and bluetooth and suddenly the audio started working when starting the app again on 2.30.2, but haven't been able to reproduce it since. It also might have affected the default audio device when starting up the SDL2 app.)?

So perhaps multiple audio devices are required on (latest, which is Android 14 in my case) Android after all and it was a mistake to remove said functionality back then in 2.28.0?

Edit: Slightly related, looking at SDL 2.30.2 I see aaudio_OpenDevice has reimplemented some of this multiple device logic? So the multiple audio device logic looks like it's related to this problem?

superfury commented 4 months ago

Wanted to try the SDL3 (using the SDL2 Android.mk makefiles as a base for SDL3 builds, as my project uses) to verify what the new SDL3 behaviour does on Android.

So ported my framework Makefiles to SDL3 to start, but then I got this (compiling SDL3's Android java files):

X:\projects\UniPCemu\android-studio\app\src\main\java\org\libsdl\app\SDLActivity.java:1431: error: cannot find symbol
                    SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
                    ^
  symbol:   variable SDLInputConnection
  location: class SDLActivity

Edit: Managed to get it to compile perhaps (still need to fixup my own SDL3 code for Android-specific stuff) by clearing the caches.

Edit: OK. It compiled.

I did have to add an extra Android SDL3_net addon to get SDL3_net's current commit to compile (due to missing ifaddrs support by default), porting the Makefile.mk from SDL2: SDL3_net's Makefile.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := SDL3_net

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS :=

LOCAL_SRC_FILES := src/SDL_net.c

LOCAL_SHARED_LIBRARIES := SDL3 android-ifaddrs

LOCAL_EXPORT_C_INCLUDES += $(LOCAL_C_INCLUDES)

include $(BUILD_SHARED_LIBRARY)

That, combined with a pre-existing port of ifaddrs to Android (https://github.com/superfury/android-ifaddrs) so I was able to compile SDL3_net properly (instead of it erroring out on the ifaddrs functions).

It compiles now at least, no errors (after I've adjusted some Android-specific SDL3 functionality in my own program).

Edit: It runs, but again no audio. Nothing is logged though (might be my code where logging fails though). Edit: OK. My code uses the same logic as connect/disconnect events when starting the app. The only difference is is that it starts with trying the documented default audio playback and record devices. And audio connect/disconnect events make it swap to the connected device(connect event) or default device if possible (disconnect event, after disconnecting the audio and cleaning up).

The basic audio handling still has a sample rate from the last playback or record device used, so it keeps generating samples (although if nothing is connected and available(so the default device not available) nothing is sent to SDL and it's discarded after parsing the recording of played back audio (and recording stays at the mid-point (0V) in it's samples)). Enabling the audio recording functionality shows that the app is properly generating samples still (I can hear the audio when I stop recording (to finish saving the stereo .wav file) and play the recorded audio file in another app).

My app doesn't log any errors though, even though I added logging if opening the audio devices. Edit: Now reworking the audio system a bit. Hopefully that'll fix SDL3 issues. Edit: OK. After some fixing the new AudioCVT-compatible conversion to behave properly (the SDL3 audio streams) I get audio running properly on Windows builds at least. Now still need to check SDL3 android builds (as well as verify SDL2 2.26.5 (and perhaps above too) still works). Edit: OK. Both 2.26.5 (still works) and SDL3 also doesn't report any errors. So the issue is really only in the 2.28.0 through 2.32.2 (lastest) builds.

superfury commented 4 months ago

Managed to get SDL3 on Android running as well (it was an error on my app's side, using incorrect Audio Stream specs).

So SDL3 (afaik it's the release 3.1.1 build and not the latest github repo) and SDL 2.26.5 and below run without issues on Android. I see nothing being logged for failed audio devices being opened.

So it's really the SDL2 builds for 2.28.0 and up only that are erroring out.

The adjustment in my app now is that after opening the SDL3 audio channel, a copy of the original specs input (which is overwritten with the audio channel specs) is combined with the frequency now present in the structure from the openaudio call (simulating the adjustment of frequency only of SDL2 and below), then when creating the audiostream it's used to convert to(output/render)/from(capture) the openaudio's returned format/channels. The framework itself is set to that combined frequency with the openaudio's input (default) channels and sample type(depth, signedness etc.).