Open Source Echo Canceller Part 2 – How Echo Cancellers Work

This post explains the basics of how echo cancellers using a very simple C code example.

From Part One of this series here is a block diagram of an echo canceller:

Lets convert this diagram to a model of the echo and echo canceller, and put some sample numbers into the system:

In this case we model the echo as a simple multiplication of the tx signal. So any signal we send down the tx port will be reflected back to us as an echo signal that is 0.1 times the size of the tx signal. The idea of the echo canceller is to work out the value of the echo (which it stores in the variable h). If we estimate h correctly, then our model of the echo (tx times h) will exactly cancel the echo signal.

Echo cancellers adapt to the particular characteristics of the echo in your telephone line. Each phone line is different so the actual amount of echo (in this case 0.1) is unknown. The echo canceller must learn this value somehow, by looking at what goes in (tx) and what comes out (rx) of the hybrid. Many echo cancellers use an adaptive filter to “learn” the echo characteristics.

Here is some C code that shows how it all works:

/* echo.c simple echo canceller demo */
#include <stdio.h>
#define ECHO 0.1
#define N    10
#define BETA 0.3
int main() {
    float tx, rx, ec, h;
    int   i;
    tx = 1.0;
    h  = 0.0;
    printf("Step  tx   rx   ec   h\n");
    for(i=0; i<N; i++) {
        rx = ECHO*tx;
        ec = rx - h*tx;
        h  += BETA*ec;
        printf("[%d]   %3.2f %3.2f %3.2f %3.2f\n",
                i, tx, rx, ec, h);
    return 0;

Here is the output from a sample run:

Step  tx   rx   ec   h
[0]   1.00 0.10 0.10 0.03
[1]   1.00 0.10 0.07 0.05
[2]   1.00 0.10 0.05 0.07
[3]   1.00 0.10 0.03 0.08
[4]   1.00 0.10 0.02 0.08
[5]   1.00 0.10 0.02 0.09
[6]   1.00 0.10 0.01 0.09
[7]   1.00 0.10 0.01 0.09
[8]   1.00 0.10 0.01 0.10
[9]   1.00 0.10 0.00 0.10

To work out the echo (h) we use a simple adaption algorithm. First we use our current guess of the echo (h) to calculate an estimate of the echo (ec). Of course if we don’t have an accurate estimate of h we won’t predict the echo exactly, we will have an error. To converge on the correct value of h we add a little bit of that error onto h and try again. As we get closer and closer the error gets smaller and eventually we converge on the correct answer (h = 0.1).

In practice the echo is a little more complex than a simple constant multiplier. It is usually modelled as a bunch of constants delayed by one sample from each other, for example:

echo_estimate = 0;
for(i=0; i<N; i++)
    echo_estimate  += h[i] * tx[j-i];
ec[j] = rx[j] - echo_estimate;

The number of samples in the echo model (N in this case) specifies the maximum delay or “tail” the echo canceller can handle. So when an echo canceller is specified as say a 128 ms tail, this means 128 ms * 8 samples/ms = 1024 samples in the echo model. The 8 samples/ms comes from the fact that telephone signals are sampled at 8000 Hz.

The array of h values are sometimes called the echo coefficients or taps. Here is what a typical array of h values looks like when plotted:

This has a rather short tail of only 64 samples (8ms), which would be typical of a FXS port where the phone is connected to the port by a few metres of cable.

In real world echo cancellation we also need to deal with problems like freezing the adaption when both people are talking at once (double talk). In this case the ec signal is made up of the echo plus the “near end” talker’s speech rather than just the echo by itself.

Reading Further

The Open Source Line Echo Canceller (Oslec) has progressed a great deal since this post was written:

Oslec Home Page
Part 1 – Introduction
Part 2 – How Echo Cancellers Work
Part 3 – Two Prototypes
Part 4 – First Calls
Part 5 – Ready for Beta Testing

7 comments to Open Source Echo Canceller Part 2 – How Echo Cancellers Work

  • Purna Chandar

    I get a feeling that the trick is to adaptively find the value that is fed to Hybrid, rather than the value h. Of course, it seems these two will be same finally, but the article highlights the calculation of h, rather than highlighting the constant fed to hybrid. Wut say??

  • Alan

    It would be great if you could elaborate on how we can arrive on the complex h[i] model. At first glance it doesn’t look very intuitive as compared to the simple model in which you just subtract a factor of rx[j]

    for(i=0; i

  • david

    Hi Alan,

    Sorry yr comment post looks cut off. This is a good introductory papers on echo can:

    Also try googling on LMS algorithm for a derivation.



  • Need4Speed

    Hi David,

    Looks like the plus characters from your C code examples went missing after the conversion to HTML.


  • david

    Thanks for point that out. Fixed now I think.

  • Dave C

    I think there’s still one ‘+’ sign missing.

    The line:
    h = BETA*ec;

    I think should read:
    h += BETA*ec;

    Thanks for a great tutorial and great information!