| /* |
| * SpanDSP - a series of DSP components for telephony |
| * |
| * echo.c - A line echo canceller. This code is being developed |
| * against and partially complies with G168. |
| * |
| * Written by Steve Underwood <steveu@coppice.org> |
| * and David Rowe <david_at_rowetel_dot_com> |
| * |
| * Copyright (C) 2001 Steve Underwood and 2007 David Rowe |
| * |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2, as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #ifndef __ECHO_H |
| #define __ECHO_H |
| |
| /* |
| Line echo cancellation for voice |
| |
| What does it do? |
| |
| This module aims to provide G.168-2002 compliant echo cancellation, to remove |
| electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. |
| |
| |
| How does it work? |
| |
| The heart of the echo cancellor is FIR filter. This is adapted to match the |
| echo impulse response of the telephone line. It must be long enough to |
| adequately cover the duration of that impulse response. The signal transmitted |
| to the telephone line is passed through the FIR filter. Once the FIR is |
| properly adapted, the resulting output is an estimate of the echo signal |
| received from the line. This is subtracted from the received signal. The result |
| is an estimate of the signal which originated at the far end of the line, free |
| from echos of our own transmitted signal. |
| |
| The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and |
| was introduced in 1960. It is the commonest form of filter adaption used in |
| things like modem line equalisers and line echo cancellers. There it works very |
| well. However, it only works well for signals of constant amplitude. It works |
| very poorly for things like speech echo cancellation, where the signal level |
| varies widely. This is quite easy to fix. If the signal level is normalised - |
| similar to applying AGC - LMS can work as well for a signal of varying |
| amplitude as it does for a modem signal. This normalised least mean squares |
| (NLMS) algorithm is the commonest one used for speech echo cancellation. Many |
| other algorithms exist - e.g. RLS (essentially the same as Kalman filtering), |
| FAP, etc. Some perform significantly better than NLMS. However, factors such |
| as computational complexity and patents favour the use of NLMS. |
| |
| A simple refinement to NLMS can improve its performance with speech. NLMS tends |
| to adapt best to the strongest parts of a signal. If the signal is white noise, |
| the NLMS algorithm works very well. However, speech has more low frequency than |
| high frequency content. Pre-whitening (i.e. filtering the signal to flatten its |
| spectrum) the echo signal improves the adapt rate for speech, and ensures the |
| final residual signal is not heavily biased towards high frequencies. A very |
| low complexity filter is adequate for this, so pre-whitening adds little to the |
| compute requirements of the echo canceller. |
| |
| An FIR filter adapted using pre-whitened NLMS performs well, provided certain |
| conditions are met: |
| |
| - The transmitted signal has poor self-correlation. |
| - There is no signal being generated within the environment being |
| cancelled. |
| |
| The difficulty is that neither of these can be guaranteed. |
| |
| If the adaption is performed while transmitting noise (or something fairly |
| noise like, such as voice) the adaption works very well. If the adaption is |
| performed while transmitting something highly correlative (typically narrow |
| band energy such as signalling tones or DTMF), the adaption can go seriously |
| wrong. The reason is there is only one solution for the adaption on a near |
| random signal - the impulse response of the line. For a repetitive signal, |
| there are any number of solutions which converge the adaption, and nothing |
| guides the adaption to choose the generalised one. Allowing an untrained |
| canceller to converge on this kind of narrowband energy probably a good thing, |
| since at least it cancels the tones. Allowing a well converged canceller to |
| continue converging on such energy is just a way to ruin its generalised |
| adaption. A narrowband detector is needed, so adapation can be suspended at |
| appropriate times. |
| |
| The adaption process is based on trying to eliminate the received signal. When |
| there is any signal from within the environment being cancelled it may upset |
| the adaption process. Similarly, if the signal we are transmitting is small, |
| noise may dominate and disturb the adaption process. If we can ensure that the |
| adaption is only performed when we are transmitting a significant signal level, |
| and the environment is not, things will be OK. Clearly, it is easy to tell when |
| we are sending a significant signal. Telling, if the environment is generating |
| a significant signal, and doing it with sufficient speed that the adaption will |
| not have diverged too much more we stop it, is a little harder. |
| |
| The key problem in detecting when the environment is sourcing significant |
| energy is that we must do this very quickly. Given a reasonably long sample of |
| the received signal, there are a number of strategies which may be used to |
| assess whether that signal contains a strong far end component. However, by the |
| time that assessment is complete the far end signal will have already caused |
| major mis-convergence in the adaption process. An assessment algorithm is |
| needed which produces a fairly accurate result from a very short burst of far |
| end energy. |
| |
| How do I use it? |
| |
| The echo cancellor processes both the transmit and receive streams sample by |
| sample. The processing function is not declared inline. Unfortunately, |
| cancellation requires many operations per sample, so the call overhead is only |
| a minor burden. |
| */ |
| |
| #include "fir.h" |
| #include "oslec.h" |
| |
| /* |
| G.168 echo canceller descriptor. This defines the working state for a line |
| echo canceller. |
| */ |
| struct oslec_state { |
| int16_t tx, rx; |
| int16_t clean; |
| int16_t clean_nlp; |
| |
| int nonupdate_dwell; |
| int curr_pos; |
| int taps; |
| int log2taps; |
| int adaption_mode; |
| |
| int cond_met; |
| int32_t Pstates; |
| int16_t adapt; |
| int32_t factor; |
| int16_t shift; |
| |
| /* Average levels and averaging filter states */ |
| int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; |
| int Ltx, Lrx; |
| int Lclean; |
| int Lclean_bg; |
| int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; |
| |
| /* foreground and background filter states */ |
| struct fir16_state_t fir_state; |
| struct fir16_state_t fir_state_bg; |
| int16_t *fir_taps16[2]; |
| |
| /* DC blocking filter states */ |
| int tx_1, tx_2, rx_1, rx_2; |
| |
| /* optional High Pass Filter states */ |
| int32_t xvtx[5], yvtx[5]; |
| int32_t xvrx[5], yvrx[5]; |
| |
| /* Parameters for the optional Hoth noise generator */ |
| int cng_level; |
| int cng_rndnum; |
| int cng_filter; |
| |
| /* snapshot sample of coeffs used for development */ |
| int16_t *snapshot; |
| }; |
| |
| #endif /* __ECHO_H */ |