18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SpanDSP - a series of DSP components for telephony 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * echo.c - A line echo canceller. This code is being developed 68c2ecf20Sopenharmony_ci * against and partially complies with G168. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Written by Steve Underwood <steveu@coppice.org> 98c2ecf20Sopenharmony_ci * and David Rowe <david_at_rowetel_dot_com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2001 Steve Underwood and 2007 David Rowe 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * All rights reserved. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#ifndef __ECHO_H 178c2ecf20Sopenharmony_ci#define __ECHO_H 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ciLine echo cancellation for voice 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciWhat does it do? 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciThis module aims to provide G.168-2002 compliant echo cancellation, to remove 258c2ecf20Sopenharmony_cielectrical echoes (e.g. from 2-4 wire hybrids) from voice calls. 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciHow does it work? 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciThe heart of the echo cancellor is FIR filter. This is adapted to match the 308c2ecf20Sopenharmony_ciecho impulse response of the telephone line. It must be long enough to 318c2ecf20Sopenharmony_ciadequately cover the duration of that impulse response. The signal transmitted 328c2ecf20Sopenharmony_cito the telephone line is passed through the FIR filter. Once the FIR is 338c2ecf20Sopenharmony_ciproperly adapted, the resulting output is an estimate of the echo signal 348c2ecf20Sopenharmony_cireceived from the line. This is subtracted from the received signal. The result 358c2ecf20Sopenharmony_ciis an estimate of the signal which originated at the far end of the line, free 368c2ecf20Sopenharmony_cifrom echos of our own transmitted signal. 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciThe least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and 398c2ecf20Sopenharmony_ciwas introduced in 1960. It is the commonest form of filter adaption used in 408c2ecf20Sopenharmony_cithings like modem line equalisers and line echo cancellers. There it works very 418c2ecf20Sopenharmony_ciwell. However, it only works well for signals of constant amplitude. It works 428c2ecf20Sopenharmony_civery poorly for things like speech echo cancellation, where the signal level 438c2ecf20Sopenharmony_civaries widely. This is quite easy to fix. If the signal level is normalised - 448c2ecf20Sopenharmony_cisimilar to applying AGC - LMS can work as well for a signal of varying 458c2ecf20Sopenharmony_ciamplitude as it does for a modem signal. This normalised least mean squares 468c2ecf20Sopenharmony_ci(NLMS) algorithm is the commonest one used for speech echo cancellation. Many 478c2ecf20Sopenharmony_ciother algorithms exist - e.g. RLS (essentially the same as Kalman filtering), 488c2ecf20Sopenharmony_ciFAP, etc. Some perform significantly better than NLMS. However, factors such 498c2ecf20Sopenharmony_cias computational complexity and patents favour the use of NLMS. 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciA simple refinement to NLMS can improve its performance with speech. NLMS tends 528c2ecf20Sopenharmony_cito adapt best to the strongest parts of a signal. If the signal is white noise, 538c2ecf20Sopenharmony_cithe NLMS algorithm works very well. However, speech has more low frequency than 548c2ecf20Sopenharmony_cihigh frequency content. Pre-whitening (i.e. filtering the signal to flatten its 558c2ecf20Sopenharmony_cispectrum) the echo signal improves the adapt rate for speech, and ensures the 568c2ecf20Sopenharmony_cifinal residual signal is not heavily biased towards high frequencies. A very 578c2ecf20Sopenharmony_cilow complexity filter is adequate for this, so pre-whitening adds little to the 588c2ecf20Sopenharmony_cicompute requirements of the echo canceller. 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciAn FIR filter adapted using pre-whitened NLMS performs well, provided certain 618c2ecf20Sopenharmony_ciconditions are met: 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci - The transmitted signal has poor self-correlation. 648c2ecf20Sopenharmony_ci - There is no signal being generated within the environment being 658c2ecf20Sopenharmony_ci cancelled. 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciThe difficulty is that neither of these can be guaranteed. 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciIf the adaption is performed while transmitting noise (or something fairly 708c2ecf20Sopenharmony_cinoise like, such as voice) the adaption works very well. If the adaption is 718c2ecf20Sopenharmony_ciperformed while transmitting something highly correlative (typically narrow 728c2ecf20Sopenharmony_ciband energy such as signalling tones or DTMF), the adaption can go seriously 738c2ecf20Sopenharmony_ciwrong. The reason is there is only one solution for the adaption on a near 748c2ecf20Sopenharmony_cirandom signal - the impulse response of the line. For a repetitive signal, 758c2ecf20Sopenharmony_cithere are any number of solutions which converge the adaption, and nothing 768c2ecf20Sopenharmony_ciguides the adaption to choose the generalised one. Allowing an untrained 778c2ecf20Sopenharmony_cicanceller to converge on this kind of narrowband energy probably a good thing, 788c2ecf20Sopenharmony_cisince at least it cancels the tones. Allowing a well converged canceller to 798c2ecf20Sopenharmony_cicontinue converging on such energy is just a way to ruin its generalised 808c2ecf20Sopenharmony_ciadaption. A narrowband detector is needed, so adapation can be suspended at 818c2ecf20Sopenharmony_ciappropriate times. 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciThe adaption process is based on trying to eliminate the received signal. When 848c2ecf20Sopenharmony_cithere is any signal from within the environment being cancelled it may upset 858c2ecf20Sopenharmony_cithe adaption process. Similarly, if the signal we are transmitting is small, 868c2ecf20Sopenharmony_cinoise may dominate and disturb the adaption process. If we can ensure that the 878c2ecf20Sopenharmony_ciadaption is only performed when we are transmitting a significant signal level, 888c2ecf20Sopenharmony_ciand the environment is not, things will be OK. Clearly, it is easy to tell when 898c2ecf20Sopenharmony_ciwe are sending a significant signal. Telling, if the environment is generating 908c2ecf20Sopenharmony_cia significant signal, and doing it with sufficient speed that the adaption will 918c2ecf20Sopenharmony_cinot have diverged too much more we stop it, is a little harder. 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciThe key problem in detecting when the environment is sourcing significant 948c2ecf20Sopenharmony_cienergy is that we must do this very quickly. Given a reasonably long sample of 958c2ecf20Sopenharmony_cithe received signal, there are a number of strategies which may be used to 968c2ecf20Sopenharmony_ciassess whether that signal contains a strong far end component. However, by the 978c2ecf20Sopenharmony_citime that assessment is complete the far end signal will have already caused 988c2ecf20Sopenharmony_cimajor mis-convergence in the adaption process. An assessment algorithm is 998c2ecf20Sopenharmony_cineeded which produces a fairly accurate result from a very short burst of far 1008c2ecf20Sopenharmony_ciend energy. 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciHow do I use it? 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciThe echo cancellor processes both the transmit and receive streams sample by 1058c2ecf20Sopenharmony_cisample. The processing function is not declared inline. Unfortunately, 1068c2ecf20Sopenharmony_cicancellation requires many operations per sample, so the call overhead is only 1078c2ecf20Sopenharmony_cia minor burden. 1088c2ecf20Sopenharmony_ci*/ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#include "fir.h" 1118c2ecf20Sopenharmony_ci#include "oslec.h" 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci G.168 echo canceller descriptor. This defines the working state for a line 1158c2ecf20Sopenharmony_ci echo canceller. 1168c2ecf20Sopenharmony_ci*/ 1178c2ecf20Sopenharmony_cistruct oslec_state { 1188c2ecf20Sopenharmony_ci int16_t tx; 1198c2ecf20Sopenharmony_ci int16_t rx; 1208c2ecf20Sopenharmony_ci int16_t clean; 1218c2ecf20Sopenharmony_ci int16_t clean_nlp; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci int nonupdate_dwell; 1248c2ecf20Sopenharmony_ci int curr_pos; 1258c2ecf20Sopenharmony_ci int taps; 1268c2ecf20Sopenharmony_ci int log2taps; 1278c2ecf20Sopenharmony_ci int adaption_mode; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci int cond_met; 1308c2ecf20Sopenharmony_ci int32_t pstates; 1318c2ecf20Sopenharmony_ci int16_t adapt; 1328c2ecf20Sopenharmony_ci int32_t factor; 1338c2ecf20Sopenharmony_ci int16_t shift; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Average levels and averaging filter states */ 1368c2ecf20Sopenharmony_ci int ltxacc; 1378c2ecf20Sopenharmony_ci int lrxacc; 1388c2ecf20Sopenharmony_ci int lcleanacc; 1398c2ecf20Sopenharmony_ci int lclean_bgacc; 1408c2ecf20Sopenharmony_ci int ltx; 1418c2ecf20Sopenharmony_ci int lrx; 1428c2ecf20Sopenharmony_ci int lclean; 1438c2ecf20Sopenharmony_ci int lclean_bg; 1448c2ecf20Sopenharmony_ci int lbgn; 1458c2ecf20Sopenharmony_ci int lbgn_acc; 1468c2ecf20Sopenharmony_ci int lbgn_upper; 1478c2ecf20Sopenharmony_ci int lbgn_upper_acc; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* foreground and background filter states */ 1508c2ecf20Sopenharmony_ci struct fir16_state_t fir_state; 1518c2ecf20Sopenharmony_ci struct fir16_state_t fir_state_bg; 1528c2ecf20Sopenharmony_ci int16_t *fir_taps16[2]; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* DC blocking filter states */ 1558c2ecf20Sopenharmony_ci int tx_1; 1568c2ecf20Sopenharmony_ci int tx_2; 1578c2ecf20Sopenharmony_ci int rx_1; 1588c2ecf20Sopenharmony_ci int rx_2; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* optional High Pass Filter states */ 1618c2ecf20Sopenharmony_ci int32_t xvtx[5]; 1628c2ecf20Sopenharmony_ci int32_t yvtx[5]; 1638c2ecf20Sopenharmony_ci int32_t xvrx[5]; 1648c2ecf20Sopenharmony_ci int32_t yvrx[5]; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Parameters for the optional Hoth noise generator */ 1678c2ecf20Sopenharmony_ci int cng_level; 1688c2ecf20Sopenharmony_ci int cng_rndnum; 1698c2ecf20Sopenharmony_ci int cng_filter; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* snapshot sample of coeffs used for development */ 1728c2ecf20Sopenharmony_ci int16_t *snapshot; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#endif /* __ECHO_H */ 176