]> code.delx.au - pulseaudio/commitdiff
echo-cancel: Add infrastructure for cancellers to do AGC
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Fri, 4 Nov 2011 10:47:26 +0000 (16:17 +0530)
committerArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 7 Nov 2011 12:07:50 +0000 (17:37 +0530)
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
src/modules/echo-cancel/module-echo-cancel.c

index 799631b0bb42fe50f728555e04d0bb983a732995..8a382984efe9a35a1f200cb3ece4472e43b9f4a3 100644 (file)
@@ -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,
index 9b69f8284c19d7858c6efe4ad8dad32140ce6f1e..05ebde366aa8a0ccb3534dc77664eb43eccacf44 100644 (file)
@@ -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;