]> git.alsa-project.org Git - alsa-plugins.git/commitdiff
Add Speex pre-processing plugin
authorTakashi Iwai <tiwai@suse.de>
Wed, 21 Jan 2009 17:11:43 +0000 (18:11 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 21 Jan 2009 17:11:43 +0000 (18:11 +0100)
Added Speex pre-processing filter plugin for denoise, AGC, etc.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Makefile.am
configure.in
doc/Makefile.am
doc/speexdsp.txt [new file with mode: 0644]
speex/Makefile.am [new file with mode: 0644]
speex/pcm_speex.c [new file with mode: 0644]

index b32f1f5460870aa6b5bdb6efff88e9799d5cc277..aba3ef68b4ffdc3e1112fac1fcf4fe0bed1809e2 100644 (file)
@@ -17,8 +17,11 @@ endif
 if HAVE_PPH
 PPHDIR = pph
 endif
+if HAVE_SPEEXDSP
+SPEEXDIR = speex
+endif
 
-SUBDIRS = oss mix $(PPHDIR) $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(LAVCRATEDIR) $(MAEMODIR) usb_stream doc
+SUBDIRS = oss mix $(PPHDIR) $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(LAVCRATEDIR) $(MAEMODIR) $(SPEEXDIR) usb_stream doc
 EXTRA_DIST = gitcompile version COPYING.GPL m4/attributes.m4
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
index ce95b5d852a79078d5b03d60324833f1ce7adfcf..12c6f54836d6fc61b3e00062127cfb4b20e81fbf 100644 (file)
@@ -166,6 +166,7 @@ AC_OUTPUT([
        maemo/Makefile
        doc/Makefile
        usb_stream/Makefile
+       speex/Makefile
 ])
 
 dnl Show the build conditions
@@ -198,7 +199,8 @@ if test "$HAVE_AVCODEC" = "yes"; then
   echo "  AVCODEC_HEADER: $AVCODEC_HEADER"
 fi
 echo "Speex rate plugin:  $PPH"
-if test "$PPH" = "lib"; then
+echo "Speex preprocess plugin:  $HAVE_SPEEXDSP"
+if test "$HAVE_SPEEX" = "yes"; then
   echo "  speexdsp_CFLAGS: $speexdsp_CFLAGS"
   echo "  speexdsp_LIBS: $speexdsp_LIBS"
 fi
index 41a7ebe8efcd0688ab23868bdc7e8a04a24d6638..3e89be8bbfdf3289c27fadaad38ec2dd8ba07700 100644 (file)
@@ -1,3 +1,4 @@
 EXTRA_DIST = README-pcm-oss README-jack README-pulse README-maemo \
        upmix.txt vdownmix.txt samplerate.txt a52.txt lavcrate.txt \
-       speexrate.txt
+       speexrate.txt speexdsp.txt
+
diff --git a/doc/speexdsp.txt b/doc/speexdsp.txt
new file mode 100644 (file)
index 0000000..875fc19
--- /dev/null
@@ -0,0 +1,54 @@
+Speex Preprocessing Plugin
+==========================
+
+This plugin provides a pre-processing of a mono stream like denoise
+using libspeex DSP API.  You can use the plugin with the plugin type
+"speex" like below:
+
+       pcm.my_pcm {
+               type speex
+               slave.pcm "default"
+       }
+
+Then record like
+
+       % arecord -fdat -c1 -Dplug:speex foo.wav
+
+so that you'll get 48kHz mono stream with the denoising effect.
+
+Right now, the plugin supports only a mono stream.
+The accepted format is only S16.
+
+The following parameters can be set optionally:
+
+* frames
+
+  This controls the frames of the intermediate buffer.  This
+  corresponds to the latency of the filter.  As default it's 64.
+
+* denoise
+
+  A boolean value to enable/disable the denoise function.  Default is
+  yes.
+
+* agc
+   
+  A boolean value to enable/disable the auto-gain control function.
+  Default is no.
+
+* agc_level
+
+  A float value for the automatic gain-control level.  Default is 8000.
+
+* dereverb
+
+  A boolean value to enable/disable dereverb function.  Default is no.
+
+For example, you can enable agc like
+
+       pcm.my_pcm {
+               type speex
+               slave.pcm "default"
+               agc 1
+               agc_level 8000
+       }
diff --git a/speex/Makefile.am b/speex/Makefile.am
new file mode 100644 (file)
index 0000000..7d84190
--- /dev/null
@@ -0,0 +1,9 @@
+asound_module_pcm_speex_LTLIBRARIES = libasound_module_pcm_speex.la
+
+asound_module_pcm_speexdir = @ALSA_PLUGIN_DIR@
+
+AM_CFLAGS = -Wall -g @ALSA_CFLAGS@ @speexdsp_CFLAGS@
+AM_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined $(LDFLAGS_NOUNDEFINED)
+
+libasound_module_pcm_speex_la_SOURCES = pcm_speex.c
+libasound_module_pcm_speex_la_LIBADD = @ALSA_LIBS@ @speexdsp_LIBS@
diff --git a/speex/pcm_speex.c b/speex/pcm_speex.c
new file mode 100644 (file)
index 0000000..7bb9213
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Speex preprocess plugin
+ *
+ * Copyright (c) 2009 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_external.h>
+#include <speex/speex_preprocess.h>
+
+/* preprocessing parameters */
+struct spx_parms {
+       int frames;
+       int denoise;
+       int agc;
+       float agc_level;
+       int dereverb;
+       float dereverb_decay;
+       float dereverb_level;
+};
+
+typedef struct {
+       snd_pcm_extplug_t ext;
+       struct spx_parms parms;
+       /* instance and intermedate buffer */
+       SpeexPreprocessState *state;
+       short *buf;
+       /* running states */
+       unsigned int filled;
+       unsigned int processed;
+} snd_pcm_speex_t;
+
+
+static inline void *area_addr(const snd_pcm_channel_area_t *area,
+                             snd_pcm_uframes_t offset)
+{
+       unsigned int bitofs = area->first + area->step * offset;
+       return (char *) area->addr + bitofs / 8;
+}
+
+static snd_pcm_sframes_t
+spx_transfer(snd_pcm_extplug_t *ext,
+            const snd_pcm_channel_area_t *dst_areas,
+            snd_pcm_uframes_t dst_offset,
+            const snd_pcm_channel_area_t *src_areas,
+            snd_pcm_uframes_t src_offset,
+            snd_pcm_uframes_t size)
+{
+       snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext;
+       short *src = area_addr(src_areas, src_offset);
+       short *dst = area_addr(dst_areas, dst_offset);
+       unsigned int count = size;
+
+       while (count > 0) {
+               unsigned int chunk;
+               if (spx->filled + count > spx->parms.frames)
+                       chunk = spx->parms.frames - spx->filled;
+               else
+                       chunk = count;
+               if (spx->processed)
+                       memcpy(dst, spx->buf + 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);
+                       spx->processed = 1;
+                       spx->filled = 0;
+               }
+               src += chunk;
+               count -= chunk;
+       }
+
+       return size;
+}
+
+static int spx_init(snd_pcm_extplug_t *ext)
+{
+       snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext;
+
+       if (!spx->buf) {
+               spx->buf = malloc(spx->parms.frames * 2);
+               if (!spx->buf)
+                       return -ENOMEM;
+       }
+       memset(spx->buf, 0, spx->parms.frames * 2);
+
+       if (spx->state)
+               speex_preprocess_state_destroy(spx->state);
+       spx->state = speex_preprocess_state_init(spx->parms.frames,
+                                                spx->ext.rate);
+       if (!spx->state)
+               return -EIO;
+
+       speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DENOISE,
+                            &spx->parms.denoise);
+       speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_AGC,
+                            &spx->parms.agc);
+       speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_AGC_LEVEL,
+                            &spx->parms.agc_level);
+       speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB,
+                            &spx->parms.dereverb);
+       speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB_DECAY,
+                            &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->buf);
+       if (spx->state)
+               speex_preprocess_state_destroy(spx->state);
+       return 0;
+}
+
+static const snd_pcm_extplug_callback_t speex_callback = {
+       .transfer = spx_transfer,
+       .init = spx_init,
+       .close = spx_close,
+};
+
+static int get_bool_parm(snd_config_t *n, const char *id, const char *str,
+                        int *val_ret)
+{
+       int val;
+       if (strcmp(id, str))
+               return 0;
+
+       val = snd_config_get_bool(n);
+       if (val < 0) {
+               SNDERR("Invalid value for %s", id);
+               return val;
+       }
+       *val_ret = val;
+       return 1;
+}
+
+static int get_int_parm(snd_config_t *n, const char *id, const char *str,
+                       int *val_ret)
+{
+       long val;
+       int err;
+
+       if (strcmp(id, str))
+               return 0;
+       err = snd_config_get_integer(n, &val);
+       if (err < 0) {
+               SNDERR("Invalid value for %s parameter", id);
+               return err;
+       }
+       *val_ret = val;
+       return 1;
+}
+
+static int get_float_parm(snd_config_t *n, const char *id, const char *str,
+                         float *val_ret)
+{
+       double val;
+       int err;
+
+       if (strcmp(id, str))
+               return 0;
+       err = snd_config_get_ireal(n, &val);
+       if (err < 0) {
+               SNDERR("Invalid value for %s", id);
+               return err;
+       }
+       *val_ret = val;
+       return 1;
+}
+
+SND_PCM_PLUGIN_DEFINE_FUNC(speex)
+{
+       snd_config_iterator_t i, next;
+       snd_pcm_speex_t *spx;
+       snd_config_t *sconf = NULL;
+       int err;
+       struct spx_parms parms = {
+               .frames = 64,
+               .denoise = 1,
+               .agc = 0,
+               .agc_level = 8000,
+               .dereverb = 0,
+               .dereverb_decay = 0,
+               .dereverb_level = 0,
+       };
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 ||
+                   strcmp(id, "hint") == 0)
+                       continue;
+               if (strcmp(id, "slave") == 0) {
+                       sconf = n;
+                       continue;
+               }
+               err = get_int_parm(n, id, "frames", &parms.frames);
+               if (err)
+                       goto ok;
+               err = get_bool_parm(n, id, "denoise", &parms.denoise);
+               if (err)
+                       goto ok;
+               err = get_bool_parm(n, id, "agc", &parms.agc);
+               if (err)
+                       goto ok;
+               err = get_float_parm(n, id, "agc_level", &parms.agc_level);
+               if (err)
+                       goto ok;
+               err = get_bool_parm(n, id, "dereverb", &parms.dereverb);
+               if (err)
+                       goto ok;
+               err = get_float_parm(n, id, "dereverb_decay",
+                                    &parms.dereverb_decay);
+               if (err)
+                       goto ok;
+               err = get_float_parm(n, id, "dereverb_level",
+                                    &parms.dereverb_level);
+               if (err)
+                       goto ok;
+               SNDERR("Unknown field %s", id);
+               err = -EINVAL;
+       ok:
+               if (err < 0)
+                       return err;
+       }
+
+       if (!sconf) {
+               SNDERR("No slave configuration for speex pcm");
+               return -EINVAL;
+       }
+
+       spx = calloc(1, sizeof(*spx));
+       if (!spx)
+               return -ENOMEM;
+
+       spx->ext.version = SND_PCM_EXTPLUG_VERSION;
+       spx->ext.name = "Speex Denoise Plugin";
+       spx->ext.callback = &speex_callback;
+       spx->ext.private_data = spx;
+       spx->parms = parms;
+
+       err = snd_pcm_extplug_create(&spx->ext, name, root, sconf,
+                                    stream, mode);
+       if (err < 0) {
+               free(spx);
+               return err;
+       }
+
+       snd_pcm_extplug_set_param(&spx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1);
+       snd_pcm_extplug_set_slave_param(&spx->ext,
+                                       SND_PCM_EXTPLUG_HW_CHANNELS, 1);
+       snd_pcm_extplug_set_param(&spx->ext, SND_PCM_EXTPLUG_HW_FORMAT,
+                                 SND_PCM_FORMAT_S16);
+       snd_pcm_extplug_set_slave_param(&spx->ext, SND_PCM_EXTPLUG_HW_FORMAT,
+                                       SND_PCM_FORMAT_S16);
+
+       *pcmp = spx->ext.pcm;
+       return 0;
+}
+
+SND_PCM_PLUGIN_SYMBOL(speex);