|
Packit |
675970 |
/*
|
|
Packit |
675970 |
* Speex DSP plugin
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* Copyright (c) 2009 by Takashi Iwai <tiwai@suse.de>
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This library is free software; you can redistribute it and/or modify
|
|
Packit |
675970 |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
675970 |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
675970 |
* the License, or (at your option) any later version.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
675970 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
675970 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
675970 |
* GNU Lesser General Public License for more details.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
675970 |
* License along with this library; if not, write to the Free Software
|
|
Packit |
675970 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#include "config.h"
|
|
Packit |
675970 |
#include <alsa/asoundlib.h>
|
|
Packit |
675970 |
#include <alsa/pcm_external.h>
|
|
Packit |
675970 |
#include <speex/speex_preprocess.h>
|
|
Packit |
675970 |
#include <speex/speex_echo.h>
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* DSP parameters */
|
|
Packit |
675970 |
struct spx_parms {
|
|
Packit |
675970 |
int frames;
|
|
Packit |
675970 |
int denoise;
|
|
Packit |
675970 |
int agc;
|
|
Packit |
675970 |
int echo;
|
|
Packit |
675970 |
int filter_length;
|
|
Packit |
675970 |
float agc_level;
|
|
Packit |
675970 |
int dereverb;
|
|
Packit |
675970 |
float dereverb_decay;
|
|
Packit |
675970 |
float dereverb_level;
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef struct {
|
|
Packit |
675970 |
snd_pcm_extplug_t ext;
|
|
Packit |
675970 |
struct spx_parms parms;
|
|
Packit |
675970 |
/* instance and intermedate buffer */
|
|
Packit |
675970 |
SpeexPreprocessState *state;
|
|
Packit |
675970 |
SpeexEchoState *echo_state;
|
|
Packit |
675970 |
short *buf;
|
|
Packit |
675970 |
short *outbuf;
|
|
Packit |
675970 |
/* running states */
|
|
Packit |
675970 |
unsigned int filled;
|
|
Packit |
675970 |
unsigned int processed;
|
|
Packit |
675970 |
} snd_pcm_speex_t;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static inline void *area_addr(const snd_pcm_channel_area_t *area,
|
|
Packit |
675970 |
snd_pcm_uframes_t offset)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
unsigned int bitofs = area->first + area->step * offset;
|
|
Packit |
675970 |
return (char *) area->addr + bitofs / 8;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t
|
|
Packit |
675970 |
spx_transfer(snd_pcm_extplug_t *ext,
|
|
Packit |
675970 |
const snd_pcm_channel_area_t *dst_areas,
|
|
Packit |
675970 |
snd_pcm_uframes_t dst_offset,
|
|
Packit |
675970 |
const snd_pcm_channel_area_t *src_areas,
|
|
Packit |
675970 |
snd_pcm_uframes_t src_offset,
|
|
Packit |
675970 |
snd_pcm_uframes_t size)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext;
|
|
Packit |
675970 |
short *src = area_addr(src_areas, src_offset);
|
|
Packit |
675970 |
short *dst = area_addr(dst_areas, dst_offset);
|
|
Packit |
675970 |
unsigned int count = size;
|
|
Packit |
675970 |
short *databuf;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!spx->state && !spx->echo_state) {
|
|
Packit |
675970 |
/* no DSP processing */
|
|
Packit |
675970 |
memcpy(dst, src, count * 2);
|
|
Packit |
675970 |
return size;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (spx->echo_state)
|
|
Packit |
675970 |
databuf = spx->outbuf;
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
databuf = spx->buf;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
while (count > 0) {
|
|
Packit |
675970 |
unsigned int chunk;
|
|
Packit |
675970 |
if (spx->filled + count > spx->parms.frames)
|
|
Packit |
675970 |
chunk = spx->parms.frames - spx->filled;
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
chunk = count;
|
|
Packit |
675970 |
if (spx->processed)
|
|
Packit |
675970 |
memcpy(dst, databuf + spx->filled, chunk * 2);
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
memset(dst, 0, chunk * 2);
|
|
Packit |
675970 |
dst += chunk;
|
|
Packit |
675970 |
memcpy(spx->buf + spx->filled, src, chunk * 2);
|
|
Packit |
675970 |
spx->filled += chunk;
|
|
Packit |
675970 |
if (spx->filled == spx->parms.frames) {
|
|
Packit |
675970 |
if (spx->echo_state)
|
|
Packit |
675970 |
speex_echo_capture(spx->echo_state, spx->buf,
|
|
Packit |
675970 |
spx->outbuf);
|
|
Packit |
675970 |
if (spx->state)
|
|
Packit |
675970 |
speex_preprocess_run(spx->state, databuf);
|
|
Packit |
675970 |
if (spx->echo_state)
|
|
Packit |
675970 |
speex_echo_playback(spx->echo_state, databuf);
|
|
Packit |
675970 |
spx->processed = 1;
|
|
Packit |
675970 |
spx->filled = 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
src += chunk;
|
|
Packit |
675970 |
count -= chunk;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return size;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int spx_init(snd_pcm_extplug_t *ext)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
spx->filled = 0;
|
|
Packit |
675970 |
spx->processed = 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!spx->buf) {
|
|
Packit |
675970 |
spx->buf = malloc(spx->parms.frames * 2);
|
|
Packit |
675970 |
if (!spx->buf)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
memset(spx->buf, 0, spx->parms.frames * 2);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!spx->outbuf) {
|
|
Packit |
675970 |
spx->outbuf = malloc(spx->parms.frames * 2);
|
|
Packit |
675970 |
if (!spx->outbuf)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
memset(spx->outbuf, 0, spx->parms.frames * 2);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (spx->state) {
|
|
Packit |
675970 |
speex_preprocess_state_destroy(spx->state);
|
|
Packit |
675970 |
spx->state = NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (spx->echo_state) {
|
|
Packit |
675970 |
speex_echo_state_destroy(spx->echo_state);
|
|
Packit |
675970 |
spx->echo_state = NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (spx->parms.echo) {
|
|
Packit |
675970 |
spx->echo_state = speex_echo_state_init(spx->parms.frames,
|
|
Packit |
675970 |
spx->parms.filter_length);
|
|
Packit |
675970 |
if (!spx->echo_state)
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
speex_echo_ctl(spx->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE,
|
|
Packit |
675970 |
&spx->ext.rate);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* no preprocessor? */
|
|
Packit |
675970 |
if (!spx->parms.denoise && !spx->parms.agc && !spx->parms.dereverb)
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
spx->state = speex_preprocess_state_init(spx->parms.frames,
|
|
Packit |
675970 |
spx->ext.rate);
|
|
Packit |
675970 |
if (!spx->state)
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
if (spx->echo_state)
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state,
|
|
Packit |
675970 |
SPEEX_PREPROCESS_SET_ECHO_STATE,
|
|
Packit |
675970 |
spx->echo_state);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DENOISE,
|
|
Packit |
675970 |
&spx->parms.denoise);
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_AGC,
|
|
Packit |
675970 |
&spx->parms.agc);
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_AGC_LEVEL,
|
|
Packit |
675970 |
&spx->parms.agc_level);
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB,
|
|
Packit |
675970 |
&spx->parms.dereverb);
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB_DECAY,
|
|
Packit |
675970 |
&spx->parms.dereverb_decay);
|
|
Packit |
675970 |
speex_preprocess_ctl(spx->state, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL,
|
|
Packit |
675970 |
&spx->parms.dereverb_level);
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int spx_close(snd_pcm_extplug_t *ext)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_speex_t *spx = (snd_pcm_speex_t *)ext;
|
|
Packit |
675970 |
free(spx->outbuf);
|
|
Packit |
675970 |
free(spx->buf);
|
|
Packit |
675970 |
if (spx->state)
|
|
Packit |
675970 |
speex_preprocess_state_destroy(spx->state);
|
|
Packit |
675970 |
if (spx->echo_state)
|
|
Packit |
675970 |
speex_echo_state_destroy(spx->echo_state);
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static const snd_pcm_extplug_callback_t speex_callback = {
|
|
Packit |
675970 |
.transfer = spx_transfer,
|
|
Packit |
675970 |
.init = spx_init,
|
|
Packit |
675970 |
.close = spx_close,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int get_bool_parm(snd_config_t *n, const char *id, const char *str,
|
|
Packit |
675970 |
int *val_ret)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
int val;
|
|
Packit |
675970 |
if (strcmp(id, str))
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
val = snd_config_get_bool(n);
|
|
Packit |
675970 |
if (val < 0) {
|
|
Packit |
675970 |
SNDERR("Invalid value for %s", id);
|
|
Packit |
675970 |
return val;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
*val_ret = val;
|
|
Packit |
675970 |
return 1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int get_int_parm(snd_config_t *n, const char *id, const char *str,
|
|
Packit |
675970 |
int *val_ret)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
long val;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (strcmp(id, str))
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
err = snd_config_get_integer(n, &val;;
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
SNDERR("Invalid value for %s parameter", id);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
*val_ret = val;
|
|
Packit |
675970 |
return 1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int get_float_parm(snd_config_t *n, const char *id, const char *str,
|
|
Packit |
675970 |
float *val_ret)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
double val;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (strcmp(id, str))
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
err = snd_config_get_ireal(n, &val;;
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
SNDERR("Invalid value for %s", id);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
*val_ret = val;
|
|
Packit |
675970 |
return 1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_DEFINE_FUNC(speex)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_config_iterator_t i, next;
|
|
Packit |
675970 |
snd_pcm_speex_t *spx;
|
|
Packit |
675970 |
snd_config_t *sconf = NULL;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
struct spx_parms parms = {
|
|
Packit |
675970 |
.frames = 64,
|
|
Packit |
675970 |
.denoise = 1,
|
|
Packit |
675970 |
.agc = 0,
|
|
Packit |
675970 |
.agc_level = 8000,
|
|
Packit |
675970 |
.dereverb = 0,
|
|
Packit |
675970 |
.dereverb_decay = 0,
|
|
Packit |
675970 |
.dereverb_level = 0,
|
|
Packit |
675970 |
.echo = 0,
|
|
Packit |
675970 |
.filter_length = 256,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_config_for_each(i, next, conf) {
|
|
Packit |
675970 |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit |
675970 |
const char *id;
|
|
Packit |
675970 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 ||
|
|
Packit |
675970 |
strcmp(id, "hint") == 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
if (strcmp(id, "slave") == 0) {
|
|
Packit |
675970 |
sconf = n;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
err = get_int_parm(n, id, "frames", &parms.frames);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_bool_parm(n, id, "denoise", &parms.denoise);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_bool_parm(n, id, "agc", &parms.agc);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_float_parm(n, id, "agc_level", &parms.agc_level);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_bool_parm(n, id, "dereverb", &parms.dereverb);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_float_parm(n, id, "dereverb_decay",
|
|
Packit |
675970 |
&parms.dereverb_decay);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_float_parm(n, id, "dereverb_level",
|
|
Packit |
675970 |
&parms.dereverb_level);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_bool_parm(n, id, "echo", &parms.echo);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
err = get_int_parm(n, id, "filter_length",
|
|
Packit |
675970 |
&parms.filter_length);
|
|
Packit |
675970 |
if (err)
|
|
Packit |
675970 |
goto ok;
|
|
Packit |
675970 |
SNDERR("Unknown field %s", id);
|
|
Packit |
675970 |
err = -EINVAL;
|
|
Packit |
675970 |
ok:
|
|
Packit |
675970 |
if (err < 0)
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!sconf) {
|
|
Packit |
675970 |
SNDERR("No slave configuration for speex pcm");
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
spx = calloc(1, sizeof(*spx));
|
|
Packit |
675970 |
if (!spx)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
spx->ext.version = SND_PCM_EXTPLUG_VERSION;
|
|
Packit |
675970 |
spx->ext.name = "Speex DSP Plugin";
|
|
Packit |
675970 |
spx->ext.callback = &speex_callback;
|
|
Packit |
675970 |
spx->ext.private_data = spx;
|
|
Packit |
675970 |
spx->parms = parms;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_extplug_create(&spx->ext, name, root, sconf,
|
|
Packit |
675970 |
stream, mode);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
free(spx);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_pcm_extplug_set_param(&spx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1);
|
|
Packit |
675970 |
snd_pcm_extplug_set_slave_param(&spx->ext,
|
|
Packit |
675970 |
SND_PCM_EXTPLUG_HW_CHANNELS, 1);
|
|
Packit |
675970 |
snd_pcm_extplug_set_param(&spx->ext, SND_PCM_EXTPLUG_HW_FORMAT,
|
|
Packit |
675970 |
SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
snd_pcm_extplug_set_slave_param(&spx->ext, SND_PCM_EXTPLUG_HW_FORMAT,
|
|
Packit |
675970 |
SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
*pcmp = spx->ext.pcm;
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_SYMBOL(speex);
|