diff options
author | Andreas Pehrson <[email protected]> | 2024-06-24 11:17:37 +0200 |
---|---|---|
committer | Andreas Pehrson <[email protected]> | 2024-06-24 11:28:05 +0200 |
commit | 220eb1978f8008127b00e58baef119e46e855913 (patch) | |
tree | 4c568f6802a82e93622634c72d0873cab2780476 | |
parent | 667fb710d31ab624e7e9893eceb1b7738372c964 (diff) | |
download | cubeb-220eb1978f8008127b00e58baef119e46e855913.tar.gz cubeb-220eb1978f8008127b00e58baef119e46e855913.zip |
Implement aaudio_get_supported_input_processing_params
This also sets up the AAudio backend with JNI if available from the
client. Input processing params are only supported if JNI is available
and it reports that AEC is available on the system.
-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); |