From 3d2f2424eb0740cbaa250867fc7280225e4d1c88 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 4 Nov 2011 16:17:26 +0530 Subject: [PATCH] echo-cancel: Add infrastructure for cancellers to do AGC This adds some infrastructure for canceller implementations to also perform acoustic gain control. Cancellers now have a couple of new API calls that allow them to get/set capture volume. This is made slightly complex by the fact that cancellation happens in thread context while most volume mangling needs to be done in main context. To deal with this, while getting the volume we save source volume updates as they are propagated to thread context and use this cached value for queries. To set the volume, we send an async message to main context and let that set the source volume. --- src/modules/echo-cancel/echo-cancel.h | 9 ++ src/modules/echo-cancel/module-echo-cancel.c | 97 +++++++++++++++++--- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/src/modules/echo-cancel/echo-cancel.h b/src/modules/echo-cancel/echo-cancel.h index 799631b0..8a382984 100644 --- a/src/modules/echo-cancel/echo-cancel.h +++ b/src/modules/echo-cancel/echo-cancel.h @@ -37,6 +37,8 @@ /* Common data structures */ +typedef struct pa_echo_canceller_msg pa_echo_canceller_msg; + typedef struct pa_echo_canceller_params pa_echo_canceller_params; struct pa_echo_canceller_params { @@ -109,8 +111,15 @@ struct pa_echo_canceller { /* Structure with common and engine-specific canceller parameters. */ pa_echo_canceller_params params; + + /* msgobject that can be used to send messages back to the main thread */ + pa_echo_canceller_msg *msg; }; +/* Functions to be used by the canceller analog gain control routines */ +void pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec, pa_cvolume *v); +void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v); + /* Speex canceller functions */ pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, pa_sample_spec *source_ss, pa_channel_map *source_map, diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 9b69f828..05ebde36 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -159,6 +159,16 @@ static const pa_echo_canceller ec_table[] = { * don't give enough accuracy to be able to do that right now. */ +struct userdata; + +struct pa_echo_canceller_msg { + pa_msgobject parent; + struct userdata *userdata; +}; + +PA_DEFINE_PRIVATE_CLASS(pa_echo_canceller_msg, pa_msgobject); +#define PA_ECHO_CANCELLER_MSG(o) (pa_echo_canceller_msg_cast(o)) + struct snapshot { pa_usec_t sink_now; pa_usec_t sink_latency; @@ -218,6 +228,12 @@ struct userdata { FILE *played_file; FILE *canceled_file; FILE *drift_file; + + pa_bool_t use_volume_sharing; + + struct { + pa_cvolume current_volume; + } thread_info; }; static void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot); @@ -254,6 +270,10 @@ enum { SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT }; +enum { + ECHO_CANCELLER_MESSAGE_SET_VOLUME, +}; + static int64_t calc_diff(struct userdata *u, struct snapshot *snapshot) { int64_t buffer, diff_time, buffer_latency; @@ -378,6 +398,9 @@ static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t return 0; + case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED: + u->thread_info.current_volume = u->source->reference_volume; + break; } return pa_source_process_msg(o, code, data, offset, chunk); @@ -1466,6 +1489,51 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) { pa_sink_mute_changed(u->sink, i->muted); } +/* Called from main context */ +static int canceller_process_msg_cb(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { + struct pa_echo_canceller_msg *msg; + struct userdata *u; + + pa_assert(o); + + msg = PA_ECHO_CANCELLER_MSG(o); + u = msg->userdata; + + switch (code) { + case ECHO_CANCELLER_MESSAGE_SET_VOLUME: { + pa_cvolume *v = (pa_cvolume *) userdata; + + if (u->use_volume_sharing) + pa_source_set_volume(u->source, v, TRUE, FALSE); + else + pa_source_output_set_volume(u->source_output, v, FALSE, TRUE); + + break; + } + + default: + pa_assert_not_reached(); + break; + } + + return 0; +} + +/* Called by the canceller, so thread context */ +void pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec, pa_cvolume *v) { + *v = ec->msg->userdata->thread_info.current_volume; +} + +/* Called by the canceller, so thread context */ +void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v) { + if (!pa_cvolume_equal(&ec->msg->userdata->thread_info.current_volume, v)) { + pa_cvolume *vol = pa_xnewdup(pa_cvolume, v, 1); + + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(ec->msg), ECHO_CANCELLER_MESSAGE_SET_VOLUME, vol, 0, NULL, + pa_xfree); + } +} + static pa_echo_canceller_method_t get_ec_method_from_string(const char *method) { if (pa_streq(method, "speex")) return PA_ECHO_CANCELLER_SPEEX; @@ -1526,7 +1594,6 @@ int pa__init(pa_module*m) { pa_sink_new_data sink_data; pa_memchunk silence; uint32_t temp; - pa_bool_t use_volume_sharing = TRUE; pa_assert(m); @@ -1560,11 +1627,6 @@ int pa__init(pa_module*m) { sink_ss = sink_master->sample_spec; sink_map = sink_master->channel_map; - if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { - pa_log("use_volume_sharing= expects a boolean argument"); - goto fail; - } - u = pa_xnew0(struct userdata, 1); if (!u) { pa_log("Failed to alloc userdata"); @@ -1575,6 +1637,12 @@ int pa__init(pa_module*m) { m->userdata = u; u->dead = FALSE; + u->use_volume_sharing = TRUE; + if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &u->use_volume_sharing) < 0) { + pa_log("use_volume_sharing= expects a boolean argument"); + goto fail; + } + temp = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; if (pa_modargs_get_value_u32(ma, "adjust_time", &temp) < 0) { pa_log("Failed to parse adjust_time value"); @@ -1653,7 +1721,7 @@ int pa__init(pa_module*m) { } u->source = pa_source_new(m->core, &source_data, (source_master->flags & (PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY)) - | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)); + | (u->use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)); pa_source_new_data_done(&source_data); if (!u->source) { @@ -1666,7 +1734,7 @@ int pa__init(pa_module*m) { u->source->update_requested_latency = source_update_requested_latency_cb; pa_source_set_get_mute_callback(u->source, source_get_mute_cb); pa_source_set_set_mute_callback(u->source, source_set_mute_cb); - if (!use_volume_sharing) { + if (!u->use_volume_sharing) { pa_source_set_get_volume_callback(u->source, source_get_volume_cb); pa_source_set_set_volume_callback(u->source, source_set_volume_cb); pa_source_enable_decibel_volume(u->source, TRUE); @@ -1703,7 +1771,7 @@ int pa__init(pa_module*m) { } u->sink = pa_sink_new(m->core, &sink_data, (sink_master->flags & (PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY)) - | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); + | (u->use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -1716,7 +1784,7 @@ int pa__init(pa_module*m) { u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); - if (!use_volume_sharing) { + if (!u->use_volume_sharing) { pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); pa_sink_enable_decibel_volume(u->sink, TRUE); } @@ -1793,7 +1861,7 @@ int pa__init(pa_module*m) { u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; - if (!use_volume_sharing) + if (!u->use_volume_sharing) u->sink_input->volume_changed = sink_input_volume_changed_cb; u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; @@ -1841,12 +1909,17 @@ int pa__init(pa_module*m) { } } + u->ec->msg = pa_msgobject_new(pa_echo_canceller_msg); + u->ec->msg->parent.process_msg = canceller_process_msg_cb; + u->ec->msg->userdata = u; + + u->thread_info.current_volume = u->source->reference_volume; + pa_sink_put(u->sink); pa_source_put(u->source); pa_sink_input_put(u->sink_input); pa_source_output_put(u->source_output); - pa_modargs_free(ma); return 0; -- 2.39.2