Blame speex/pcm_speex.c

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);