Blame speex/pcm_speex.c

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