mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-07 03:48:14 +00:00
Android AAUDIO: handle multiple devices
This commit is contained in:
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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_ */
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user