aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndreas Pehrson <[email protected]>2024-06-13 23:51:39 +0200
committerAndreas Pehrson <[email protected]>2024-06-14 01:32:12 +0200
commit05bfc896db1555588a3bdea80b30215eb518ae9f (patch)
tree1200f9af5bbc8afad4283ada5f54149f0171a150
parent667fb710d31ab624e7e9893eceb1b7738372c964 (diff)
downloadcubeb-05bfc896db1555588a3bdea80b30215eb518ae9f.tar.gz
cubeb-05bfc896db1555588a3bdea80b30215eb518ae9f.zip
Implement JNI layer cubeb_fx for controlling android audiofx classes
-rw-r--r--src/cubeb-jni.cpp199
-rw-r--r--src/cubeb-jni.h18
2 files changed, 185 insertions, 32 deletions
diff --git a/src/cubeb-jni.cpp b/src/cubeb-jni.cpp
index 8e7345b..57c642d 100644
--- a/src/cubeb-jni.cpp
+++ b/src/cubeb-jni.cpp
@@ -1,7 +1,10 @@
/* clang-format off */
-#include "jni.h"
-#include <assert.h>
+#include "cubeb-jni.h"
#include "cubeb-jni-instances.h"
+#include <assert.h>
+#include <atomic>
+#include <initializer_list>
+#include <jni.h>
/* clang-format on */
#define AUDIO_STREAM_TYPE_MUSIC 3
@@ -9,7 +12,53 @@
struct cubeb_jni {
jobject s_audio_manager_obj = nullptr;
jclass s_audio_manager_class = nullptr;
- jmethodID s_get_output_latency_id = nullptr;
+ jmethodID s_audio_manager_get_output_latency_id = nullptr;
+ struct cubeb_fx_def {
+ jclass clazz = nullptr;
+ jmethodID static_function_is_available = nullptr;
+ jmethodID static_function_create = nullptr;
+ jmethodID method_set_enabled = nullptr;
+ jmethodID method_release = nullptr;
+ } s_fxs[CUBEB_FX_SENTINEL__]{};
+ std::atomic<size_t> cubeb_fx_count{0};
+};
+
+static constexpr const char *
+cubeb_fx_name(cubeb_fx_type type)
+{
+ switch (type) {
+ case CUBEB_FX_ACOUSTIC_ECHO_CANCELER:
+ return "android/media/audiofx/AcousticEchoCanceler";
+ case CUBEB_FX_AUTOMATIC_GAIN_CONTROL:
+ return "android/media/audiofx/AutomaticGainControl";
+ case CUBEB_FX_NOISE_SUPPRESSOR:
+ return "android/media/audiofx/NoiseSuppressor";
+ case CUBEB_FX_SENTINEL__:
+ return nullptr;
+ }
+ return nullptr;
+}
+
+static constexpr const char *
+cubeb_fx_create_signature(cubeb_fx_type type)
+{
+ switch (type) {
+ case CUBEB_FX_ACOUSTIC_ECHO_CANCELER:
+ return "(I)Landroid/media/audiofx/AcousticEchoCanceler;";
+ case CUBEB_FX_AUTOMATIC_GAIN_CONTROL:
+ return "(I)Landroid/media/audiofx/AutomaticGainControl;";
+ case CUBEB_FX_NOISE_SUPPRESSOR:
+ return "(I)Landroid/media/audiofx/NoiseSuppressor;";
+ case CUBEB_FX_SENTINEL__:
+ return nullptr;
+ }
+ return nullptr;
+}
+
+struct cubeb_fx {
+ cubeb_jni * jni = nullptr;
+ cubeb_fx_type type = CUBEB_FX_SENTINEL__;
+ jobject obj = nullptr;
};
extern "C" cubeb_jni *
@@ -24,38 +73,120 @@ cubeb_jni_init()
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
assert(cubeb_jni_ptr);
- // Find the audio manager object and make it global to call it from another
- // method
- jclass context_class = jni_env->FindClass("android/content/Context");
- jfieldID audio_service_field = jni_env->GetStaticFieldID(
- context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
- jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class,
- audio_service_field);
- jmethodID get_system_service_id =
- jni_env->GetMethodID(context_class, "getSystemService",
- "(Ljava/lang/String;)Ljava/lang/Object;");
- jobject audio_manager_obj =
- jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
- cubeb_jni_ptr->s_audio_manager_obj =
- reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
-
- // Make the audio manager class a global reference in order to preserve method
- // id
- jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
- cubeb_jni_ptr->s_audio_manager_class =
- reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
- cubeb_jni_ptr->s_get_output_latency_id =
- jni_env->GetMethodID(audio_manager_class, "getOutputLatency", "(I)I");
-
- jni_env->DeleteLocalRef(ctx_obj);
- jni_env->DeleteLocalRef(context_class);
- jni_env->DeleteLocalRef(jstr);
- jni_env->DeleteLocalRef(audio_manager_obj);
- jni_env->DeleteLocalRef(audio_manager_class);
+ {
+ // Find the audio manager object and make it global to call it from another
+ // method
+ jclass context_class = jni_env->FindClass("android/content/Context");
+ jfieldID audio_service_field = jni_env->GetStaticFieldID(
+ context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
+ jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class,
+ audio_service_field);
+ jmethodID get_system_service_id =
+ jni_env->GetMethodID(context_class, "getSystemService",
+ "(Ljava/lang/String;)Ljava/lang/Object;");
+ jobject audio_manager_obj =
+ jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
+ cubeb_jni_ptr->s_audio_manager_obj =
+ reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
+ jni_env->DeleteLocalRef(ctx_obj);
+ jni_env->DeleteLocalRef(context_class);
+ jni_env->DeleteLocalRef(jstr);
+ jni_env->DeleteLocalRef(audio_manager_obj);
+ }
+
+ {
+ // Make the audio manager class a global reference in order to preserve
+ // method id
+ jclass audio_manager_class =
+ jni_env->FindClass("android/media/AudioManager");
+ cubeb_jni_ptr->s_audio_manager_class =
+ reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
+ cubeb_jni_ptr->s_audio_manager_get_output_latency_id =
+ jni_env->GetMethodID(audio_manager_class, "getOutputLatency", "(I)I");
+ jni_env->DeleteLocalRef(audio_manager_class);
+ }
+
+ auto populate_fx_class = [&](cubeb_fx_type type) {
+ jclass c = jni_env->FindClass(cubeb_fx_name(type));
+ cubeb_jni::cubeb_fx_def & fx = cubeb_jni_ptr->s_fxs[type];
+ fx.clazz = reinterpret_cast<jclass>(jni_env->NewGlobalRef(c));
+ fx.static_function_is_available =
+ jni_env->GetStaticMethodID(c, "isAvailable", "()Z");
+ fx.static_function_create = jni_env->GetStaticMethodID(
+ c, "create", cubeb_fx_create_signature(type));
+ fx.method_set_enabled = jni_env->GetMethodID(c, "setEnabled", "(Z)I");
+ fx.method_release = jni_env->GetMethodID(c, "release", "()V");
+ jni_env->DeleteLocalRef(c);
+ };
+ populate_fx_class(CUBEB_FX_ACOUSTIC_ECHO_CANCELER);
+ populate_fx_class(CUBEB_FX_AUTOMATIC_GAIN_CONTROL);
+ populate_fx_class(CUBEB_FX_NOISE_SUPPRESSOR);
return cubeb_jni_ptr;
}
+extern "C" bool
+cubeb_fx_is_available(cubeb_jni * cubeb_jni_ptr, cubeb_fx_type type)
+{
+ JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+ cubeb_jni::cubeb_fx_def & fx = cubeb_jni_ptr->s_fxs[type];
+ return JNI_FALSE != jni_env->CallStaticBooleanMethod(
+ fx.clazz, fx.static_function_is_available);
+}
+
+extern "C" cubeb_fx *
+cubeb_fx_init(cubeb_jni * cubeb_jni_ptr, cubeb_fx_type type, int audio_session)
+{
+ JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+ cubeb_jni::cubeb_fx_def & fx = cubeb_jni_ptr->s_fxs[type];
+ cubeb_jni_ptr->cubeb_fx_count += 1;
+
+ jobject obj = jni_env->CallStaticObjectMethod(
+ fx.clazz, fx.static_function_create, static_cast<jint>(audio_session));
+ if (!obj) {
+ return nullptr;
+ }
+
+ cubeb_fx * res = new cubeb_fx;
+ res->jni = cubeb_jni_ptr;
+ res->type = type;
+ res->obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(obj));
+
+ jni_env->DeleteLocalRef(obj);
+
+ return res;
+}
+
+extern "C" int
+cubeb_fx_set_enabled(cubeb_fx * cubeb_fx_ptr, bool enabled)
+{
+ assert(cubeb_fx_ptr);
+
+ JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+ cubeb_jni::cubeb_fx_def & fx = cubeb_fx_ptr->jni->s_fxs[cubeb_fx_ptr->type];
+ return static_cast<int>(
+ jni_env->CallIntMethod(cubeb_fx_ptr->obj, fx.method_set_enabled,
+ static_cast<jboolean>(enabled)));
+}
+
+extern "C" void
+cubeb_fx_destroy(cubeb_fx * cubeb_fx_ptr)
+{
+ assert(cubeb_fx_ptr);
+
+ JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
+
+ {
+ cubeb_jni::cubeb_fx_def & fx = cubeb_fx_ptr->jni->s_fxs[cubeb_fx_ptr->type];
+ jni_env->CallVoidMethod(cubeb_fx_ptr->obj, fx.method_release);
+ }
+
+ jni_env->DeleteGlobalRef(cubeb_fx_ptr->obj);
+
+ cubeb_fx_ptr->jni->cubeb_fx_count -= 1;
+ delete cubeb_fx_ptr;
+}
+
extern "C" int
cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
{
@@ -63,7 +194,7 @@ cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
return jni_env->CallIntMethod(
cubeb_jni_ptr->s_audio_manager_obj,
- cubeb_jni_ptr->s_get_output_latency_id,
+ cubeb_jni_ptr->s_audio_manager_get_output_latency_id,
AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC
}
@@ -71,12 +202,16 @@ extern "C" void
cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
{
assert(cubeb_jni_ptr);
+ assert(cubeb_jni_ptr->cubeb_fx_count == 0);
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
assert(jni_env);
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_obj);
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_class);
+ for (size_t i = 0; i < CUBEB_FX_SENTINEL__; ++i) {
+ jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_fxs[i].clazz);
+ }
delete cubeb_jni_ptr;
}
diff --git a/src/cubeb-jni.h b/src/cubeb-jni.h
index d63629f..0a2d41a 100644
--- a/src/cubeb-jni.h
+++ b/src/cubeb-jni.h
@@ -2,6 +2,13 @@
#define _CUBEB_JNI_H_
typedef struct cubeb_jni cubeb_jni;
+typedef struct cubeb_fx cubeb_fx;
+enum cubeb_fx_type {
+ CUBEB_FX_ACOUSTIC_ECHO_CANCELER,
+ CUBEB_FX_AUTOMATIC_GAIN_CONTROL,
+ CUBEB_FX_NOISE_SUPPRESSOR,
+ CUBEB_FX_SENTINEL__,
+};
#ifdef __cplusplus
extern "C" {
@@ -9,8 +16,19 @@ extern "C" {
cubeb_jni *
cubeb_jni_init();
+
+bool
+cubeb_fx_is_available(cubeb_jni * cubeb_jni_ptr, cubeb_fx_type type);
+cubeb_fx *
+cubeb_fx_init(cubeb_jni * cubeb_jni_ptr, cubeb_fx_type type, int audio_session);
+int
+cubeb_fx_set_enabled(cubeb_fx * cubeb_fx_ptr, bool enabled);
+void
+cubeb_fx_destroy(cubeb_fx * cubeb_fx_ptr);
+
int
cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
+
void
cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);