Skip to content

Commit

Permalink
aaudio: Rearranged source code to match other backends.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jul 29, 2023
1 parent aed992c commit 8aa5f0c
Showing 1 changed file with 127 additions and 129 deletions.
256 changes: 127 additions & 129 deletions src/audio/aaudio/SDL_aaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct SDL_PrivateAudioData
#define LOGI(...)
#endif

#define LIB_AAUDIO_SO "libaaudio.so"

typedef struct AAUDIO_Data
{
void *handle;
Expand All @@ -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));
Expand All @@ -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;
}

static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
{
SDL_WaitSemaphore(device->hidden->semaphore);
}

#define LIB_AAUDIO_SO "libaaudio.so"
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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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

0 comments on commit 8aa5f0c

Please sign in to comment.