diff options
-rw-r--r-- | src/android/cubeb-output-latency.h | 2 | ||||
-rw-r--r-- | src/cubeb-jni.cpp | 116 | ||||
-rw-r--r-- | src/cubeb-jni.h | 14 | ||||
-rw-r--r-- | src/cubeb_aaudio.cpp | 35 |
4 files changed, 132 insertions, 35 deletions
diff --git a/src/android/cubeb-output-latency.h b/src/android/cubeb-output-latency.h index 70dec8d..0f2760f 100644 --- a/src/android/cubeb-output-latency.h +++ b/src/android/cubeb-output-latency.h @@ -69,7 +69,7 @@ cubeb_get_output_latency(output_latency_function * ol) assert(cubeb_output_latency_method_is_loaded(ol)); if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) { - return cubeb_get_output_latency_from_jni(ol->from_jni); + return cubeb_audio_manager_get_output_latency(ol->from_jni); } return cubeb_get_output_latency_from_media_library(ol->from_lib); diff --git a/src/cubeb-jni.cpp b/src/cubeb-jni.cpp index 8e7345b..5580d4e 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,9 +12,30 @@ 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; + jmethodID s_audio_manager_set_mode_id = nullptr; + struct cubeb_fx_def { + jclass clazz = nullptr; + jmethodID static_function_is_available = nullptr; + } s_fxs[CUBEB_FX_SENTINEL__]{}; }; +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; +} + extern "C" cubeb_jni * cubeb_jni_init() { @@ -24,46 +48,73 @@ 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)); + { + // 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_get_output_latency_id = - jni_env->GetMethodID(audio_manager_class, "getOutputLatency", "(I)I"); + { + // 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"); + cubeb_jni_ptr->s_audio_manager_set_mode_id = + jni_env->GetMethodID(audio_manager_class, "setMode", "(I)V"); + jni_env->DeleteLocalRef(audio_manager_class); + } - 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); + 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"); + 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" int -cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr) +cubeb_audio_manager_get_output_latency(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_get_output_latency_id, + cubeb_jni_ptr->s_audio_manager_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC } @@ -77,6 +128,9 @@ cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr) 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..c5bed6d 100644 --- a/src/cubeb-jni.h +++ b/src/cubeb-jni.h @@ -3,14 +3,26 @@ typedef struct cubeb_jni cubeb_jni; +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" { #endif cubeb_jni * cubeb_jni_init(); + +bool +cubeb_fx_is_available(cubeb_jni * cubeb_jni_ptr, cubeb_fx_type type); + int -cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr); +cubeb_audio_manager_get_output_latency(cubeb_jni * cubeb_jni_ptr); + void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr); diff --git a/src/cubeb_aaudio.cpp b/src/cubeb_aaudio.cpp index 9e81ed3..26f28f8 100644 --- a/src/cubeb_aaudio.cpp +++ b/src/cubeb_aaudio.cpp @@ -5,6 +5,7 @@ * accompanying file LICENSE for details. */ #include "cubeb-internal.h" +#include "cubeb-jni.h" #include "cubeb/cubeb.h" #include "cubeb_android.h" #include "cubeb_log.h" @@ -135,6 +136,10 @@ struct AAudioTimingInfo { uint32_t input_latency; }; +struct cubeb_jni_delete { + void operator()(cubeb_jni * jni) { cubeb_jni_destroy(jni); } +}; + struct cubeb_stream { /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context{}; @@ -179,6 +184,8 @@ struct cubeb { struct cubeb_ops const * ops{}; void * libaaudio{}; + std::unique_ptr<cubeb_jni, cubeb_jni_delete> jni; + struct { // The state thread: it waits for state changes and stops // drained streams. @@ -1743,6 +1750,28 @@ aaudio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) return CUBEB_OK; } +int +aaudio_get_supported_input_processing_params( + cubeb * ctx, cubeb_input_processing_params * params) +{ + if (!ctx->jni) { + return CUBEB_ERROR_NOT_SUPPORTED; + } + + if (cubeb_fx_is_available(ctx->jni.get(), CUBEB_FX_ACOUSTIC_ECHO_CANCELER)) { + *params = static_cast<cubeb_input_processing_params>( + CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION); + } else { + *params = CUBEB_INPUT_PROCESSING_PARAM_NONE; + } + + LOG("%s: Supported params are %s (%d)", __func__, + input_processing_params_to_str(*params), *params); + return CUBEB_OK; +} + extern "C" int aaudio_init(cubeb ** context, char const * context_name); @@ -1750,9 +1779,10 @@ const static struct cubeb_ops aaudio_ops = { /*.init =*/aaudio_init, /*.get_backend_id =*/aaudio_get_backend_id, /*.get_max_channel_count =*/aaudio_get_max_channel_count, - /* .get_min_latency =*/aaudio_get_min_latency, + /*.get_min_latency =*/aaudio_get_min_latency, /*.get_preferred_sample_rate =*/aaudio_get_preferred_sample_rate, - /*.get_supported_input_processing_params =*/nullptr, + /*.get_supported_input_processing_params =*/ + aaudio_get_supported_input_processing_params, /*.enumerate_devices =*/nullptr, /*.device_collection_destroy =*/nullptr, /*.destroy =*/aaudio_destroy, @@ -1800,6 +1830,7 @@ aaudio_init(cubeb ** context, char const * /* context_name */) cubeb * ctx = new cubeb; ctx->ops = &aaudio_ops; ctx->libaaudio = libaaudio; + ctx->jni.reset(cubeb_jni_init()); ctx->state.thread = std::thread(state_thread, ctx); |