Android audio device selection (#6824)

Make it possible to select a specific audio device for Android
This commit is contained in:
Maido
2022-12-16 17:38:57 +02:00
committed by Sylvain
parent b461d9e183
commit abf5cc5203
9 changed files with 277 additions and 33 deletions

View File

@@ -68,6 +68,45 @@ void aaudio_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t
#define LIB_AAUDIO_SO "libaaudio.so"
static void aaudio_DetectDevices(void)
{
int *inputs;
inputs = SDL_malloc(sizeof(int) * 100);
SDL_zerop(inputs);
int inputs_length = 0;
Android_JNI_GetAudioInputDevices(inputs, &inputs_length);
for (int i = 0; i < inputs_length; ++i) {
int device_id = inputs[i];
int n = (int) (log10(device_id) + 1);
char device_name[n];
SDL_itoa(device_id, device_name, 10);
SDL_Log("Adding input device with name %s", device_name);
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *) ((size_t) device_id + 1));
}
SDL_free(inputs);
int *outputs;
outputs = SDL_malloc(sizeof(int) * 100);
SDL_zerop(outputs);
int outputs_length = 0;
Android_JNI_GetAudioOutputDevices(outputs, &outputs_length);
for (int i = 0; i < outputs_length; ++i) {
int device_id = outputs[i];
int n = (int) (log10(device_id) + 1);
char device_name[n];
SDL_itoa(device_id, device_name, 10);
SDL_Log("Adding output device with name %s", device_name);
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *) ((size_t) device_id + 1));
}
SDL_free(outputs);
}
static int aaudio_OpenDevice(_THIS, const char *devname)
{
struct SDL_PrivateAudioData *private;
@@ -99,6 +138,11 @@ static int aaudio_OpenDevice(_THIS, const char *devname)
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, this->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, this->spec.channels);
if(devname != NULL) {
int aaudio_device_id = SDL_atoi(devname);
LOGI("Opening device id %d", aaudio_device_id);
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id);
}
{
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction);
@@ -298,17 +342,19 @@ static SDL_bool aaudio_Init(SDL_AudioDriverImpl *impl)
goto failure;
}
impl->DetectDevices = aaudio_DetectDevices;
impl->Deinitialize = aaudio_Deinitialize;
impl->OpenDevice = aaudio_OpenDevice;
impl->CloseDevice = aaudio_CloseDevice;
impl->PlayDevice = aaudio_PlayDevice;
impl->GetDeviceBuf = aaudio_GetDeviceBuf;
impl->CaptureFromDevice = aaudio_CaptureFromDevice;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
/* this audio target is available. */
LOGI("SDL aaudio_Init OK");

View File

@@ -24,7 +24,7 @@
SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode))
SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder * *builder))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId))
SDL_PROC(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId))
SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder * builder, int32_t sampleRate))
SDL_PROC(void, AAudioStreamBuilder_setChannelCount, (AAudioStreamBuilder * builder, int32_t channelCount))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuilder * builder, int32_t samplesPerFrame))

View File

@@ -34,6 +34,44 @@
static SDL_AudioDevice *audioDevice = NULL;
static SDL_AudioDevice *captureDevice = NULL;
static void ANDROIDAUDIO_DetectDevices(void) {
int *inputs;
inputs = SDL_malloc(sizeof(int) * 100);
SDL_zerop(inputs);
int inputs_length = 0;
Android_JNI_GetAudioInputDevices(inputs, &inputs_length);
for (int i = 0; i < inputs_length; ++i) {
int device_id = inputs[i];
int n = (int) (log10(device_id) + 1);
char device_name[n];
SDL_itoa(device_id, device_name, 10);
SDL_Log("Adding input device with name %s", device_name);
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *) ((size_t) device_id + 1));
}
SDL_free(inputs);
int *outputs;
outputs = SDL_malloc(sizeof(int) * 100);
SDL_zerop(outputs);
int outputs_length = 0;
Android_JNI_GetAudioOutputDevices(outputs, &outputs_length);
for (int i = 0; i < outputs_length; ++i) {
int device_id = outputs[i];
int n = (int) (log10(device_id) + 1);
char device_name[n];
SDL_itoa(device_id, device_name, 10);
SDL_Log("Adding output device with name %s", device_name);
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *) ((size_t) device_id + 1));
}
SDL_free(outputs);
}
static int ANDROIDAUDIO_OpenDevice(_THIS, const char *devname)
{
SDL_AudioFormat test_format;
@@ -62,12 +100,18 @@ static int ANDROIDAUDIO_OpenDevice(_THIS, const char *devname)
}
}
int audio_device_id = 0;
if(devname != NULL) {
audio_device_id = SDL_atoi(devname);
}
if (!test_format) {
/* Didn't find a compatible format :( */
return SDL_SetError("%s: Unsupported audio format", "android");
}
if (Android_JNI_OpenAudioDevice(iscapture, &this->spec) < 0) {
if (Android_JNI_OpenAudioDevice(iscapture, audio_device_id, &this->spec) < 0) {
return -1;
}
@@ -115,17 +159,19 @@ static void ANDROIDAUDIO_CloseDevice(_THIS)
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->DetectDevices = ANDROIDAUDIO_DetectDevices;
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
return SDL_TRUE; /* this audio target is available. */
}

View File

@@ -213,8 +213,18 @@ static JNINativeMethod SDLInputConnection_tab[] = {
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id);
static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
};
/* Java class SDLControllerManager */
@@ -322,6 +332,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midGetAudioOutputDevices;
static jmethodID midGetAudioInputDevices;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer;
@@ -647,8 +659,14 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices",
"()[I");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices",
"()[I");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIII)[I");
"audioOpen", "(IIIII)[I");
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioWriteByteBuffer", "([B)V");
midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
@@ -658,7 +676,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioClose", "()V");
midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"captureOpen", "(IIII)[I");
"captureOpen", "(IIIII)[I");
midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"captureReadByteBuffer", "([BZ)I");
midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
@@ -670,9 +688,13 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioSetThreadPriority", "(ZI)V");
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
!midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
__android_log_print(ANDROID_LOG_WARN, "SDL",
"Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
}
checkJNIReady();
@@ -903,6 +925,26 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
}
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id) {
int n = (int) (log10(device_id) + 1);
char device_name[n];
SDL_itoa(device_id, device_name, 10);
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *) ((size_t) device_id + 1));
}
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *) ((size_t) device_id + 1));
}
/* Paddown */
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
JNIEnv *env, jclass jcls,
@@ -1421,7 +1463,29 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0;
static jobject captureBuffer = NULL;
int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
void Android_JNI_GetAudioOutputDevices(int *devices, int *length) {
JNIEnv *env = Android_JNI_GetEnv();
jintArray result;
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
*length = (*env)->GetArrayLength(env, result);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
}
void Android_JNI_GetAudioInputDevices(int * devices, int *length) {
JNIEnv *env = Android_JNI_GetEnv();
jintArray result;
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
*length = (*env)->GetArrayLength(env, result);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
}
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
{
int audioformat;
jobject jbufobj = NULL;
@@ -1447,10 +1511,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
if (iscapture) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
}
if (result == NULL) {
/* Error during audio initialization, error printed from Java */

View File

@@ -47,7 +47,9 @@ extern SDL_DisplayOrientation Android_JNI_GetDisplayOrientation(void);
extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi);
/* Audio support */
extern int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec);
extern void Android_JNI_GetAudioOutputDevices(int* devices, int *length);
extern void Android_JNI_GetAudioInputDevices(int* devices, int *length);
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
extern void *Android_JNI_GetAudioBuffer(void);
extern void Android_JNI_WriteAudioBuffer(void);
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);