mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
6.5 KiB
175 lines
6.5 KiB
/* SPDX-License-Identifier: GPL-2.0-only */ |
|
/* |
|
* 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 <[email protected]> |
|
* and David Rowe <david_at_rowetel_dot_com> |
|
* |
|
* Copyright (C) 2001 Steve Underwood and 2007 David Rowe |
|
* |
|
* All rights reserved. |
|
*/ |
|
|
|
#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; |
|
int16_t 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; |
|
int lrxacc; |
|
int lcleanacc; |
|
int lclean_bgacc; |
|
int ltx; |
|
int lrx; |
|
int lclean; |
|
int lclean_bg; |
|
int lbgn; |
|
int lbgn_acc; |
|
int lbgn_upper; |
|
int 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; |
|
int tx_2; |
|
int rx_1; |
|
int rx_2; |
|
|
|
/* optional High Pass Filter states */ |
|
int32_t xvtx[5]; |
|
int32_t yvtx[5]; |
|
int32_t xvrx[5]; |
|
int32_t 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 */
|
|
|