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/oss/pcm_oss.c b/oss/pcm_oss.c index 8a2a672..d43b44c 100644 --- a/oss/pcm_oss.c +++ b/oss/pcm_oss.c @@ -181,6 +181,7 @@ static int oss_hw_params(snd_pcm_ioplug_t *io, fprintf(stderr, "*** OSS: invalid period size %d\n", (int)io->period_size); return -EINVAL; } + oss->periods = io->buffer_size / io->period_size; _retry: tmp = oss->period_shift | (oss->periods << 16); diff --git a/pph/rate_speexrate.c b/pph/rate_speexrate.c index 38244b6..0a1325c 100644 --- a/pph/rate_speexrate.c +++ b/pph/rate_speexrate.c @@ -116,6 +116,26 @@ static void pcm_src_close(void *obj) free(obj); } +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 +static int get_supported_rates(void *obj, unsigned int *rate_min, + unsigned int *rate_max) +{ + *rate_min = *rate_max = 0; /* both unlimited */ + return 0; +} + +static void dump(void *obj, snd_output_t *out) +{ + snd_output_printf(out, "Converter: libspeex " +#ifdef USE_LIBSPEEX + "(external)" +#else + "(builtin)" +#endif + "\n"); +} +#endif + static snd_pcm_rate_ops_t pcm_src_ops = { .close = pcm_src_close, .init = pcm_src_init, @@ -125,6 +145,11 @@ static snd_pcm_rate_ops_t pcm_src_ops = { .convert_s16 = pcm_src_convert_s16, .input_frames = input_frames, .output_frames = output_frames, +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + .version = SND_PCM_RATE_PLUGIN_VERSION, + .get_supported_rates = get_supported_rates, + .dump = dump, +#endif }; static int pcm_src_open(unsigned int version, void **objp, @@ -132,18 +157,24 @@ static int pcm_src_open(unsigned int version, void **objp, { struct rate_src *rate; +#if SND_PCM_RATE_PLUGIN_VERSION < 0x010002 if (version != SND_PCM_RATE_PLUGIN_VERSION) { fprintf(stderr, "Invalid rate plugin version %x\n", version); return -EINVAL; } - +#endif rate = calloc(1, sizeof(*rate)); if (! rate) return -ENOMEM; rate->quality = quality; *objp = rate; - *ops = pcm_src_ops; +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + if (version == 0x010001) + memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t)); + else +#endif + *ops = pcm_src_ops; return 0; } diff --git a/pulse/pcm_pulse.c b/pulse/pcm_pulse.c index db8d1e1..c276839 100644 --- a/pulse/pcm_pulse.c +++ b/pulse/pcm_pulse.c @@ -739,6 +739,30 @@ static int pulse_close(snd_pcm_ioplug_t * io) return 0; } +static int pulse_pause(snd_pcm_ioplug_t * io, int enable) +{ + snd_pcm_pulse_t *pcm = io->private_data; + int err = 0; + + assert (pcm); + assert (pcm->p); + + pa_threaded_mainloop_lock(pcm->p->mainloop); + + if (pcm->stream) { + pa_operation *o; + o = pa_stream_cork(pcm->stream, enable, NULL, NULL); + if (o) + pa_operation_unref(o); + else + err = -EIO; + } + + pa_threaded_mainloop_unlock(pcm->p->mainloop); + + return err; +} + static const snd_pcm_ioplug_callback_t pulse_playback_callback = { .start = pulse_start, .stop = pulse_stop, @@ -750,6 +774,7 @@ static const snd_pcm_ioplug_callback_t pulse_playback_callback = { .prepare = pulse_prepare, .hw_params = pulse_hw_params, .close = pulse_close, + .pause = pulse_pause }; diff --git a/rate-lavc/rate_lavcrate.c b/rate-lavc/rate_lavcrate.c index ea2e2f5..14a2198 100644 --- a/rate-lavc/rate_lavcrate.c +++ b/rate-lavc/rate_lavcrate.c @@ -202,6 +202,20 @@ static void pcm_src_close(void *obj) pcm_src_free(obj); } +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 +static int get_supported_rates(void *obj, unsigned int *rate_min, + unsigned int *rate_max) +{ + *rate_min = *rate_max = 0; /* both unlimited */ + return 0; +} + +static void dump(void *obj, snd_output_t *out) +{ + snd_output_printf(out, "Converter: liblavc\n"); +} +#endif + static snd_pcm_rate_ops_t pcm_src_ops = { .close = pcm_src_close, .init = pcm_src_init, @@ -211,6 +225,11 @@ static snd_pcm_rate_ops_t pcm_src_ops = { .convert_s16 = pcm_src_convert_s16, .input_frames = input_frames, .output_frames = output_frames, +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + .version = SND_PCM_RATE_PLUGIN_VERSION, + .get_supported_rates = get_supported_rates, + .dump = dump, +#endif }; int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops) @@ -218,18 +237,24 @@ int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops) { struct rate_src *rate; +#if SND_PCM_RATE_PLUGIN_VERSION < 0x010002 if (version != SND_PCM_RATE_PLUGIN_VERSION) { fprintf(stderr, "Invalid rate plugin version %x\n", version); return -EINVAL; } - +#endif rate = calloc(1, sizeof(*rate)); if (!rate) return -ENOMEM; *objp = rate; rate->context = NULL; - *ops = pcm_src_ops; +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + if (version == 0x010001) + memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t)); + else +#endif + *ops = pcm_src_ops; return 0; } diff --git a/rate/rate_samplerate.c b/rate/rate_samplerate.c index e366db0..53a0627 100644 --- a/rate/rate_samplerate.c +++ b/rate/rate_samplerate.c @@ -137,6 +137,20 @@ static void pcm_src_close(void *obj) free(obj); } +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 +static int get_supported_rates(void *obj, unsigned int *rate_min, + unsigned int *rate_max) +{ + *rate_min = *rate_max = 0; /* both unlimited */ + return 0; +} + +static void dump(void *obj, snd_output_t *out) +{ + snd_output_printf(out, "Converter: libsamplerate\n"); +} +#endif + static snd_pcm_rate_ops_t pcm_src_ops = { .close = pcm_src_close, .init = pcm_src_init, @@ -146,6 +160,11 @@ static snd_pcm_rate_ops_t pcm_src_ops = { .convert_s16 = pcm_src_convert_s16, .input_frames = input_frames, .output_frames = output_frames, +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + .version = SND_PCM_RATE_PLUGIN_VERSION, + .get_supported_rates = get_supported_rates, + .dump = dump, +#endif }; static int pcm_src_open(unsigned int version, void **objp, @@ -153,18 +172,24 @@ static int pcm_src_open(unsigned int version, void **objp, { struct rate_src *rate; +#if SND_PCM_RATE_PLUGIN_VERSION < 0x010002 if (version != SND_PCM_RATE_PLUGIN_VERSION) { fprintf(stderr, "Invalid rate plugin version %x\n", version); return -EINVAL; } - +#endif rate = calloc(1, sizeof(*rate)); if (! rate) return -ENOMEM; rate->converter = type; *objp = rate; - *ops = pcm_src_ops; +#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002 + if (version == 0x010001) + memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t)); + else +#endif + *ops = pcm_src_ops; return 0; } 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;