diff options
author | Andreas Pehrson <[email protected]> | 2024-06-14 01:30:51 +0200 |
---|---|---|
committer | Andreas Pehrson <[email protected]> | 2024-06-24 11:29:24 +0200 |
commit | 76ffc3e9e159c05c2513587e82e396a41eaf55f8 (patch) | |
tree | 73e4e2651b560719c1c4fd2dbaf6d778a4f9cedc | |
parent | 1ed53cd69ab9ccd3172f446a697ddd3ece56bc3a (diff) | |
download | cubeb-76ffc3e9e159c05c2513587e82e396a41eaf55f8.tar.gz cubeb-76ffc3e9e159c05c2513587e82e396a41eaf55f8.zip |
Implement aaudio_set_input_processing_params
-rw-r--r-- | src/cubeb_aaudio.cpp | 161 |
1 files changed, 149 insertions, 12 deletions
diff --git a/src/cubeb_aaudio.cpp b/src/cubeb_aaudio.cpp index 6176bcb..fe8a8db 100644 --- a/src/cubeb_aaudio.cpp +++ b/src/cubeb_aaudio.cpp @@ -23,6 +23,7 @@ #include <limits> #include <memory> #include <mutex> +#include <optional> #include <thread> #include <vector> @@ -65,6 +66,7 @@ using namespace std; X(AAudioStream_getState) \ X(AAudioStream_getFramesWritten) \ X(AAudioStream_getFramesPerBurst) \ + X(AAudioStream_getInputPreset) \ X(AAudioStreamBuilder_setInputPreset) \ X(AAudioStreamBuilder_setUsage) \ X(AAudioStreamBuilder_setFramesPerDataCallback) @@ -83,7 +85,6 @@ using namespace std; // X(AAudioStreamBuilder_setSessionId) \ // X(AAudioStream_getUsage) \ // X(AAudioStream_getContentType) \ - // X(AAudioStream_getInputPreset) \ // X(AAudioStream_getSessionId) \ // END: not needed or added later on @@ -177,6 +178,7 @@ struct cubeb_stream { unsigned out_frame_size{}; bool voice_input{}; bool voice_output{}; + cubeb_input_processing_params input_processing_params{}; uint64_t previous_clock{}; }; @@ -893,7 +895,7 @@ aaudio_input_data_cb(AAudioStream * astream, void * user_data, return AAUDIO_CALLBACK_RESULT_CONTINUE; } -static void +static int reinitialize_stream_locked(cubeb_stream * stm, lock_guard<mutex> & lock) { stream_state state = stm->state.load(); @@ -912,7 +914,7 @@ reinitialize_stream_locked(cubeb_stream * stm, lock_guard<mutex> & lock) LOG("aaudio_stream_init_impl error while reiniting: %s", WRAP(AAudio_convertResultToText)(err)); stm->state.store(stream_state::ERROR); - return; + return err; } if (was_playing) { @@ -922,9 +924,11 @@ reinitialize_stream_locked(cubeb_stream * stm, lock_guard<mutex> & lock) LOG("aaudio_stream_start error while reiniting: %s", WRAP(AAudio_convertResultToText)(err)); stm->state.store(stream_state::ERROR); - return; + return err; } } + + return CUBEB_OK; } static void @@ -1017,6 +1021,7 @@ aaudio_stream_destroy(cubeb_stream * stm) { lock_guard lock(stm->mutex); stm->in_use.store(false); + stm->input_processing_params = CUBEB_INPUT_PROCESSING_PARAM_NONE; aaudio_stream_destroy_locked(stm, lock); } @@ -1081,6 +1086,28 @@ aaudio_stream_destroy_locked(cubeb_stream * stm, lock_guard<mutex> & lock) stm->state.store(stream_state::INIT); } +static std::optional<aaudio_input_preset_t> +input_processing_params_to_input_preset(cubeb_input_processing_params params) +{ + // Mapping based on + // https://source.android.com/docs/core/audio/implement-pre-processing. + switch (static_cast<int>(params)) { + case CUBEB_INPUT_PROCESSING_PARAM_NONE: + return AAUDIO_INPUT_PRESET_UNPROCESSED; + case CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL: + return AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; + case (CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION): + return AAUDIO_INPUT_PRESET_CAMCORDER; + case (CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION): + return AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + default: + return std::nullopt; + } +} + static int aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock) { @@ -1198,13 +1225,11 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock) // input cubeb_stream_params in_params; if (stm->input_stream_params) { - // Match what the OpenSL backend does for now, we could use UNPROCESSED and - // VOICE_COMMUNICATION here, but we'd need to make it clear that - // application-level AEC and other voice processing should be disabled - // there. - int input_preset = stm->voice_input ? AAUDIO_INPUT_PRESET_VOICE_RECOGNITION - : AAUDIO_INPUT_PRESET_CAMCORDER; - WRAP(AAudioStreamBuilder_setInputPreset)(sb, input_preset); + aaudio_input_preset_t preset = + stm->voice_input ? *input_processing_params_to_input_preset( + stm->input_processing_params) + : AAUDIO_INPUT_PRESET_CAMCORDER; + WRAP(AAudioStreamBuilder_setInputPreset)(sb, preset); WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_INPUT); WRAP(AAudioStreamBuilder_setDataCallback)(sb, in_data_callback, stm); assert(stm->latency_frames < std::numeric_limits<int32_t>::max()); @@ -1676,6 +1701,118 @@ aaudio_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } +static const char * +input_preset_to_str(aaudio_input_preset_t preset) +{ + switch (preset) { + case AAUDIO_INPUT_PRESET_GENERIC: + return "GENERIC"; + case AAUDIO_INPUT_PRESET_CAMCORDER: + return "CAMCORDER"; + case AAUDIO_INPUT_PRESET_VOICE_RECOGNITION: + return "VOICE_RECOGNITION"; + case AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION: + return "VOICE_COMMUNICATION"; + case AAUDIO_INPUT_PRESET_UNPROCESSED: + return "UNPROCESSED"; + case AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE: + return "VOICE_PERFORMANCE"; + } + return "UNKNOWN"; +} + +static const char * +input_processing_params_to_str(cubeb_input_processing_params params) +{ + switch (static_cast<int>(params)) { + case CUBEB_INPUT_PROCESSING_PARAM_NONE: + return "None"; + case CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION: + return "AEC"; + case CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL: + return "AGC"; + case CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION: + return "NS"; + case CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL: + return "AEC | AGC"; + case CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION: + return "AEC | NS"; + case CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION: + return "AGC | NS"; + case CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION: + return "AEC | AGC | NS"; + } + return "Unknown"; +} + +static int +aaudio_set_input_processing_params(cubeb_stream * stm, + cubeb_input_processing_params params) +{ + assert(stm); + + LOG("%s(stm=%p, params=%s)", __func__, stm, + input_processing_params_to_str(params)); + + if (!stm->istream) { + LOG("%s: no input stream", __func__); + return CUBEB_ERROR_INVALID_PARAMETER; + } + + if (!stm->voice_input) { + LOG("%s: input stream is not a voice stream", __func__); + return CUBEB_ERROR_INVALID_PARAMETER; + } + + const auto preset = input_processing_params_to_input_preset(params); + if (!preset) { + LOG("%s: attempted to set unsupported params %s", __func__, + input_processing_params_to_str(params)); + return CUBEB_ERROR_INVALID_PARAMETER; + } + + const auto current = WRAP(AAudioStream_getInputPreset)(stm->istream); + if (*preset == current) { + LOG("%s: no change in preset: %s", __func__, input_preset_to_str(current)); + return CUBEB_OK; + } + + lock_guard lock(stm->mutex); + LOG("%s: reinitializing stream due to preset change. %s->%s", __func__, + input_preset_to_str(current), input_preset_to_str(*preset)); + cubeb_input_processing_params current_params = stm->input_processing_params; + stm->input_processing_params = params; + int err = reinitialize_stream_locked(stm, lock); + if (err == CUBEB_OK) { + LOG("%s: stream %p successfully set params %s (preset %s).", __func__, stm, + input_processing_params_to_str(params), input_preset_to_str(*preset)); + return CUBEB_OK; + } + + stm->input_processing_params = current_params; + err = reinitialize_stream_locked(stm, lock); + if (err == CUBEB_OK) { + LOG("%s: stream %p failed to set params %s (preset %s), current params " + "%s have been restored.", + __func__, stm, input_processing_params_to_str(params), + input_preset_to_str(*preset), + input_processing_params_to_str(current_params)); + } else { + LOG("%s: stream %p failed to set params %s (preset %s), and restoring " + "current params %s failed also. e=%d.", + __func__, stm, input_processing_params_to_str(params), + input_preset_to_str(*preset), + input_processing_params_to_str(current_params), err); + stm->state.store(stream_state::ERROR); + } + return CUBEB_ERROR; +} + aaudio_data_callback_result_t dummy_callback(AAudioStream * stream, void * userData, void * audioData, int32_t numFrames) @@ -1803,7 +1940,7 @@ const static struct cubeb_ops aaudio_ops = { /*.stream_set_name =*/nullptr, /*.stream_get_current_device =*/nullptr, /*.stream_set_input_mute =*/nullptr, - /*.stream_set_input_processing_params =*/nullptr, + /*.stream_set_input_processing_params =*/aaudio_set_input_processing_params, /*.stream_device_destroy =*/nullptr, /*.stream_register_device_changed_callback =*/nullptr, /*.register_device_collection_changed =*/nullptr}; |