echo-cancel: Let AEC module determine source/sink spec
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 6 Sep 2010 16:53:51 +0000 (22:23 +0530)
committerArun Raghavan <arun.raghavan@collabora.co.uk>
Tue, 7 Sep 2010 09:42:12 +0000 (15:12 +0530)
Since the source and sink specification will need to be determined by
the AEC algorithm (can it handle multi-channel audio, does it work with
a fixed sample rate, etc.), we negotiate these using inout parameters at
initialisation time.

There is opportunity to make the sink-handling more elegant. Since the
sink data isn't used for playback (just processing), we could pass
through the data as-is and resample to the required spec before using in
the cancellation algorithm. This isn't too important immediately, but
would be nice to have.

src/modules/echo-cancel/echo-cancel.h
src/modules/echo-cancel/module-echo-cancel.c
src/modules/echo-cancel/speex.c

index 186ce32..205c4d1 100644 (file)
@@ -46,7 +46,12 @@ struct pa_echo_canceller_params {
 typedef struct pa_echo_canceller pa_echo_canceller;
 
 struct pa_echo_canceller {
-    pa_bool_t   (*init)                 (pa_echo_canceller *ec, pa_sample_spec ss, pa_channel_map map, const char *args);
+    pa_bool_t   (*init)                 (pa_echo_canceller *ec,
+                                         pa_sample_spec *source_ss,
+                                         pa_channel_map *source_map,
+                                         pa_sample_spec *sink_ss,
+                                         pa_channel_map *sink_map,
+                                         const char *args);
     void        (*run)                  (pa_echo_canceller *ec, uint8_t *rec, uint8_t *play, uint8_t *out);
     void        (*done)                 (pa_echo_canceller *ec);
     uint32_t    (*get_block_size)       (pa_echo_canceller *ec);
@@ -55,7 +60,10 @@ struct pa_echo_canceller {
 };
 
 /* Speex canceller functions */
-pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec, pa_sample_spec ss, pa_channel_map map, const char *args);
+pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec,
+                           pa_sample_spec *source_ss, pa_channel_map *source_map,
+                           pa_sample_spec *sink_ss, pa_channel_map *sink_map,
+                           const char *args);
 void pa_speex_ec_run(pa_echo_canceller *ec, uint8_t *rec, uint8_t *play, uint8_t *out);
 void pa_speex_ec_done(pa_echo_canceller *ec);
 uint32_t pa_speex_ec_get_block_size(pa_echo_canceller *ec);
index d6968cd..6a88167 100644 (file)
@@ -1269,8 +1269,8 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) {
 
 int pa__init(pa_module*m) {
     struct userdata *u;
-    pa_sample_spec ss;
-    pa_channel_map map;
+    pa_sample_spec source_ss, sink_ss;
+    pa_channel_map source_map, sink_map;
     pa_modargs *ma;
     pa_source *source_master=NULL;
     pa_sink *sink_master=NULL;
@@ -1300,14 +1300,16 @@ int pa__init(pa_module*m) {
     }
     pa_assert(sink_master);
 
-    ss = source_master->sample_spec;
-    ss.format = PA_SAMPLE_S16LE;
-    map = source_master->channel_map;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+    source_ss = source_master->sample_spec;
+    source_map = source_master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &source_ss, &source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
         pa_log("Invalid sample format specification or channel map");
         goto fail;
     }
 
+    sink_ss = sink_master->sample_spec;
+    sink_map = sink_master->channel_map;
+
     u = pa_xnew0(struct userdata, 1);
     if (!u) {
         pa_log("Failed to alloc userdata");
@@ -1347,7 +1349,7 @@ int pa__init(pa_module*m) {
     u->asyncmsgq = pa_asyncmsgq_new(0);
     u->need_realign = TRUE;
     if (u->ec->init) {
-        if (!u->ec->init(u->ec, ss, map, pa_modargs_get_value(ma, "aec_args", NULL))) {
+        if (!u->ec->init(u->ec, &source_ss, &source_map, &sink_ss, &sink_map, pa_modargs_get_value(ma, "aec_args", NULL))) {
             pa_log("Failed to init AEC engine");
             goto fail;
         }
@@ -1359,8 +1361,8 @@ int pa__init(pa_module*m) {
     source_data.module = m;
     if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
         source_data.name = pa_sprintf_malloc("%s.echo-cancel", source_master->name);
-    pa_source_new_data_set_sample_spec(&source_data, &ss);
-    pa_source_new_data_set_channel_map(&source_data, &map);
+    pa_source_new_data_set_sample_spec(&source_data, &source_ss);
+    pa_source_new_data_set_channel_map(&source_data, &source_map);
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, source_master->name);
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
@@ -1406,8 +1408,8 @@ int pa__init(pa_module*m) {
     sink_data.module = m;
     if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
         sink_data.name = pa_sprintf_malloc("%s.echo-cancel", sink_master->name);
-    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
-    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    pa_sink_new_data_set_sample_spec(&sink_data, &sink_ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, sink_master->name);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
@@ -1456,8 +1458,8 @@ int pa__init(pa_module*m) {
 
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Source Stream");
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
-    pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
-    pa_source_output_new_data_set_channel_map(&source_output_data, &map);
+    pa_source_output_new_data_set_sample_spec(&source_output_data, &source_ss);
+    pa_source_output_new_data_set_channel_map(&source_output_data, &source_map);
 
     pa_source_output_new(&u->source_output, m->core, &source_output_data);
     pa_source_output_new_data_done(&source_output_data);
@@ -1487,8 +1489,8 @@ int pa__init(pa_module*m) {
     sink_input_data.sink = sink_master;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
-    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
-    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_map);
     sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
 
     pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
@@ -1518,9 +1520,9 @@ int pa__init(pa_module*m) {
     pa_sink_input_get_silence(u->sink_input, &silence);
 
     u->source_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0,
-        pa_frame_size(&ss), 1, 1, 0, &silence);
+        pa_frame_size(&source_ss), 1, 1, 0, &silence);
     u->sink_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0,
-        pa_frame_size(&ss), 1, 1, 0, &silence);
+        pa_frame_size(&sink_ss), 1, 1, 0, &silence);
 
     pa_memblock_unref(silence.memblock);
 
index a8fcc86..cb8212e 100644 (file)
@@ -39,9 +39,21 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec, pa_sample_spec ss, pa_channel_map map, const char *args)
+static void pa_speex_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *source_map,
+                                   pa_sample_spec *sink_ss, pa_channel_map *sink_map)
 {
-    int framelen, y, rate = ss.rate;
+    source_ss->format = PA_SAMPLE_S16LE;
+
+    *sink_ss = *source_ss;
+    *sink_map = *source_map;
+}
+
+pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec,
+                           pa_sample_spec *source_ss, pa_channel_map *source_map,
+                           pa_sample_spec *sink_ss, pa_channel_map *sink_map,
+                           const char *args)
+{
+    int framelen, y, rate;
     uint32_t frame_size_ms, filter_size_ms;
     pa_modargs *ma;
 
@@ -62,6 +74,9 @@ pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec, pa_sample_spec ss, pa_channel_
         goto fail;
     }
 
+    pa_speex_ec_fixate_spec(source_ss, source_map, sink_ss, sink_map);
+
+    rate = source_ss->rate;
     framelen = (rate * frame_size_ms) / 1000;
     /* framelen should be a power of 2, round down to nearest power of two */
     y = 1 << ((8 * sizeof (int)) - 2);
@@ -69,11 +84,11 @@ pa_bool_t pa_speex_ec_init(pa_echo_canceller *ec, pa_sample_spec ss, pa_channel_
       y >>= 1;
     framelen = y;
 
-    ec->params.priv.speex.blocksize = framelen * pa_frame_size (&ss);
+    ec->params.priv.speex.blocksize = framelen * pa_frame_size (source_ss);
 
-    pa_log_debug ("Using framelen %d, blocksize %lld, channels %d, rate %d", framelen, (long long) ec->params.priv.speex.blocksize, ss.channels, ss.rate);
+    pa_log_debug ("Using framelen %d, blocksize %lld, channels %d, rate %d", framelen, (long long) ec->params.priv.speex.blocksize, source_ss->channels, source_ss->rate);
 
-    ec->params.priv.speex.state = speex_echo_state_init_mc (framelen, (rate * filter_size_ms) / 1000, ss.channels, ss.channels);
+    ec->params.priv.speex.state = speex_echo_state_init_mc (framelen, (rate * filter_size_ms) / 1000, source_ss->channels, source_ss->channels);
 
     if (!ec->params.priv.speex.state)
        goto fail;