At Tue, 2 Jun 2009 16:39:46 -0400, Robert Krakora wrote:
Hello,
Has anyone successfully employed the ALSA 1.0.20 Speex PCM Plugin? I followed the "speexdsp.txt" document under the 'doc' directory but the result was the following error:
[root@vizioroom105 ~]# arecord -Dplug:mic poopy.wav Recording WAVE 'poopy.wav' : Unsigned 8 bit, Rate 8000 Hz, Mono ALSA lib pcm_params.c:2135:(snd1_pcm_hw_refine_slave) Slave PCM not usable arecord: set_params:957: Broken configuration for this PCM: no configurations available
It's because the slave plugin of pcm.mic is "hw" and that doesn't support mono streams but only stereo. speex plugin requires a mono stream explicitly.
Wrap speex plugin over the default or add plug layer inside it, too.
BTW, I found that I didn't put any echo-cancelling code in the speex plugin. It was just written for denoising.
The below is a quick hack to add the echo-cancelling part. Give it a try (although it's totally untested :)
Takashi
--- diff --git a/doc/speexdsp.txt b/doc/speexdsp.txt index 875fc19..1937de6 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,16 @@ 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..38b3582 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 tiwai@suse.de * @@ -21,12 +21,15 @@ #include <alsa/asoundlib.h> #include <alsa/pcm_external.h> #include <speex/speex_preprocess.h> +#include <speex/speex_echo.h>
-/* 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,12 @@ 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->parms.echo) + databuf = spx->outbuf; + else + databuf = spx->buf;
while (count > 0) { unsigned int chunk; @@ -72,14 +83,19 @@ 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->parms.echo) + speex_echo_capture(spx->echo_state, spx->buf, + spx->outbuf); + speex_preprocess_run(spx->state, databuf); + if (spx->parms.echo) + speex_echo_playback(spx->echo_state, databuf); spx->processed = 1; spx->filled = 0; } @@ -101,13 +117,34 @@ static int spx_init(snd_pcm_extplug_t *ext) } memset(spx->buf, 0, spx->parms.frames * 2);
- if (spx->state) + 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); + } + spx->state = speex_preprocess_state_init(spx->parms.frames, spx->ext.rate); if (!spx->state) return -EIO;
+ if (spx->parms.echo) + 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); speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_AGC, @@ -132,6 +169,8 @@ static int spx_close(snd_pcm_extplug_t *ext) 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 +244,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 +283,12 @@ 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 +306,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;