[pulseaudio-discuss] [PATCH 1/3] echo-cancel: Do not bypass EC implementation when play stream is empty

Stefan Huber s.huber at bct-electronic.com
Thu Feb 7 05:40:05 PST 2013


When the play stream from the EC sink has not enough data available then
the EC implementation is currently bypassed by directly forwarding the
record bytes to the EC source. Since EC implementations maintain their
own buffers and cause certain latencies, a bypass leads to glitches as
the out stream stream jumps forth and back in time. Furthermore, some
EC implementations may also apply noise reduction or other sound
enhancing techniques, which are therefore bypassed, too.

Fix this by passing silence bytes to the EC implementation if the play
stream runs empty. Hence, this patch keeps the EC implementation running
even if the play stream has no data available.
---
 src/modules/echo-cancel/module-echo-cancel.c |   95 ++++++++++++++------------
 1 file changed, 52 insertions(+), 43 deletions(-)

diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 11ad1de..d6feaa4 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -238,6 +238,8 @@ struct userdata {
     size_t sink_rem;
     size_t source_rem;
 
+    pa_memchunk silence;
+
     pa_atomic_t request_resync;
 
     pa_time_event *time_event;
@@ -820,60 +822,68 @@ static void do_push(struct userdata *u) {
     plen = pa_memblockq_get_length(u->sink_memblockq);
 
     while (rlen >= u->source_blocksize) {
+
         /* take fixed block from recorded samples */
         pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_blocksize, &rchunk);
 
         if (plen >= u->sink_blocksize) {
             /* take fixed block from played samples */
             pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk);
+        } else {
+            /* If we run out of play data, we substitute the play bytes with
+             * silence bytes in order keep the EC implementation with its
+             * internal buffers running. */
+
+            pchunk = u->silence;
+            pa_memblock_ref(pchunk.memblock);
+            pchunk.index = 0;
+            pchunk.length = u->sink_blocksize;
+        }
 
-            rdata = pa_memblock_acquire(rchunk.memblock);
-            rdata += rchunk.index;
-            pdata = pa_memblock_acquire(pchunk.memblock);
-            pdata += pchunk.index;
-
-            cchunk.index = 0;
-            cchunk.length = u->source_blocksize;
-            cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
-            cdata = pa_memblock_acquire(cchunk.memblock);
-
-            if (u->save_aec) {
-                if (u->captured_file)
-                    unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
-                if (u->played_file)
-                    unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
-            }
+        rdata = pa_memblock_acquire(rchunk.memblock);
+        rdata += rchunk.index;
+        pdata = pa_memblock_acquire(pchunk.memblock);
+        pdata += pchunk.index;
 
-            /* perform echo cancellation */
-            u->ec->run(u->ec, rdata, pdata, cdata);
+        cchunk.index = 0;
+        cchunk.length = u->source_blocksize;
+        cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
+        cdata = pa_memblock_acquire(cchunk.memblock);
 
-            if (u->save_aec) {
-                if (u->canceled_file)
-                    unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
-            }
+        if (u->save_aec) {
+            if (u->captured_file)
+                unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
+            if (u->played_file)
+                unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
+        }
 
-            pa_memblock_release(cchunk.memblock);
-            pa_memblock_release(pchunk.memblock);
-            pa_memblock_release(rchunk.memblock);
+        /* perform echo cancellation */
+        u->ec->run(u->ec, rdata, pdata, cdata);
 
-            /* drop consumed sink samples */
-            pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
-            pa_memblock_unref(pchunk.memblock);
+        if (u->save_aec) {
+            if (u->canceled_file)
+                unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
+        }
 
-            pa_memblock_unref(rchunk.memblock);
-            /* the filtered samples now become the samples from our
-             * source */
-            rchunk = cchunk;
+        pa_memblock_release(cchunk.memblock);
+        pa_memblock_release(pchunk.memblock);
+        pa_memblock_release(rchunk.memblock);
 
+        /* drop consumed source samples */
+        pa_memblockq_drop(u->source_memblockq, u->source_blocksize);
+        pa_memblock_unref(rchunk.memblock);
+        rlen -= u->source_blocksize;
+
+        if (plen >= u->sink_blocksize) {
+            /* drop consumed sink samples */
+            pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
             plen -= u->sink_blocksize;
         }
+        pa_memblock_unref(pchunk.memblock);
 
         /* forward the (echo-canceled) data to the virtual source */
-        pa_source_post(u->source, &rchunk);
-        pa_memblock_unref(rchunk.memblock);
-
-        pa_memblockq_drop(u->source_memblockq, u->source_blocksize);
-        rlen -= u->source_blocksize;
+        pa_source_post(u->source, &cchunk);
+        pa_memblock_unref(cchunk.memblock);
     }
 }
 
@@ -1639,7 +1649,6 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data sink_input_data;
     pa_source_new_data source_data;
     pa_sink_new_data sink_data;
-    pa_memchunk silence;
     uint32_t temp;
     uint32_t nframes = 0;
 
@@ -1920,14 +1929,12 @@ int pa__init(pa_module*m) {
 
     u->sink->input_to_master = u->sink_input;
 
-    pa_sink_input_get_silence(u->sink_input, &silence);
+    pa_sink_input_get_silence(u->sink_input, &u->silence);
 
     u->source_memblockq = pa_memblockq_new("module-echo-cancel source_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
-        &source_ss, 1, 1, 0, &silence);
+        &source_ss, 1, 1, 0, &u->silence);
     u->sink_memblockq = pa_memblockq_new("module-echo-cancel sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
-        &sink_ss, 1, 1, 0, &silence);
-
-    pa_memblock_unref(silence.memblock);
+        &sink_ss, 1, 1, 0, &u->silence);
 
     if (!u->source_memblockq || !u->sink_memblockq) {
         pa_log("Failed to create memblockq.");
@@ -2006,6 +2013,8 @@ void pa__done(pa_module*m) {
 
     u->dead = TRUE;
 
+    pa_memblock_unref(u->silence.memblock);
+
     /* See comments in source_output_kill_cb() above regarding
      * destruction order! */
 
-- 
1.7.9.5



More information about the pulseaudio-discuss mailing list