From 741499dea777b98f6dee4479596f94ba02adbfdc Mon Sep 17 00:00:00 2001
From: Maido <[EMAIL REDACTED]>
Date: Fri, 16 Dec 2022 17:38:57 +0200
Subject: [PATCH] Android audio device selection (#6824)
Make it possible to select a specific audio device for Android
---
.../app/src/main/java/org/libsdl/app/SDL.java | 1 +
.../main/java/org/libsdl/app/SDLActivity.java | 4 +-
.../java/org/libsdl/app/SDLAudioManager.java | 113 +++++++++++++++---
include/SDL_surface.h | 2 +-
src/audio/aaudio/SDL_aaudio.c | 50 +++++++-
src/audio/aaudio/SDL_aaudiofuncs.h | 2 +-
src/audio/android/SDL_androidaudio.c | 52 +++++++-
src/core/android/SDL_android.c | 82 +++++++++++--
src/core/android/SDL_android.h | 4 +-
9 files changed, 277 insertions(+), 33 deletions(-)
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDL.java b/android-project/app/src/main/java/org/libsdl/app/SDL.java
index dafc0cb87d58..44c21c1c75c9 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDL.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDL.java
@@ -29,6 +29,7 @@ public static void initialize() {
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
+ SDLAudioManager.setContext(context);
mContext = context;
}
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index e2748f26f3e7..535e16395f73 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -393,7 +393,7 @@ public void onClick(DialogInterface dialog,int id) {
mHIDDeviceManager = HIDDeviceManager.acquire(this);
// Set up the surface
- mSurface = createSDLSurface(getApplication());
+ mSurface = createSDLSurface(this);
mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
@@ -588,6 +588,8 @@ protected void onDestroy() {
mHIDDeviceManager = null;
}
+ SDLAudioManager.release(this);
+
if (SDLActivity.mBrokenLibraries) {
super.onDestroy();
return;
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
index 2bfc71860900..94aa49a53d3a 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
@@ -1,5 +1,8 @@
package org.libsdl.app;
+import android.content.Context;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
@@ -8,34 +11,59 @@
import android.os.Build;
import android.util.Log;
-public class SDLAudioManager
-{
+import java.util.Arrays;
+
+public class SDLAudioManager {
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
+ protected static Context mContext;
+
+ private static final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallback() {
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
+ }
+ };
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
}
+ public static void setContext(Context context) {
+ mContext = context;
+ if (context != null) {
+ registerAudioDeviceCallback();
+ }
+ }
+
+ public static void release(Context context) {
+ unregisterAudioDeviceCallback(context);
+ }
+
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
- case AudioFormat.ENCODING_PCM_8BIT:
- return "8-bit";
- case AudioFormat.ENCODING_PCM_16BIT:
- return "16-bit";
- case AudioFormat.ENCODING_PCM_FLOAT:
- return "float";
- default:
- return Integer.toString(audioFormat);
+ case AudioFormat.ENCODING_PCM_8BIT:
+ return "8-bit";
+ case AudioFormat.ENCODING_PCM_16BIT:
+ return "16-bit";
+ case AudioFormat.ENCODING_PCM_FLOAT:
+ return "float";
+ default:
+ return Integer.toString(audioFormat);
}
}
- protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
int channelConfig;
int sampleSize;
int frameSize;
@@ -201,6 +229,10 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat,
return null;
}
+ if (deviceId != 0) {
+ mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
+ }
+
mAudioRecord.startRecording();
}
@@ -224,6 +256,10 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat,
return null;
}
+ if (deviceId != 0) {
+ mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
+ }
+
mAudioTrack.play();
}
@@ -238,11 +274,53 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat,
return results;
}
+ private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
+ .filter(deviceInfo -> deviceInfo.getId() == deviceId)
+ .findFirst()
+ .orElse(null);
+ }
+
+ private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
+ .filter(deviceInfo -> deviceInfo.getId() == deviceId)
+ .findFirst()
+ .orElse(null);
+ }
+
+ private static void registerAudioDeviceCallback() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
+ }
+
+ private static void unregisterAudioDeviceCallback(Context context) {
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
+ }
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public static int[] getAudioOutputDevices() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
+ }
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public static int[] getAudioInputDevices() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
+ }
+
/**
* This method is called by SDL using JNI.
*/
- public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
- return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
+ public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
+ return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/**
@@ -326,8 +404,8 @@ public static void audioWriteByteBuffer(byte[] buffer) {
/**
* This method is called by SDL using JNI.
*/
- public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
- return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
+ public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
+ return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/** This method is called by SDL using JNI. */
@@ -391,4 +469,9 @@ public static void audioSetThreadPriority(boolean iscapture, int device_id) {
}
public static native int nativeSetupJNI();
+
+ public static native void removeAudioDevice(boolean isCapture, int deviceId);
+
+ public static native void addAudioDevice(boolean isCapture, int deviceId);
+
}
diff --git a/include/SDL_surface.h b/include/SDL_surface.h
index 561b2be188cf..838de654eed7 100644
--- a/include/SDL_surface.h
+++ b/include/SDL_surface.h
@@ -683,7 +683,7 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface
* it might be easier to call but it doesn't have access to palette
* information for the destination surface, in case that would be important.
*
- * \param surface the existing SDL_Surface structure to convert
+ * \param src the existing SDL_Surface structure to convert
* \param pixel_format the SDL_PixelFormatEnum that the new surface is
* optimized for
* \param flags the flags are unused and should be set to 0; this is a
diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c
index 92d22338d5a7..7b7e662f2c3a 100644
--- a/src/audio/aaudio/SDL_aaudio.c
+++ b/src/audio/aaudio/SDL_aaudio.c
@@ -70,6 +70,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;
@@ -101,6 +140,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);
@@ -300,17 +344,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");
diff --git a/src/audio/aaudio/SDL_aaudiofuncs.h b/src/audio/aaudio/SDL_aaudiofuncs.h
index 57f84d45af55..79b24d2140fc 100644
--- a/src/audio/aaudio/SDL_aaudiofuncs.h
+++ b/src/audio/aaudio/SDL_aaudiofuncs.h
@@ -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))
diff --git a/src/audio/android/SDL_androidaudio.c b/src/audio/android/SDL_androidaudio.c
index 60779b049489..3944e925eb7a 100644
--- a/src/audio/android/SDL_androidaudio.c
+++ b/src/audio/android/SDL_androidaudio.c
@@ -35,6 +35,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;
@@ -63,12 +101,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;
}
@@ -116,17 +160,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. */
}
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 7df8a6946c55..ac8f9a34e92c 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -221,8 +221,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 */
@@ -330,6 +340,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass;
/* method signatures */
+static jmethodID midGetAudioOutputDevices;
+static jmethodID midGetAudioInputDevices;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer;
@@ -655,8 +667,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,
@@ -666,7 +684,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,
@@ -678,9 +696,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();
@@ -911,6 +933,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,
@@ -1429,7 +1471,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;
@@ -1455,10 +1519,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 */
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index 62ae65311b3a..a1027124f44d 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -52,7 +52,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);