Professional Documents
Culture Documents
Echocancellation: 1 Adaptive Echo Cancellation
Echocancellation: 1 Adaptive Echo Cancellation
1
x = np.zeros(int(x))
x[0] = 1
y = np.zeros(len(x))
for n in range(0, len(x)):
if n >= M:
y[n] = x[n] - lmb * x[n-1] + lmb * y[n-1] + alpha * (1 - lmb) * y[n - M]
elif n > 0:
y[n] = x[n] - lmb * x[n-1] + lmb * y[n-1]
else:
y[n] = x[n]
return y
Let’s look at the impulse response of the echo "system" for M small
In [157]: plt.plot(echo(1000, 100));
We will compare the convergence properties of the LMS algorithm both for noise-like signals
and for voice signals. Let’s load a brief speech sample that we will use in the rest of the notebook.
In [158]: Fs, s = wavfile.read('speech2.wav')
s = s / 32767.0 # scale the signal to floats in [-1, 1]
print('sampling rate:', Fs, 'Hz, data length:', len(s), 'samples')
IPython.display.Audio(s, rate=Fs)
sampling rate: 8000 Hz, data length: 19063 samples
2
1.2 The LMS filter
Let’s now implement the LMS filter for echo cancellation. Given the original signal x [n] and its
echo-corrupted version d[n] = h[n] ∗ x [n], the LMS algorithm will estimate h[n] iteratively as
and where [ ]
x n = x [ n ] x [ n − 1] x [ n − 2] . . . x [ n − N + 1]
We will first test the LMS filter using unit-variance white Gaussian noise as the input signal.
With this maximally decorrelated input the convergence is faster. First, let’s verify that the filter
converges to a good approximation of the echo’s impulse response:
# LMS parameters
taps = 500
step_size = 0.0008
# this function generates runs the LMS adaptation on a signal of length L and returns
def test_lms(L):
# the input signal
ns = np.random.randn(L)
return lms(ns, echo(ns, delay), taps, step_size)[0]
3
h = echo(taps, delay)
# precision increases with length of the adaptation
plt.plot(h, 'g'); # original impulse response (green)
plt.plot(test_lms(1000), 'r');
plt.plot(test_lms(5000), 'y');
best = test_lms(10000)
plt.plot(best, 'b');
The approximation obtained with the highest number of iterations is actually quite good, as
we can see by plotting the difference between the original and the estimated impulse response:
Clearly the precision depends on the number of steps in the adaptation. You can try and play
with the value of the step size, for instance, and see how it affects the convergence.
To have a quantitative descrition of the convergence process we can plot the MSE, averaged
over a number of independent experiments.
4
if n == 0:
mse = err
else:
mse = mse + err
mse = mse / TRIALS
plt.plot(mse);
As you can see, with these parameters the error stops decreasing after about 4000 iterations.
# now let's estimate the first 1000 taps of the echo impulse response using the speech
taps = 1500
step_size = 0.021
h, err = lms(audio, echo(audio, delay), taps, step_size)
If we plot the difference between the ideal and estimated impulse response we can see that the
match is not perfect. This is because the speech signal, as opposed to white noise, does not drive
the adaptation as effectively since it doesn’t "hit" all of the frequencies. You can try and use more
copies of the audio in sequence to improve the adaptation; in a normal use case the LMS filter
would be running all the time, using much more data to converge.
5
In [254]: plt.plot(echo(len(h), delay) - h);
We can now listen to the effectiveness of the echo canceler; listen in sequence to the reverber-
ated sound, the cancellation performed by simple subtraction and the cancellation after filtering
with the coefficients produced by the LMS adaptation. The results should speak for themselves!
6
In [ ]:
In [ ]: