David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 1 | /* |
| 2 | * SpanDSP - a series of DSP components for telephony |
| 3 | * |
| 4 | * echo.c - A line echo canceller. This code is being developed |
| 5 | * against and partially complies with G168. |
| 6 | * |
| 7 | * Written by Steve Underwood <steveu@coppice.org> |
| 8 | * and David Rowe <david_at_rowetel_dot_com> |
| 9 | * |
| 10 | * Copyright (C) 2001 Steve Underwood and 2007 David Rowe |
| 11 | * |
| 12 | * All rights reserved. |
| 13 | * |
| 14 | * This program is free software; you can redistribute it and/or modify |
| 15 | * it under the terms of the GNU General Public License version 2, as |
| 16 | * published by the Free Software Foundation. |
| 17 | * |
| 18 | * This program is distributed in the hope that it will be useful, |
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | * GNU General Public License for more details. |
| 22 | * |
| 23 | * You should have received a copy of the GNU General Public License |
| 24 | * along with this program; if not, write to the Free Software |
| 25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 26 | */ |
| 27 | |
| 28 | #ifndef __ECHO_H |
| 29 | #define __ECHO_H |
| 30 | |
Greg Kroah-Hartman | 56791f0 | 2009-08-25 22:07:56 -0700 | [diff] [blame] | 31 | /* |
| 32 | Line echo cancellation for voice |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 33 | |
Greg Kroah-Hartman | 56791f0 | 2009-08-25 22:07:56 -0700 | [diff] [blame] | 34 | What does it do? |
| 35 | |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 36 | This module aims to provide G.168-2002 compliant echo cancellation, to remove |
| 37 | electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. |
| 38 | |
Greg Kroah-Hartman | 56791f0 | 2009-08-25 22:07:56 -0700 | [diff] [blame] | 39 | How does it work? |
| 40 | |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 41 | The heart of the echo cancellor is FIR filter. This is adapted to match the |
| 42 | echo impulse response of the telephone line. It must be long enough to |
| 43 | adequately cover the duration of that impulse response. The signal transmitted |
| 44 | to the telephone line is passed through the FIR filter. Once the FIR is |
| 45 | properly adapted, the resulting output is an estimate of the echo signal |
| 46 | received from the line. This is subtracted from the received signal. The result |
| 47 | is an estimate of the signal which originated at the far end of the line, free |
| 48 | from echos of our own transmitted signal. |
| 49 | |
| 50 | The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and |
| 51 | was introduced in 1960. It is the commonest form of filter adaption used in |
| 52 | things like modem line equalisers and line echo cancellers. There it works very |
| 53 | well. However, it only works well for signals of constant amplitude. It works |
| 54 | very poorly for things like speech echo cancellation, where the signal level |
| 55 | varies widely. This is quite easy to fix. If the signal level is normalised - |
| 56 | similar to applying AGC - LMS can work as well for a signal of varying |
| 57 | amplitude as it does for a modem signal. This normalised least mean squares |
| 58 | (NLMS) algorithm is the commonest one used for speech echo cancellation. Many |
| 59 | other algorithms exist - e.g. RLS (essentially the same as Kalman filtering), |
| 60 | FAP, etc. Some perform significantly better than NLMS. However, factors such |
| 61 | as computational complexity and patents favour the use of NLMS. |
| 62 | |
| 63 | A simple refinement to NLMS can improve its performance with speech. NLMS tends |
| 64 | to adapt best to the strongest parts of a signal. If the signal is white noise, |
| 65 | the NLMS algorithm works very well. However, speech has more low frequency than |
| 66 | high frequency content. Pre-whitening (i.e. filtering the signal to flatten its |
| 67 | spectrum) the echo signal improves the adapt rate for speech, and ensures the |
| 68 | final residual signal is not heavily biased towards high frequencies. A very |
| 69 | low complexity filter is adequate for this, so pre-whitening adds little to the |
| 70 | compute requirements of the echo canceller. |
| 71 | |
| 72 | An FIR filter adapted using pre-whitened NLMS performs well, provided certain |
| 73 | conditions are met: |
| 74 | |
| 75 | - The transmitted signal has poor self-correlation. |
| 76 | - There is no signal being generated within the environment being |
| 77 | cancelled. |
| 78 | |
| 79 | The difficulty is that neither of these can be guaranteed. |
| 80 | |
| 81 | If the adaption is performed while transmitting noise (or something fairly |
| 82 | noise like, such as voice) the adaption works very well. If the adaption is |
| 83 | performed while transmitting something highly correlative (typically narrow |
| 84 | band energy such as signalling tones or DTMF), the adaption can go seriously |
| 85 | wrong. The reason is there is only one solution for the adaption on a near |
| 86 | random signal - the impulse response of the line. For a repetitive signal, |
| 87 | there are any number of solutions which converge the adaption, and nothing |
| 88 | guides the adaption to choose the generalised one. Allowing an untrained |
| 89 | canceller to converge on this kind of narrowband energy probably a good thing, |
| 90 | since at least it cancels the tones. Allowing a well converged canceller to |
| 91 | continue converging on such energy is just a way to ruin its generalised |
| 92 | adaption. A narrowband detector is needed, so adapation can be suspended at |
| 93 | appropriate times. |
| 94 | |
| 95 | The adaption process is based on trying to eliminate the received signal. When |
| 96 | there is any signal from within the environment being cancelled it may upset |
| 97 | the adaption process. Similarly, if the signal we are transmitting is small, |
| 98 | noise may dominate and disturb the adaption process. If we can ensure that the |
| 99 | adaption is only performed when we are transmitting a significant signal level, |
| 100 | and the environment is not, things will be OK. Clearly, it is easy to tell when |
| 101 | we are sending a significant signal. Telling, if the environment is generating |
| 102 | a significant signal, and doing it with sufficient speed that the adaption will |
| 103 | not have diverged too much more we stop it, is a little harder. |
| 104 | |
| 105 | The key problem in detecting when the environment is sourcing significant |
| 106 | energy is that we must do this very quickly. Given a reasonably long sample of |
| 107 | the received signal, there are a number of strategies which may be used to |
| 108 | assess whether that signal contains a strong far end component. However, by the |
| 109 | time that assessment is complete the far end signal will have already caused |
| 110 | major mis-convergence in the adaption process. An assessment algorithm is |
| 111 | needed which produces a fairly accurate result from a very short burst of far |
| 112 | end energy. |
| 113 | |
Greg Kroah-Hartman | 56791f0 | 2009-08-25 22:07:56 -0700 | [diff] [blame] | 114 | How do I use it? |
| 115 | |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 116 | The echo cancellor processes both the transmit and receive streams sample by |
| 117 | sample. The processing function is not declared inline. Unfortunately, |
| 118 | cancellation requires many operations per sample, so the call overhead is only |
| 119 | a minor burden. |
| 120 | */ |
| 121 | |
| 122 | #include "fir.h" |
Tzafrir Cohen | 17f8c11 | 2008-10-12 06:03:14 +0200 | [diff] [blame] | 123 | #include "oslec.h" |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 124 | |
Greg Kroah-Hartman | 56791f0 | 2009-08-25 22:07:56 -0700 | [diff] [blame] | 125 | /* |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 126 | G.168 echo canceller descriptor. This defines the working state for a line |
| 127 | echo canceller. |
| 128 | */ |
J.R. Mauro | 4460a86 | 2008-10-20 19:01:31 -0400 | [diff] [blame] | 129 | struct oslec_state { |
Jesper Juhl | 3ec50be | 2012-06-27 22:28:55 +0200 | [diff] [blame] | 130 | int16_t tx; |
| 131 | int16_t rx; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 132 | int16_t clean; |
| 133 | int16_t clean_nlp; |
| 134 | |
| 135 | int nonupdate_dwell; |
| 136 | int curr_pos; |
| 137 | int taps; |
| 138 | int log2taps; |
| 139 | int adaption_mode; |
| 140 | |
| 141 | int cond_met; |
Lisa Nguyen | 0c47482 | 2013-05-05 23:38:24 -0700 | [diff] [blame] | 142 | int32_t pstates; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 143 | int16_t adapt; |
| 144 | int32_t factor; |
| 145 | int16_t shift; |
| 146 | |
| 147 | /* Average levels and averaging filter states */ |
Lisa Nguyen | 0c47482 | 2013-05-05 23:38:24 -0700 | [diff] [blame] | 148 | int ltxacc; |
| 149 | int lrxacc; |
| 150 | int lcleanacc; |
| 151 | int lclean_bgacc; |
| 152 | int ltx; |
| 153 | int lrx; |
| 154 | int lclean; |
| 155 | int lclean_bg; |
| 156 | int lbgn; |
| 157 | int lbgn_acc; |
| 158 | int lbgn_upper; |
| 159 | int lbgn_upper_acc; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 160 | |
| 161 | /* foreground and background filter states */ |
J.R. Mauro | c82895b | 2008-10-30 19:26:59 -0400 | [diff] [blame] | 162 | struct fir16_state_t fir_state; |
| 163 | struct fir16_state_t fir_state_bg; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 164 | int16_t *fir_taps16[2]; |
| 165 | |
| 166 | /* DC blocking filter states */ |
Jesper Juhl | 3ec50be | 2012-06-27 22:28:55 +0200 | [diff] [blame] | 167 | int tx_1; |
| 168 | int tx_2; |
| 169 | int rx_1; |
| 170 | int rx_2; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 171 | |
| 172 | /* optional High Pass Filter states */ |
Jesper Juhl | 3ec50be | 2012-06-27 22:28:55 +0200 | [diff] [blame] | 173 | int32_t xvtx[5]; |
| 174 | int32_t yvtx[5]; |
| 175 | int32_t xvrx[5]; |
| 176 | int32_t yvrx[5]; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 177 | |
| 178 | /* Parameters for the optional Hoth noise generator */ |
| 179 | int cng_level; |
| 180 | int cng_rndnum; |
| 181 | int cng_filter; |
| 182 | |
| 183 | /* snapshot sample of coeffs used for development */ |
| 184 | int16_t *snapshot; |
Tzafrir Cohen | 17f8c11 | 2008-10-12 06:03:14 +0200 | [diff] [blame] | 185 | }; |
David Rowe | 10602db | 2008-10-06 21:41:46 -0700 | [diff] [blame] | 186 | |
J.R. Mauro | 4460a86 | 2008-10-20 19:01:31 -0400 | [diff] [blame] | 187 | #endif /* __ECHO_H */ |