From: <jpg...@us...> - 2008-04-24 18:48:57
|
Revision: 1426 http://iaxclient.svn.sourceforge.net/iaxclient/?rev=1426&view=rev Author: jpgrayson Date: 2008-04-24 11:49:01 -0700 (Thu, 24 Apr 2008) Log Message: ----------- Merge new acoustic echo cancellation (AEC) code into trunk. This code has been simmering in branches/team/mihai/echocan for some time. This new code takes advantage of the speex echo cancellation improvements introduced in speex-1.2beta2. Echo cancellation is disabled by default on Windows. Echo cancellation is not yet working well with WMME. Modified Paths: -------------- trunk/lib/audio_encode.c trunk/lib/audio_encode.h trunk/lib/audio_portaudio.c Modified: trunk/lib/audio_encode.c =================================================================== --- trunk/lib/audio_encode.c 2008-04-24 17:52:51 UTC (rev 1425) +++ trunk/lib/audio_encode.c 2008-04-24 18:49:01 UTC (rev 1426) @@ -25,6 +25,18 @@ #include "codec_speex.h" #include <speex/speex_preprocess.h> +/* Determine if we should do AEC */ +#if defined(SPEEX_EC) && !defined(WIN32) +#define DO_EC +#else +#undef DO_EC +#endif + +#ifdef DO_EC +#include <speex/speex_echo.h> +#include "ringbuffer.h" +#endif + #ifdef CODEC_ILBC #include "codec_ilbc.h" #endif @@ -41,6 +53,35 @@ static int speex_state_rate = 0; int iaxci_filters = IAXC_FILTER_AGC|IAXC_FILTER_DENOISE|IAXC_FILTER_AAGC|IAXC_FILTER_CN; +static MUTEX audio_lock; + +/* echo_tail length, in samples */ +#define ECHO_TAIL 512 + +/* Maximum attenuation of residual echo in dB (negative number) */ +#define ECHO_SUPPRESS -60 +/* Maximum attenuation of residual echo when near end is active, in dB (negative number) */ +#define ECHO_SUPPRESS_ACTIVE -60 + +/* Size of ring buffer used for echo cancellation. Must be power of 2. */ +#define EC_RING_SIZE 512 + +#ifdef DO_EC +static SpeexEchoState *ec = 0; +static rb_RingBuffer ecOutRing; +static char outRingBuf[EC_RING_SIZE]; +#endif + +/* AAGC threshold */ +#define AAGC_VERY_HOT 16 +#define AAGC_HOT 8 +#define AAGC_COLD 4 + +/* AAGC increments */ +#define AAGC_RISE_SLOW 0.10f +#define AAGC_DROP_SLOW 0.15f +#define AAGC_DROP_FAST 0.20f + /* use to measure time since last audio was processed */ static struct timeval timeLastInput ; static struct timeval timeLastOutput ; @@ -141,20 +182,34 @@ float volume; int silent = 0; + MUTEXLOCK(&audio_lock); if ( !st || speex_state_size != len || speex_state_rate != rate ) { if (st) speex_preprocess_state_destroy(st); st = speex_preprocess_state_init(len,rate); +#ifdef DO_EC + if ( ec ) + { + int i; + + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_STATE, ec); + i = ECHO_SUPPRESS; + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, &i); + i = ECHO_SUPPRESS_ACTIVE; + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, &i); + } +#endif speex_state_size = len; speex_state_rate = rate; set_speex_filters(); } + MUTEXUNLOCK(&audio_lock); calculate_level(audio, len, &input_level); /* go through the motions only if we need at least one of the preprocessor filters */ - if ( (iaxci_filters & (IAXC_FILTER_DENOISE | IAXC_FILTER_AGC | IAXC_FILTER_DEREVERB)) || + if ( (iaxci_filters & (IAXC_FILTER_DENOISE | IAXC_FILTER_AGC | IAXC_FILTER_DEREVERB | IAXC_FILTER_ECHO)) || iaxci_silence_threshold > 0.0f ) silent = !speex_preprocess(st, (spx_int16_t *)audio, NULL); @@ -410,12 +465,87 @@ set_speex_filters(); } +int audio_echo_cancellation(short *inputBuffer, short *outputBuffer, int samples) +{ +#ifdef DO_EC + int i; + short delayedBuf[1024]; + short cancelledBuffer[1024]; + + /* if ec is off, clear ec state -- this way, we start fresh if/when + * it's turned back on. */ + MUTEXLOCK(&audio_lock); + if ( !(iaxci_filters & IAXC_FILTER_ECHO) ) + { + if ( ec ) + { + speex_echo_state_destroy(ec); + ec = NULL; + if ( st ) + { + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_STATE, NULL); + } + } + + MUTEXUNLOCK(&audio_lock); + return 0; + } + + /* we want echo cancellation */ + if ( !ec ) + { + rb_InitializeRingBuffer(&ecOutRing, EC_RING_SIZE, &outRingBuf); + ec = speex_echo_state_init(SAMPLES_PER_FRAME, ECHO_TAIL); + + if ( st ) + { + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_STATE, ec); + i = ECHO_SUPPRESS; + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, &i); + i = ECHO_SUPPRESS_ACTIVE; + speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, &i); + } + } + MUTEXUNLOCK(&audio_lock); + + // Put our data in the EC ring buffer. + // Echo canceller needs SAMPLES_PER_FRAME samples, so if we don't have enough + // at this time, we just store what we have and return. + rb_WriteRingBuffer(&ecOutRing, outputBuffer, samples * 2); + if ( rb_GetRingBufferReadAvailable(&ecOutRing) < (SAMPLES_PER_FRAME * 2) ) + return -1; + + rb_ReadRingBuffer(&ecOutRing, delayedBuf, SAMPLES_PER_FRAME * 2); + + speex_echo_cancellation(ec, inputBuffer, delayedBuf, cancelledBuffer); + + memcpy(inputBuffer, cancelledBuffer, samples * sizeof(short)); +#endif + return 0; +} + int audio_initialize() { + MUTEXINIT(&audio_lock); return 0; } int audio_destroy() { + MUTEXLOCK(&audio_lock); + if ( st ) + { + speex_preprocess_state_destroy(st); + st = NULL; + } +#ifdef DO_EC + if ( ec ) + { + speex_echo_state_destroy(ec); + ec = NULL; + } +#endif + MUTEXUNLOCK(&audio_lock); + MUTEXDESTROY(&audio_lock); return 0; } Modified: trunk/lib/audio_encode.h =================================================================== --- trunk/lib/audio_encode.h 2008-04-24 17:52:51 UTC (rev 1425) +++ trunk/lib/audio_encode.h 2008-04-24 18:49:01 UTC (rev 1426) @@ -15,6 +15,14 @@ #ifndef _AUDIO_ENCODE_H #define _AUDIO_ENCODE_H +/* Some audio parameters */ +#define MAX_SAMPLE_RATE 48000 +#ifndef MS_PER_FRAME +# define MS_PER_FRAME 20 +#endif +#define SAMPLES_PER_FRAME (MS_PER_FRAME * iaxci_sample_rate / 1000) +#define MAX_SAMPLES_PER_FRAME (MS_PER_FRAME * MAX_SAMPLE_RATE / 1000) + extern int iaxci_sample_rate; /* Minimum dB possible in the iaxclient world. This level @@ -22,16 +30,6 @@ */ #define AUDIO_ENCODE_SILENCE_DB -99.0f -/* AAGC threshold */ -#define AAGC_VERY_HOT 16 -#define AAGC_HOT 8 -#define AAGC_COLD 4 - -/* AAGC increments */ -#define AAGC_RISE_SLOW 0.10f -#define AAGC_DROP_SLOW 0.15f -#define AAGC_DROP_FAST 0.20f - struct iaxc_call; struct iax_event; @@ -44,5 +42,7 @@ int audio_decode_audio(struct iaxc_call * p, void * out, void * data, int len, int iEncodeType, int * samples); +int audio_echo_cancellation(short *inputBuffer, short *outputBuffer, int samples); + #endif Modified: trunk/lib/audio_portaudio.c =================================================================== --- trunk/lib/audio_portaudio.c 2008-04-24 17:52:51 UTC (rev 1425) +++ trunk/lib/audio_portaudio.c 2008-04-24 18:49:01 UTC (rev 1426) @@ -38,27 +38,6 @@ #include "ringbuffer.h" #include "portmixer.h" -#ifdef USE_MEC2 -#define DO_EC -#include "mec3.h" -static echo_can_state_t *ec; -#endif - -#ifdef SPAN_EC -#define DO_EC -#include "ec/echo.h" -static echo_can_state_t *ec; -#endif - -#if defined(SPEEX_EC) && ! defined (WIN32) -#define DO_EC -#define restrict __restrict -#include "speex/speex_echo.h" -static SpeexEchoState *ec; -#endif - -#define EC_RING_SZ 8192 /* must be pow(2) */ - typedef short SAMPLE; static PaStream *iStream, *oStream, *aStream; @@ -68,18 +47,6 @@ static int mixers_initialized; -#define MAX_SAMPLE_RATE 48000 -#ifndef MS_PER_FRAME -# define MS_PER_FRAME 20 -#endif -#define SAMPLES_PER_FRAME (MS_PER_FRAME * iaxci_sample_rate / 1000) - -/* static frame buffer allocation */ -#define MAX_SAMPLES_PER_FRAME (MS_PER_FRAME * MAX_SAMPLE_RATE / 1000) - -/* echo_tail length, in frames must be pow(2) for mec/span ? */ -#define ECHO_TAIL 4096 - /* RingBuffer Size; Needs to be Pow(2), 1024 = 512 samples = 64ms */ #ifndef OUTRBSZ # define OUTRBSZ 32768 @@ -372,84 +339,6 @@ return retval; /* found? */ } -static void iaxc_echo_can(short *inputBuffer, short *outputBuffer, int n) -{ - static rb_RingBuffer ecOutRing; - static char outRingBuf[EC_RING_SZ]; - static long bias = 0; - short delayedBuf[1024]; - int i; - - /* remove bias -- whether ec is on or not. */ - for ( i = 0; i < n; i++ ) - { - bias += ((((long int) inputBuffer[i]) << 15) - bias) >> 14; - inputBuffer[i] -= (short int) (bias >> 15); - } - - /* if ec is off, clear ec state -- this way, we start fresh if/when - * it's turned back on. */ - if ( !(iaxc_get_filters() & IAXC_FILTER_ECHO) ) - { -#if defined(DO_EC) - if ( ec ) - { -#if defined(USE_MEC2) || defined(SPAN_EC) - echo_can_free(ec); - ec = NULL; -#elif defined(SPEEX_EC) - speex_echo_state_destroy(ec); - ec = NULL; -#endif - } -#endif - - return; - } - - /* we want echo cancellation */ - -#if defined(DO_EC) - if ( !ec ) - { - rb_InitializeRingBuffer(&ecOutRing, EC_RING_SZ, &outRingBuf); -#if defined(USE_MEC2) || defined(SPAN_EC) - ec = echo_can_create(ECHO_TAIL, 0); -#elif defined(SPEEX_EC) - ec = speex_echo_state_init(SAMPLES_PER_FRAME, ECHO_TAIL); -#endif - } -#endif - - /* fill ecOutRing */ - rb_WriteRingBuffer(&ecOutRing, outputBuffer, n * 2); - - // Make sure we have enough buffer. - // Currently, just one SAMPLES_PER_FRAME's worth. - if ( rb_GetRingBufferReadAvailable(&ecOutRing) < ((n + SAMPLES_PER_FRAME) * 2) ) - return; - - rb_ReadRingBuffer(&ecOutRing, delayedBuf, n * 2); - -#if defined(DO_EC) && defined(SPEEX_EC) - { - short cancelledBuffer[1024]; - - speex_echo_cancel(ec, inputBuffer, delayedBuf, - cancelledBuffer, NULL); - - for ( i = 0; i < n; i++ ) - inputBuffer[i] = cancelledBuffer[i]; - } -#endif - -#if defined(USE_MEC2) || defined(SPAN_EC) - for ( i = 0; i < n; i++ ) - inputBuffer[i] = echo_can_update(ec, delayedBuf[i], - inputBuffer[i]); -#endif -} - static int pa_callback(const void *inputBuffer, void *outputBuffer, unsigned long samplesPerFrame, const PaStreamCallbackTimeInfo* outTime, @@ -524,23 +413,26 @@ if ( inputBuffer ) { + int res; + /* input overflow might happen here */ if ( virtualMonoIn ) { stereo2mono(virtualInBuffer, (SAMPLE *)inputBuffer, samplesPerFrame); - iaxc_echo_can(virtualInBuffer, virtualOutBuffer, - samplesPerFrame); - - rb_WriteRingBuffer(&inRing, virtualInBuffer, totBytes); + res = audio_echo_cancellation(virtualInBuffer, + virtualOutBuffer, + samplesPerFrame); + if ( !res ) + rb_WriteRingBuffer(&inRing, virtualInBuffer, totBytes); } else { - iaxc_echo_can((short *)inputBuffer, - (short *)outputBuffer, - samplesPerFrame); - - rb_WriteRingBuffer(&inRing, inputBuffer, totBytes); + res = audio_echo_cancellation((short *)inputBuffer, + (short *)outputBuffer, + samplesPerFrame); + if ( !res) + rb_WriteRingBuffer(&inRing, inputBuffer, totBytes); } } @@ -599,7 +491,7 @@ &in_stream_params, &out_stream_params, iaxci_sample_rate, - paFramesPerBufferUnspecified, //FEEBACK - unsure if appropriate + SAMPLES_PER_FRAME, paNoFlag, (PaStreamCallback *)pa_callback, NULL); @@ -612,7 +504,7 @@ &in_stream_params, &no_device, iaxci_sample_rate, - paFramesPerBufferUnspecified, //FEEBACK - unsure if appropriate + SAMPLES_PER_FRAME, paNoFlag, (PaStreamCallback *)pa_callback, NULL); @@ -622,7 +514,7 @@ &no_device, &out_stream_params, iaxci_sample_rate, - paFramesPerBufferUnspecified, //FEEBACK - unsure if appropriate + SAMPLES_PER_FRAME, paNoFlag, (PaStreamCallback *)pa_callback, NULL); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |