speex中的AEC框架介绍(in C)

摘自:Programming with Speex (the libspeex API)


Echo Cancellation

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 <speex/speex_echo.h>

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:



     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:


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:


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:




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

  • Using a different soundcard to do thecapture and plaback will *not* work, regardless of what you may think. The onlyexception to that is if the two cards can be made to have their sampling clock``locked'' on the same clock source.
  • The delay between the record and playbacksignals must be minimal. Any signal played has to ``appear'' on the playback(far end) signal slightly before the echo canceller ``sees'' it in the near endsignal, but excessive delay means that part of the filter length is wasted. Inthe worst situations, the delay is such that it is longer than the filterlength, in which case, no echo can be cancelled.
  • When it comes to echo tail length (filterlength), longer is *not* better. Actually, the longer the tail length, thelonger it takes for the filter to adapt. Of course, a tail length that is tooshort will not cancel enough echo, but the most common problem seen is thatpeople set a very long tail length and then wonder why no echo is beingcancelled.
  • Non-linear distortion cannot (bydefinition) be modeled by the linear adaptive filter used in the echo cancellerand thus cannot be cancelled. Use good audio gear and avoidsaturation/clipping.

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.




#ifdef HAVE_CONFIG_Hrn#include "config.h"rn#endifrnrn#include rn#include rn#include rn#include rnrn#ifdef FIXED_DEBUGrnextern long long spx_mips;rn#endifrnrn#define FRAME_SIZE 160rn#include rnint main(int argc, char **argv)rnrn char *inFile, *outFile, *bitsFile;rn FILE *fin, *fout, *fbits=NULL;rn short in_short[FRAME_SIZE];rn short out_short[FRAME_SIZE];rn int snr_frames = 0;rn char cbits[200];rn int nbBits;rn int i;rn void *st;rn void *dec;rn SpeexBits bits;rn spx_int32_t tmp;rn int bitCount=0;rn spx_int32_t skip_group_delay;rn SpeexCallback callback;rnrn st = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));rn dec = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));rnrn /* BEGIN: You probably don't need the following in a real application */rn callback.callback_id = SPEEX_INBAND_CHAR;rn callback.func = speex_std_char_handler;rn callback.data = stderr;rn speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback);rnrn callback.callback_id = SPEEX_INBAND_MODE_REQUEST;rn callback.func = speex_std_mode_request_handler;rn callback.data = st;rn speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback);rn /* END of unnecessary stuff */rnrn tmp=1;rn speex_decoder_ctl(dec, SPEEX_SET_ENH, &tmp);rn tmp=0;rn speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);rn tmp=8;rn speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp);rn tmp=1;rn speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp);rnrn /* Turn this off if you want to measure SNR (on by default) */rn tmp=1;rn speex_encoder_ctl(st, SPEEX_SET_HIGHPASS, &tmp);rn speex_decoder_ctl(dec, SPEEX_SET_HIGHPASS, &tmp);rnrn speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &skip_group_delay);rn speex_decoder_ctl(dec, SPEEX_GET_LOOKAHEAD, &tmp);rn skip_group_delay += tmp;rnrn if (argc != 4 && argc != 3)rn rn fprintf (stderr, "Usage: encode [in file] [out file] [bits file]\nargc = %d", argc);rn exit(1);rn rn inFile = argv[1];rn fin = fopen(inFile, "rb");rn outFile = argv[2];rn fout = fopen(outFile, "wb+");rn if (argc==4)rn rn bitsFile = argv[3];rn fbits = fopen(bitsFile, "wb");rn rn speex_bits_init(&bits);rn while (!feof(fin))rn rn fread(in_short, sizeof(short), FRAME_SIZE, fin);rn if (feof(fin))rn break;rn speex_bits_reset(&bits);rnrn speex_encode_int(st, in_short, &bits);rn nbBits = speex_bits_write(&bits, cbits, 200);rn bitCount+=bits.nbBits;rnrn if (argc==4)rn fwrite(cbits, 1, nbBits, fbits);rn speex_bits_rewind(&bits);rnrn speex_decode_int(dec, &bits, out_short);rn speex_bits_reset(&bits);rnrn fwrite(&out_short[skip_group_delay], sizeof(short), FRAME_SIZE-skip_group_delay, fout);rn skip_group_delay = 0;rn rn fprintf (stderr, "Total encoded size: %d bits\n", bitCount);rn speex_encoder_destroy(st);rn speex_decoder_destroy(dec);rn speex_bits_destroy(&bits);rnrn#ifndef DISABLE_FLOAT_APIrn rn float sigpow,errpow,snr, seg_snr=0;rn sigpow = 0;rn errpow = 0;rnrn /* This code just computes SNR, so you don't need it either */rn rewind(fin);rn rewind(fout);rnrn while ( FRAME_SIZE == fread(in_short, sizeof(short), FRAME_SIZE, fin) rn &&rn FRAME_SIZE == fread(out_short, sizeof(short), FRAME_SIZE,fout) )rn rn float s=0, e=0;rn for (i=0;i 论坛



有谁把speex编译成so库过的?我这边一直报错:rnfftwrap.c这个文件里面:  error :#error No other FFT implementrnrn[code=C/C++]LOCAL_PATH := $(call my-dir)rnrninclude $(CLEAR_VARS)rnLOCAL_MODULE := speex_jnirnLOCAL_SRC_FILES := speex_jni.cpp \rn /libspeex/bits.c \rn /libspeex/buffer.c \rn /libspeex/cb_search.c \rn /libspeex/exc_5_64_table.c \rn /libspeex/exc_5_256_table.c \rn /libspeex/exc_8_128_table.c \rn /libspeex/exc_10_16_table.c \rn /libspeex/exc_10_32_table.c \rn /libspeex/exc_20_32_table.c \rn /libspeex/fftwrap.c \rn /libspeex/filterbank.c \rn /libspeex/filters.c \rn /libspeex/gain_table.c \rn /libspeex/gain_table_lbr.c \rn /libspeex/hexc_10_32_table.c \rn /libspeex/hexc_table.c \rn /libspeex/high_lsp_tables.c \rn /libspeex/jitter.c \rn /libspeex/kiss_fft.c \rn /libspeex/kiss_fftr.c \rn /libspeex/lpc.c \rn /libspeex/lsp.c \rn /libspeex/lsp_tables_nb.c \rn /libspeex/ltp.c \rn /libspeex/mdf.c \rn /libspeex/modes.c \rn /libspeex/modes_wb.c \rn /libspeex/nb_celp.c \rn /libspeex/preprocess.c \rn /libspeex/quant_lsp.c \rn /libspeex/resample.c \rn /libspeex/sb_celp.c \rn /libspeex/scal.c \rn /libspeex/smallft.c \rn /libspeex/speex.c \rn /libspeex/speex_callbacks.c \rn /libspeex/speex_header.c \rn /libspeex/stereo.c \rn /libspeex/testdenoise.c \rn /libspeex/testecho.c \rn /libspeex/testenc.c \rn /libspeex/testenc_uwb.c \rn /libspeex/testenc_wb.c \rn /libspeex/testjitter.c \rn /libspeex/vbr.c \rn /libspeex/vq.c \rn /libspeex/window.crnrnLOCAL_C_INCLUDES += rnLOCAL_CFLAGS = -DFIXED_POINT -DEXPORT="" -UHAVE_CONFIG_H -I$(LOCAL_PATH)/speexrninclude $(BUILD_SHARED_LIBRARY)[/code]rn有谁遇到过没?? 论坛