From 95d463cb38ad7b0ad6e1d1550b9e8195528cbc46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 Jun 2009 08:04:22 +0200 Subject: speex - Add echo-cancelling option to speexdsp plugin Signed-off-by: Takashi Iwai --- doc/speexdsp.txt | 11 +++++++- speex/pcm_speex.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/doc/speexdsp.txt b/doc/speexdsp.txt index 875fc19..5b5e5a0 100644 --- a/doc/speexdsp.txt +++ b/doc/speexdsp.txt @@ -12,7 +12,7 @@ using libspeex DSP API. You can use the plugin with the plugin type Then record like - % arecord -fdat -c1 -Dplug:speex foo.wav + % arecord -fdat -c1 -Dplug:my_pcm foo.wav so that you'll get 48kHz mono stream with the denoising effect. @@ -44,6 +44,15 @@ The following parameters can be set optionally: A boolean value to enable/disable dereverb function. Default is no. +* echo + + A boolean value to enable/disable echo-cancellation function. + Default is no. + +* filter_length + + Number of samples of echo to cancel. As default it's 256. + For example, you can enable agc like pcm.my_pcm { diff --git a/speex/pcm_speex.c b/speex/pcm_speex.c index 7bb9213..757a400 100644 --- a/speex/pcm_speex.c +++ b/speex/pcm_speex.c @@ -1,5 +1,5 @@ /* - * Speex preprocess plugin + * Speex DSP plugin * * Copyright (c) 2009 by Takashi Iwai * @@ -21,12 +21,15 @@ #include #include #include +#include -/* preprocessing parameters */ +/* DSP parameters */ struct spx_parms { int frames; int denoise; int agc; + int echo; + int filter_length; float agc_level; int dereverb; float dereverb_decay; @@ -38,7 +41,9 @@ typedef struct { struct spx_parms parms; /* instance and intermedate buffer */ SpeexPreprocessState *state; + SpeexEchoState *echo_state; short *buf; + short *outbuf; /* running states */ unsigned int filled; unsigned int processed; @@ -64,6 +69,18 @@ spx_transfer(snd_pcm_extplug_t *ext, short *src = area_addr(src_areas, src_offset); short *dst = area_addr(dst_areas, dst_offset); unsigned int count = size; + short *databuf; + + if (!spx->state && !spx->echo_state) { + /* no DSP processing */ + memcpy(dst, src, count * 2); + return size; + } + + if (spx->echo_state) + databuf = spx->outbuf; + else + databuf = spx->buf; while (count > 0) { unsigned int chunk; @@ -72,14 +89,20 @@ spx_transfer(snd_pcm_extplug_t *ext, else chunk = count; if (spx->processed) - memcpy(dst, spx->buf + spx->filled, chunk * 2); + memcpy(dst, databuf + spx->filled, chunk * 2); else memset(dst, 0, chunk * 2); dst += chunk; memcpy(spx->buf + spx->filled, src, chunk * 2); spx->filled += chunk; if (spx->filled == spx->parms.frames) { - speex_preprocess_run(spx->state, spx->buf); + if (spx->echo_state) + speex_echo_capture(spx->echo_state, spx->buf, + spx->outbuf); + if (spx->state) + speex_preprocess_run(spx->state, databuf); + if (spx->echo_state) + speex_echo_playback(spx->echo_state, databuf); spx->processed = 1; spx->filled = 0; } @@ -94,6 +117,9 @@ static int spx_init(snd_pcm_extplug_t *ext) { snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext; + spx->filled = 0; + spx->processed = 0; + if (!spx->buf) { spx->buf = malloc(spx->parms.frames * 2); if (!spx->buf) @@ -101,12 +127,43 @@ static int spx_init(snd_pcm_extplug_t *ext) } memset(spx->buf, 0, spx->parms.frames * 2); - if (spx->state) + if (!spx->outbuf) { + spx->outbuf = malloc(spx->parms.frames * 2); + if (!spx->outbuf) + return -ENOMEM; + } + memset(spx->outbuf, 0, spx->parms.frames * 2); + + if (spx->state) { speex_preprocess_state_destroy(spx->state); + spx->state = NULL; + } + if (spx->echo_state) { + speex_echo_state_destroy(spx->echo_state); + spx->echo_state = NULL; + } + + if (spx->parms.echo) { + spx->echo_state = speex_echo_state_init(spx->parms.frames, + spx->parms.filter_length); + if (!spx->echo_state) + return -EIO; + speex_echo_ctl(spx->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, + &spx->ext.rate); + } + + /* no preprocessor? */ + if (!spx->parms.denoise && !spx->parms.agc && !spx->parms.dereverb) + return 0; + spx->state = speex_preprocess_state_init(spx->parms.frames, spx->ext.rate); if (!spx->state) return -EIO; + if (spx->echo_state) + speex_preprocess_ctl(spx->state, + SPEEX_PREPROCESS_SET_ECHO_STATE, + spx->echo_state); speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DENOISE, &spx->parms.denoise); @@ -120,18 +177,18 @@ static int spx_init(snd_pcm_extplug_t *ext) &spx->parms.dereverb_decay); speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &spx->parms.dereverb_level); - - spx->filled = 0; - spx->processed = 0; return 0; } static int spx_close(snd_pcm_extplug_t *ext) { snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext; + free(spx->outbuf); free(spx->buf); if (spx->state) speex_preprocess_state_destroy(spx->state); + if (spx->echo_state) + speex_echo_state_destroy(spx->echo_state); return 0; } @@ -205,6 +262,8 @@ SND_PCM_PLUGIN_DEFINE_FUNC(speex) .dereverb = 0, .dereverb_decay = 0, .dereverb_level = 0, + .echo = 0, + .filter_length = 256, }; snd_config_for_each(i, next, conf) { @@ -242,6 +301,13 @@ SND_PCM_PLUGIN_DEFINE_FUNC(speex) &parms.dereverb_level); if (err) goto ok; + err = get_bool_parm(n, id, "echo", &parms.echo); + if (err) + goto ok; + err = get_int_parm(n, id, "filter_length", + &parms.filter_length); + if (err) + goto ok; SNDERR("Unknown field %s", id); err = -EINVAL; ok: @@ -259,7 +325,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(speex) return -ENOMEM; spx->ext.version = SND_PCM_EXTPLUG_VERSION; - spx->ext.name = "Speex Denoise Plugin"; + spx->ext.name = "Speex DSP Plugin"; spx->ext.callback = &speex_callback; spx->ext.private_data = spx; spx->parms = parms; -- cgit