summaryrefslogtreecommitdiff
path: root/Source/Android
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2024-06-06 15:38:41 +0200
committerJosJuice <josjuice@gmail.com>2025-05-25 11:59:33 +0200
commitf99d3dbd5ccd80bcd071cf49e689f6b6724df499 (patch)
treec68fc6111b608654dc52fff6c9e909ad93781f48 /Source/Android
parent34e8fb068f50c585a6f69eca365d1008c898f0c3 (diff)
Android: Ask system for optimal audio buffer size and sample rate
This can reduce audio latency according to https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes#perform. Previously we were using the hardcoded values of 48000 Hz and 256 frames per buffer. The sample rate we use with this change is 48000 Hz on all devices I'm aware of, but the buffer size does vary across devices. Terminology note: The old code used the term "sample" to refer to what Android refers to as a "frame". "Frame" is a clearer term to use for this, so I've changed OpenSLESStream's terminology. One frame consists of one sample per channel.
Diffstat (limited to 'Source/Android')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AudioUtils.kt28
-rw-r--r--Source/Android/jni/AndroidCommon/IDCache.cpp27
-rw-r--r--Source/Android/jni/AndroidCommon/IDCache.h4
3 files changed, 59 insertions, 0 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AudioUtils.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AudioUtils.kt
new file mode 100644
index 0000000000..68e3573fa5
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AudioUtils.kt
@@ -0,0 +1,28 @@
+package org.dolphinemu.dolphinemu.utils
+
+import android.content.Context
+import android.media.AudioManager
+import androidx.annotation.Keep
+import org.dolphinemu.dolphinemu.DolphinApplication
+
+object AudioUtils {
+ @JvmStatic @Keep
+ fun getSampleRate(): Int =
+ getAudioServiceProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE, 48000)
+
+ @JvmStatic @Keep
+ fun getFramesPerBuffer(): Int =
+ getAudioServiceProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER, 256)
+
+ private fun getAudioServiceProperty(property: String, fallback: Int): Int {
+ return try {
+ val context = DolphinApplication.getAppContext()
+ val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+ Integer.parseUnsignedInt(am.getProperty(property))
+ } catch (e: NullPointerException) {
+ fallback
+ } catch (e: NumberFormatException) {
+ fallback
+ }
+ }
+}
diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp
index 08ec2c9804..d8dfa2b7c4 100644
--- a/Source/Android/jni/AndroidCommon/IDCache.cpp
+++ b/Source/Android/jni/AndroidCommon/IDCache.cpp
@@ -122,6 +122,10 @@ static jmethodID s_permission_handler_request_record_audio_permission;
static jmethodID s_runnable_run;
+static jclass s_audio_utils_class;
+static jmethodID s_audio_utils_get_sample_rate;
+static jmethodID s_audio_utils_get_frames_per_buffer;
+
namespace IDCache
{
JNIEnv* GetEnvForThread()
@@ -562,6 +566,21 @@ jmethodID GetRunnableRun()
return s_runnable_run;
}
+jclass GetAudioUtilsClass()
+{
+ return s_audio_utils_class;
+}
+
+jmethodID GetAudioUtilsGetSampleRate()
+{
+ return s_audio_utils_get_sample_rate;
+}
+
+jmethodID GetAudioUtilsGetFramesPerBuffer()
+{
+ return s_audio_utils_get_frames_per_buffer;
+}
+
} // namespace IDCache
extern "C" {
@@ -798,6 +817,13 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_runnable_run = env->GetMethodID(runnable_class, "run", "()V");
env->DeleteLocalRef(runnable_class);
+ const jclass audio_utils_class = env->FindClass("org/dolphinemu/dolphinemu/utils/AudioUtils");
+ s_audio_utils_class = reinterpret_cast<jclass>(env->NewGlobalRef(audio_utils_class));
+ s_audio_utils_get_sample_rate = env->GetStaticMethodID(audio_utils_class, "getSampleRate", "()I");
+ s_audio_utils_get_frames_per_buffer =
+ env->GetStaticMethodID(audio_utils_class, "getFramesPerBuffer", "()I");
+ env->DeleteLocalRef(audio_utils_class);
+
return JNI_VERSION;
}
@@ -834,5 +860,6 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_core_device_control_class);
env->DeleteGlobalRef(s_input_detector_class);
env->DeleteGlobalRef(s_permission_handler_class);
+ env->DeleteGlobalRef(s_audio_utils_class);
}
}
diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h
index 3cc8c11b5e..e44e68e431 100644
--- a/Source/Android/jni/AndroidCommon/IDCache.h
+++ b/Source/Android/jni/AndroidCommon/IDCache.h
@@ -121,4 +121,8 @@ jmethodID GetPermissionHandlerRequestRecordAudioPermission();
jmethodID GetRunnableRun();
+jclass GetAudioUtilsClass();
+jmethodID GetAudioUtilsGetSampleRate();
+jmethodID GetAudioUtilsGetFramesPerBuffer();
+
} // namespace IDCache