Android AAUDIO: handle multiple devices

This commit is contained in:
Sylvain
2023-04-06 23:17:29 +02:00
committed by Sylvain Becker
parent f38cb0d06f
commit 117169d610
3 changed files with 135 additions and 105 deletions

View File

@@ -201,6 +201,16 @@ static SDL_AudioDevice *get_audio_device(SDL_AudioDeviceID id)
return open_devices[id]; return open_devices[id];
} }
int get_max_num_audio_dev(void)
{
return SDL_arraysize(open_devices);
}
SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id)
{
return open_devices[id];
}
/* stubs for audio drivers that don't need a specific entry point... */ /* stubs for audio drivers that don't need a specific entry point... */
static void SDL_AudioDetectDevices_Default(void) static void SDL_AudioDetectDevices_Default(void)
{ {

View File

@@ -200,4 +200,7 @@ extern AudioBootStrap N3DSAUDIO_bootstrap;
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
extern AudioBootStrap QSAAUDIO_bootstrap; extern AudioBootStrap QSAAUDIO_bootstrap;
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
extern int get_max_num_audio_dev(void);
#endif /* SDL_sysaudio_h_ */ #endif /* SDL_sysaudio_h_ */

View File

@@ -43,9 +43,6 @@ typedef struct AAUDIO_Data
} AAUDIO_Data; } AAUDIO_Data;
static AAUDIO_Data ctx; static AAUDIO_Data ctx;
static SDL_AudioDevice *audioDevice = NULL;
static SDL_AudioDevice *captureDevice = NULL;
static int aaudio_LoadFunctions(AAUDIO_Data *data) static int aaudio_LoadFunctions(AAUDIO_Data *data)
{ {
#define SDL_PROC(ret, func, params) \ #define SDL_PROC(ret, func, params) \
@@ -75,18 +72,6 @@ static int aaudio_OpenDevice(_THIS, const char *devname)
aaudio_result_t res; aaudio_result_t res;
LOGI(__func__); LOGI(__func__);
if (iscapture) {
if (captureDevice) {
return SDL_SetError("An audio capture device is already opened");
}
}
if (!iscapture) {
if (audioDevice) {
return SDL_SetError("An audio playback device is already opened");
}
}
if (iscapture) { if (iscapture) {
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) { if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
LOGI("This app doesn't have RECORD_AUDIO permission"); LOGI("This app doesn't have RECORD_AUDIO permission");
@@ -94,12 +79,6 @@ static int aaudio_OpenDevice(_THIS, const char *devname)
} }
} }
if (iscapture) {
captureDevice = this;
} else {
audioDevice = this;
}
this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*this->hidden)); this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*this->hidden));
if (this->hidden == NULL) { if (this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
@@ -200,14 +179,6 @@ static void aaudio_CloseDevice(_THIS)
} }
} }
if (this->iscapture) {
SDL_assert(captureDevice == this);
captureDevice = NULL;
} else {
SDL_assert(audioDevice == this);
audioDevice = NULL;
}
SDL_free(this->hidden->mixbuf); SDL_free(this->hidden->mixbuf);
SDL_free(this->hidden); SDL_free(this->hidden);
} }
@@ -349,89 +320,120 @@ AudioBootStrap aaudio_bootstrap = {
/* Pause (block) all non already paused audio devices by taking their mixer lock */ /* Pause (block) all non already paused audio devices by taking their mixer lock */
void aaudio_PauseDevices(void) void aaudio_PauseDevices(void)
{ {
/* TODO: Handle multiple devices? */ int i;
struct SDL_PrivateAudioData *private; for (i = 0; i < get_max_num_audio_dev(); i++) {
if (audioDevice != NULL && audioDevice->hidden != NULL) { SDL_AudioDevice *this = get_audio_dev(i);
private = (struct SDL_PrivateAudioData *)audioDevice->hidden; SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (private->stream) { if (this == NULL) {
aaudio_result_t res = ctx.AAudioStream_requestPause(private->stream); continue;
if (res != AAUDIO_OK) { }
LOGI("SDL Failed AAudioStream_requestPause %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); if (this->iscapture) {
captureDevice = this;
} else {
audioDevice = this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestPause(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestPause %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
if (SDL_AtomicGet(&audioDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(audioDevice->mixer_lock);
SDL_AtomicSet(&audioDevice->paused, 1);
private->resume = SDL_TRUE;
} }
} }
if (SDL_AtomicGet(&audioDevice->paused)) { if (captureDevice != NULL && captureDevice->hidden != NULL) {
/* The device is already paused, leave it alone */ struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(audioDevice->mixer_lock);
SDL_AtomicSet(&audioDevice->paused, 1);
private->resume = SDL_TRUE;
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) { if (private->stream) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden; /* Pause() isn't implemented for 'capture', use Stop() */
aaudio_result_t res = ctx.AAudioStream_requestStop(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStop %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
if (private->stream) { if (SDL_AtomicGet(&captureDevice->paused)) {
/* Pause() isn't implemented for 'capture', use Stop() */ /* The device is already paused, leave it alone */
aaudio_result_t res = ctx.AAudioStream_requestStop(private->stream); private->resume = SDL_FALSE;
if (res != AAUDIO_OK) { } else {
LOGI("SDL Failed AAudioStream_requestStop %d", res); SDL_LockMutex(captureDevice->mixer_lock);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); SDL_AtomicSet(&captureDevice->paused, 1);
private->resume = SDL_TRUE;
} }
} }
if (SDL_AtomicGet(&captureDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(captureDevice->mixer_lock);
SDL_AtomicSet(&captureDevice->paused, 1);
private->resume = SDL_TRUE;
}
} }
} }
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */ /* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
void aaudio_ResumeDevices(void) void aaudio_ResumeDevices(void)
{ {
/* TODO: Handle multiple devices? */ int i;
struct SDL_PrivateAudioData *private; for (i = 0; i < get_max_num_audio_dev(); i++) {
if (audioDevice != NULL && audioDevice->hidden != NULL) { SDL_AudioDevice *this = get_audio_dev(i);
private = (struct SDL_PrivateAudioData *)audioDevice->hidden; SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (private->resume) { if (this == NULL) {
SDL_AtomicSet(&audioDevice->paused, 0); continue;
private->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->mixer_lock);
} }
if (private->stream) { if (this->iscapture) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream); captureDevice = this;
if (res != AAUDIO_OK) { } else {
LOGI("SDL Failed AAudioStream_requestStart %d", res); audioDevice = this;
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); }
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&audioDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->mixer_lock);
}
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
} }
} }
}
if (captureDevice != NULL && captureDevice->hidden != NULL) { if (captureDevice != NULL && captureDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden; struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) { if (private->resume) {
SDL_AtomicSet(&captureDevice->paused, 0); SDL_AtomicSet(&captureDevice->paused, 0);
private->resume = SDL_FALSE; private->resume = SDL_FALSE;
SDL_UnlockMutex(captureDevice->mixer_lock); SDL_UnlockMutex(captureDevice->mixer_lock);
} }
if (private->stream) { if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream); aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) { if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res); LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
} }
} }
} }
@@ -444,24 +446,39 @@ void aaudio_ResumeDevices(void)
*/ */
SDL_bool aaudio_DetectBrokenPlayState(void) SDL_bool aaudio_DetectBrokenPlayState(void)
{ {
struct SDL_PrivateAudioData *private; int i;
int64_t framePosition, timeNanoseconds; for (i = 0; i < get_max_num_audio_dev(); i++) {
aaudio_result_t res; SDL_AudioDevice *this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (audioDevice == NULL || !audioDevice->hidden) { if (this == NULL) {
return SDL_FALSE; continue;
}
private = audioDevice->hidden;
res = ctx.AAudioStream_getTimestamp(private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
if (res == AAUDIO_ERROR_INVALID_STATE) {
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(private->stream);
/* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
LOGI("SDL aaudio_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
return SDL_TRUE;
} }
if (this->iscapture) {
captureDevice = this;
} else {
audioDevice = this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
int64_t framePosition, timeNanoseconds;
aaudio_result_t res = ctx.AAudioStream_getTimestamp(private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
if (res == AAUDIO_ERROR_INVALID_STATE) {
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(private->stream);
/* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
LOGI("SDL aaudio_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
return SDL_TRUE;
}
}
}
(void) captureDevice;
} }
return SDL_FALSE; return SDL_FALSE;