/* clang-format off */ #include "cubeb-jni.h" #include "cubeb-jni-instances.h" #include #include #include #include /* clang-format on */ #define AUDIO_STREAM_TYPE_MUSIC 3 struct cubeb_jni { jobject s_audio_manager_obj = nullptr; jclass s_audio_manager_class = 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 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 * cubeb_jni_init() { jobject ctx_obj = cubeb_jni_get_context_instance(); JNIEnv * jni_env = cubeb_get_jni_env_for_thread(); if (!jni_env || !ctx_obj) { return nullptr; } 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(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(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(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(audio_session)); if (!obj) { return nullptr; } cubeb_fx * res = new cubeb_fx; res->jni = cubeb_jni_ptr; res->type = type; res->obj = reinterpret_cast(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( jni_env->CallIntMethod(cubeb_fx_ptr->obj, fx.method_set_enabled, static_cast(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) { assert(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_audio_manager_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC } 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; }