aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndreas Pehrson <[email protected]>2024-06-14 01:30:51 +0200
committerAndreas Pehrson <[email protected]>2024-06-24 11:29:24 +0200
commit76ffc3e9e159c05c2513587e82e396a41eaf55f8 (patch)
tree73e4e2651b560719c1c4fd2dbaf6d778a4f9cedc
parent1ed53cd69ab9ccd3172f446a697ddd3ece56bc3a (diff)
downloadcubeb-76ffc3e9e159c05c2513587e82e396a41eaf55f8.tar.gz
cubeb-76ffc3e9e159c05c2513587e82e396a41eaf55f8.zip
Implement aaudio_set_input_processing_params
-rw-r--r--src/cubeb_aaudio.cpp161
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};