├── Sender.wav ├── output.wav ├── Listener.wav ├── README.md └── echocanceldemo.py /Sender.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varuncm/echo-cancel/HEAD/Sender.wav -------------------------------------------------------------------------------- /output.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varuncm/echo-cancel/HEAD/output.wav -------------------------------------------------------------------------------- /Listener.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varuncm/echo-cancel/HEAD/Listener.wav -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # echo-cancel 2 | Demo the principle of Echo Cancellation using simple audio samples. 3 | 4 | This is a practical demo of Echo Cancellation in audio samples using adaptive filter.  The filter used is adaptfilt from the Python library. For more information on adaptfilt, please refer to the source, https://pypi.python.org/pypi/adaptfilt/0.2 5 | 6 | An explainer on the terminologies used in the program: The person in the far-end is "the sender".  The "listener" on the near-end hears the sender and replies to him.  However, the sender may get distracted hearing his own voice reflected from the listener's room along with the listener's voice.  7 | The coefficients used to simulate the sender's voice getting reflected in the listener's room is the same as those used in the adaptfilt example in the above source. 8 | 9 | The purpose of this demo is to illustrate through audio programming, how an adaptive filter can remove the sender's own echo from what he hears. 10 | NLMS (Normalized Least Means Squares) mode of adaptfilt is used. For the given audio samples, step size of the algorithm is taken to be 0.05, and the number of coefficients of the filter to be 50.  These are trial and error values chosen to give the best fidelity. 11 | 12 | The speech content in the audio samples is as follows: 13 | Sender.wav "Hello there, I am speaking to the person I want to talk to right now." 14 | Listener.wav "This is the voice of the listener.  And he replies to the person on the other side." 15 | 16 | Processed audio is stored in the file 'output.wav', which is read and played twice by the program.  First it plays the pre-processing scenario.  Next it plays the post-processed scenario. The audio is kept slightly attenuated, so it is advisable to turn the speaker or headphone volume up to a medium level. 17 | -------------------------------------------------------------------------------- /echocanceldemo.py: -------------------------------------------------------------------------------- 1 | # This is a practical demo of Echo Cancellation in audio samples using adaptive filter. The filter used is adaptfilt from the Python library. 2 | # For more information on adaptfilt, please refer to the source, https://pypi.python.org/pypi/adaptfilt/0.2 3 | # An explainer on the terminologies used in the program: 4 | # The person in the far-end is "the sender". The "listener" on the near-end hears the sender and replies to him. 5 | # However, the sender may get distracted hearing his own voice reflected from the listener's room along with the listener's voice. 6 | # The coefficients used to simulate the sender's voice getting reflected in the listener's room is the same as those used in the adaptfilt example 7 | # in the above source. 8 | # The purpose of this demo is to illustrate through audio programming, how an adaptive filter can remove the sender's own echo from what he hears. 9 | # NLMS (Normalized Least Means Squares) mode of adaptfilt is used. For the given audio samples, step size of the algorithm is taken to be 0.05, and 10 | # the number of coefficients of the filter to be 50. These are trial and error values chosen to give the best fidelity. 11 | 12 | # The speech content in the audio samples is as follows: 13 | ## Sender.wav "Hello there, I am speaking to the person I want to talk to right now." 14 | ## Listener.wav "This is the voice of the listener. And he replies to the person on the other side." 15 | 16 | # Processed audio is stored in the file 'output.wav', which is read and played twice by the program. First it plays the pre-processing scenario. 17 | # Next it plays the post-processed scenario. 18 | # The audio is kept slightly attenuated, so it is advisable to turn the speaker or headphone volume up to a medium level. 19 | 20 | # **** START OF PROGRAM **** 21 | 22 | import numpy as np 23 | import matplotlib.pyplot as plt 24 | import adaptfilt as adf 25 | import winsound 26 | from scipy.io import wavfile 27 | 28 | waveout = 'output.wav' # Defining the output wave file 29 | step = 0.05 # Step size 30 | M = 50 # Number of filter taps in adaptive filter 31 | 32 | # Read the audio files of sender and listener 33 | sfs, u = wavfile.read('sender.wav') 34 | lfs, v = wavfile.read('listener.wav') 35 | 36 | u = np.fromstring(u, np.int16) 37 | u = np.float64(u) 38 | 39 | v = np.fromstring(v, np.int16) 40 | v = np.float64(v) 41 | 42 | # Generate the fedback signal d(n) by a) convolving the sender's voice with randomly chosen coefficients assumed to emulate the listener's room 43 | # characteristic, and b) mixing the result with listener's voice, so that the sender hears a mix of noise and echo in the reply. 44 | 45 | coeffs = np.concatenate(([0.8], np.zeros(8), [-0.7], np.zeros(9), [0.5], np.zeros(11), [-0.3], np.zeros(3),[0.1], np.zeros(20), [-0.05])) 46 | d = np.convolve(u, coeffs) 47 | d = d/20.0 48 | v = v/20.0 49 | d = d[:len(v)] # Trims sender's audio to the same length as that of the listener's in order to mix them 50 | d = d + v - (d*v)/256.0 # Mix with listener's voice. 51 | d = np.round(d,0) 52 | 53 | # Hear how the mixed signal sounds before proceeding with the filtering. 54 | dsound = d.astype('int16') 55 | wavfile.write(waveout, lfs, dsound) 56 | winsound.PlaySound(waveout, winsound.SND_ALIAS) 57 | 58 | # Apply adaptive filter 59 | y, e, w = adf.nlms(u[:len(d)], d, M, step, returnCoeffs=True) 60 | 61 | # The algorithm stores the processed result in the variable 'e', which is the mix of the error signal and the listener's voice. 62 | # Hear how e sounds now. Ideally we on behalf of the sender, should hear only the listener's voice. Practically, some echo would still be present. 63 | 64 | e = e.astype('int16') 65 | wavfile.write(waveout, lfs, e) 66 | winsound.PlaySound(waveout, winsound.SND_ALIAS) 67 | 68 | # Calculate and plot the mean square weight error 69 | mswe = adf.mswe(w, coeffs) 70 | plt.figure() 71 | plt.title('Mean squared weight error') 72 | plt.plot(mswe) 73 | plt.grid() 74 | plt.xlabel('Samples') 75 | 76 | plt.show() 77 | 78 | # **** PROGRAM END **** 79 | 80 | # Copyright (c) 2016 by Varun Chandramohan 81 | --------------------------------------------------------------------------------