摘自:Programming with Speex (the libspeex API)
The Speex library now includes an echocancellation algorithm suitable for Acoustic Echo Cancellation (AEC). In orderto use the echo canceller, you first need to
#include
Then, an echo canceller state can becreated by:
SpeexEchoState *echo_state =speex_echo_state_init(frame_size, filter_length);
where frame_size is the amount of data (in samples) you want to process at once and filter_length is the length (in samples) of the echo cancelling filter you want to use (also known as tail length). It is recommended to use a frame size in the order of 20 ms (or equal to the codec frame size) and make sure it is easy to perform an FFT of that size (powers of two are better than prime sizes). The recommended tail length is approximately the third of the room reverberation time. For example, in a small room, reverberation time is in the order of 300ms, so a tail length of 100ms is a good choice (800 samples at 8000 Hz sampling rate).
Once the echo canceller state is created,audio can be processed by:
speex_echo_cancellation(echo_state,input_frame, echo_frame, output_frame);
where input_frame is the audio as captured by the microphone,;echo_frame is the signal that was played in the speaker (and needs to be removed) ;output_frame is the signal with echo removed.
One important thing to keep in mind is the relationship between input_frame and echo_frame. It is important that, at anytime, any echo that is present in the input has already been sent to the echo canceller as echo_frame. In other words, the echo canceller cannot remove a signal that it hasn't yet received. On the other hand, the delay between the input signal and the echo signal must be small enough because otherwise part of the echo cancellation filter is inefficient. In the ideal case, you code would look like:
write_to_soundcard(echo_frame,frame_size);
read_from_soundcard(input_frame,frame_size);
speex_echo_cancellation(echo_state,input_frame, echo_frame, output_frame);
If you wish to further reduce the echo present in the signal, you can do so by associating the echo canceller to the preprocessor (see Section 5.3). This is done by calling:
speex_preprocess_ctl(preprocess_state,SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);
in the initialisation.
As of version 1.2-beta2, there is an alternative, simpler API that can be used instead of speex_echo_cancellation().When audio capture and playback are handled asynchronously (e.g. in different threads or using the poll() or select() system call), it can be difficult to keep track of what input_frame comes with what echo_frame. Instead, the playback comtext/thread can simply call:
speex_echo_playback(echo_state,echo_frame);
every time an audio frame is played. Then,the capture context/thread calls:
speex_echo_capture(echo_state,input_frame, output_frame);
for every frame captured. Internally,speex_echo_playback() simply buffers the playback frame so it can be used by speex_echo_capture() to call speex_echo_cancel(). A side effect of using this alternate API is that the playback audio is delayed by two frames, which is the normal delay caused by the soundcard. When capture and playback are already synchronised, speex_echo_cancellation() is preferable since it gives better control on the exact input/echo timing.
The echo cancellation state can bedestroyed with:
speex_echo_state_destroy(echo_state);
It is also possible to reset the state of the echo canceller so it can be reused without the need to create another state with:
Troubleshooting
There are several things that may preventthe echo canceller from working properly. One of them is a bug (or somethingsuboptimal) in the code, but there are many others you should consider first
Also useful is reading Echo CancellationDemystified by Alexey Frunze[*], which explains the fundamental principles ofecho cancellation. The details of the algorithm described in the article aredifferent, but the general ideas of echo cancellation through adaptive filtersare the same.
As of version 1.2beta2, a new echo_diagnostic.m tool is included in the source distribution. The first stepis to define DUMP_ECHO_CANCEL_DATA during the build. This causes the echocanceller to automatically save the near-end, far-end and output signals tofiles (aec_rec.sw aec_play.sw and aec_out.sw). These are exactly what the AECreceives and outputs. From there, it is necessary to start Octave and type:
echo_diagnostic('aec_rec.sw','aec_play.sw', 'aec_diagnostic.sw', 1024);
The value of 1024 is the filter length andcan be changed. There will be some (hopefully) useful messages printed and echocancelled audio will be saved to aec_diagnostic.sw . If even that output is bad(almost no cancellation) then there is probably problem with the playback orrecording process.