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