From 18fc0db9e51058060f19dc22fe801edc0427fc40 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 29 Jul 2023 19:56:51 -0400 Subject: [PATCH] aaudio: Rearranged source code to match other backends. --- src/audio/aaudio/SDL_aaudio.c | 256 +++++++++++++++++----------------- 1 file changed, 127 insertions(+), 129 deletions(-) diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c index f49599eb3..aeaf58a86 100644 --- a/src/audio/aaudio/SDL_aaudio.c +++ b/src/audio/aaudio/SDL_aaudio.c @@ -50,6 +50,8 @@ struct SDL_PrivateAudioData #define LOGI(...) #endif +#define LIB_AAUDIO_SO "libaaudio.so" + typedef struct AAUDIO_Data { void *handle; @@ -71,6 +73,7 @@ static int AAUDIO_LoadFunctions(AAUDIO_Data *data) return 0; } + static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error) { LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error)); @@ -82,10 +85,71 @@ static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_re SDL_PostSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice. } -static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames); +// due to the way the aaudio data callback works, PlayDevice is a no-op. The callback collects audio while SDL camps in WaitDevice and +// fires a semaphore that will unblock WaitDevice and start a new iteration, so when the callback runs again, WaitDevice is ready +// to hand it more data. +static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) userData; + SDL_assert(numFrames == device->sample_frames); + if (device->iscapture) { + SDL_memcpy(device->hidden->mixbuf, audioData, device->buffer_size); + } else { + SDL_memcpy(audioData, device->hidden->mixbuf, device->buffer_size); + } + SDL_PostSemaphore(device->hidden->semaphore); + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} +static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize) +{ + return device->hidden->mixbuf; +} -#define LIB_AAUDIO_SO "libaaudio.so" +static void AAUDIO_WaitDevice(SDL_AudioDevice *device) +{ + SDL_WaitSemaphore(device->hidden->semaphore); +} + +static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +{ + // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. + if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) { + SDL_AtomicSet(&device->hidden->error_callback_triggered, 0); + SDL_AudioDeviceDisconnected(device); + } +} + +// no need for a FlushCapture implementation, just don't read mixbuf until the next iteration. +static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) +{ + const int cpy = SDL_min(buflen, device->buffer_size); + SDL_memcpy(buffer, device->hidden->mixbuf, cpy); + return cpy; +} + +static void AAUDIO_CloseDevice(SDL_AudioDevice *device) +{ + struct SDL_PrivateAudioData *hidden = device->hidden; + LOGI(__func__); + + if (hidden) { + if (hidden->stream) { + ctx.AAudioStream_requestStop(hidden->stream); + // !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)? + // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again? + ctx.AAudioStream_close(hidden->stream); + } + + if (hidden->semaphore) { + SDL_DestroySemaphore(hidden->semaphore); + } + + SDL_free(hidden->mixbuf); + SDL_free(hidden); + device->hidden = NULL; + } +} static int AAUDIO_OpenDevice(SDL_AudioDevice *device) { @@ -220,133 +284,6 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device) return 0; } -static void AAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - LOGI(__func__); - - if (hidden) { - if (hidden->stream) { - ctx.AAudioStream_requestStop(hidden->stream); - // !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)? - // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again? - ctx.AAudioStream_close(hidden->stream); - } - - if (hidden->semaphore) { - SDL_DestroySemaphore(hidden->semaphore); - } - - SDL_free(hidden->mixbuf); - SDL_free(hidden); - device->hidden = NULL; - } -} - -// due to the way the aaudio data callback works, PlayDevice is a no-op. The callback collects audio while SDL camps in WaitDevice and -// fires a semaphore that will unblock WaitDevice and start a new iteration, so when the callback runs again, WaitDevice is ready -// to hand it more data. -static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userData; - SDL_assert(numFrames == device->sample_frames); - if (device->iscapture) { - SDL_memcpy(device->hidden->mixbuf, audioData, device->buffer_size); - } else { - SDL_memcpy(audioData, device->hidden->mixbuf, device->buffer_size); - } - SDL_PostSemaphore(device->hidden->semaphore); - return AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize) -{ - return device->hidden->mixbuf; -} - -static void AAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - SDL_WaitSemaphore(device->hidden->semaphore); -} - -static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. - if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) { - SDL_AtomicSet(&device->hidden->error_callback_triggered, 0); - SDL_AudioDeviceDisconnected(device); - } -} - -// no need for a FlushCapture implementation, just don't read mixbuf until the next iteration. -static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - const int cpy = SDL_min(buflen, device->buffer_size); - SDL_memcpy(buffer, device->hidden->mixbuf, cpy); - return cpy; -} - -static void AAUDIO_Deinitialize(void) -{ - Android_StopAudioHotplug(); - - LOGI(__func__); - if (ctx.handle) { - SDL_UnloadObject(ctx.handle); - } - SDL_zero(ctx); - LOGI("End AAUDIO %s", SDL_GetError()); -} - -static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - LOGI(__func__); - - /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, - * so don't use it until 8.1. - * - * See https://github.com/google/oboe/issues/40 for more information. - */ - if (SDL_GetAndroidSDKVersion() < 27) { - return SDL_FALSE; - } - - SDL_zero(ctx); - - ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); - if (ctx.handle == NULL) { - LOGI("SDL couldn't find " LIB_AAUDIO_SO); - return SDL_FALSE; - } - - if (AAUDIO_LoadFunctions(&ctx) < 0) { - SDL_UnloadObject(ctx.handle); - SDL_zero(ctx); - return SDL_FALSE; - } - - impl->ThreadInit = Android_AudioThreadInit; - impl->DetectDevices = Android_StartAudioHotplug; - impl->Deinitialize = AAUDIO_Deinitialize; - impl->OpenDevice = AAUDIO_OpenDevice; - impl->CloseDevice = AAUDIO_CloseDevice; - impl->WaitDevice = AAUDIO_WaitDevice; - impl->PlayDevice = AAUDIO_PlayDevice; - impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; - impl->WaitCaptureDevice = AAUDIO_WaitDevice; - impl->CaptureFromDevice = AAUDIO_CaptureFromDevice; - - impl->HasCaptureSupport = SDL_TRUE; - - LOGI("SDL AAUDIO_Init OK"); - return SDL_TRUE; -} - -AudioBootStrap AAUDIO_bootstrap = { - "AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE -}; - - static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata) { struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden; @@ -440,4 +377,65 @@ SDL_bool AAUDIO_DetectBrokenPlayState(void) return (ctx.handle && SDL_FindPhysicalAudioDeviceByCallback(DetectBrokenPlayStatePerDevice, NULL) != NULL) ? SDL_TRUE : SDL_FALSE; } +static void AAUDIO_Deinitialize(void) +{ + Android_StopAudioHotplug(); + + LOGI(__func__); + if (ctx.handle) { + SDL_UnloadObject(ctx.handle); + } + SDL_zero(ctx); + LOGI("End AAUDIO %s", SDL_GetError()); +} + + +static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl) +{ + LOGI(__func__); + + /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, + * so don't use it until 8.1. + * + * See https://github.com/google/oboe/issues/40 for more information. + */ + if (SDL_GetAndroidSDKVersion() < 27) { + return SDL_FALSE; + } + + SDL_zero(ctx); + + ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); + if (ctx.handle == NULL) { + LOGI("SDL couldn't find " LIB_AAUDIO_SO); + return SDL_FALSE; + } + + if (AAUDIO_LoadFunctions(&ctx) < 0) { + SDL_UnloadObject(ctx.handle); + SDL_zero(ctx); + return SDL_FALSE; + } + + impl->ThreadInit = Android_AudioThreadInit; + impl->DetectDevices = Android_StartAudioHotplug; + impl->Deinitialize = AAUDIO_Deinitialize; + impl->OpenDevice = AAUDIO_OpenDevice; + impl->CloseDevice = AAUDIO_CloseDevice; + impl->WaitDevice = AAUDIO_WaitDevice; + impl->PlayDevice = AAUDIO_PlayDevice; + impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; + impl->WaitCaptureDevice = AAUDIO_WaitDevice; + impl->CaptureFromDevice = AAUDIO_CaptureFromDevice; + + impl->HasCaptureSupport = SDL_TRUE; + + LOGI("SDL AAUDIO_Init OK"); + return SDL_TRUE; +} + +AudioBootStrap AAUDIO_bootstrap = { + "AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE +}; + #endif // SDL_AUDIO_DRIVER_AAUDIO