Blame mix/pcm_vdownmix.c

Packit Service cd2a00
/*
Packit Service cd2a00
 * 4/5/6 -> 2 downmix with a simple spacialization
Packit Service cd2a00
 *
Packit Service cd2a00
 * Copyright (c) 2006 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 <alsa/asoundlib.h>
Packit Service cd2a00
#include <alsa/pcm_external.h>
Packit Service cd2a00
Packit Service cd2a00
/* #define I_AM_POWERFUL */
Packit Service cd2a00
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
#define RINGBUF_SIZE	(1 << 9)
Packit Service cd2a00
#else
Packit Service cd2a00
#define RINGBUF_SIZE	(1 << 7)
Packit Service cd2a00
#endif
Packit Service cd2a00
#define RINGBUF_MASK	(RINGBUF_SIZE - 1)
Packit Service cd2a00
Packit Service cd2a00
struct vdownmix_tap {
Packit Service cd2a00
	int delay;
Packit Service cd2a00
	int weight;
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
#define MAX_TAPS	30
Packit Service cd2a00
Packit Service cd2a00
struct vdownmix_filter {
Packit Service cd2a00
	int taps;
Packit Service cd2a00
	struct vdownmix_tap tap[MAX_TAPS];
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
typedef struct {
Packit Service cd2a00
	snd_pcm_extplug_t ext;
Packit Service cd2a00
	int channels;
Packit Service cd2a00
	unsigned int curpos;
Packit Service cd2a00
	short rbuf[RINGBUF_SIZE][5];
Packit Service cd2a00
} snd_pcm_vdownmix_t;
Packit Service cd2a00
Packit Service cd2a00
static const struct vdownmix_filter tap_filters[5] = {
Packit Service cd2a00
	{
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
		18,
Packit Service cd2a00
#else
Packit Service cd2a00
		14,
Packit Service cd2a00
#endif
Packit Service cd2a00
		{{ 0, 0xfffffd0a },
Packit Service cd2a00
		 { 1, 0x41d },
Packit Service cd2a00
		 { 2, 0xffffe657 },
Packit Service cd2a00
		 { 3, 0x6eb5 },
Packit Service cd2a00
		 { 4, 0xffffe657 },
Packit Service cd2a00
		 { 5, 0x41d },
Packit Service cd2a00
		 { 6, 0xfffffd0a },
Packit Service cd2a00
		 { 71, 0xffffff1c },
Packit Service cd2a00
		 { 72, 0x12e },
Packit Service cd2a00
		 { 73, 0xfffff81a },
Packit Service cd2a00
		 { 74, 0x24de },
Packit Service cd2a00
		 { 75, 0xfffff81a },
Packit Service cd2a00
		 { 76, 0x12e },
Packit Service cd2a00
		 { 77, 0xffffff1c },
Packit Service cd2a00
		 { 265, 0xfffffc65 },
Packit Service cd2a00
		 { 266, 0xee1 },
Packit Service cd2a00
		 { 267, 0xfffffc65 },
Packit Service cd2a00
		 { 395, 0x46a }},
Packit Service cd2a00
	},
Packit Service cd2a00
Packit Service cd2a00
	{
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
		17,
Packit Service cd2a00
#else
Packit Service cd2a00
		10,
Packit Service cd2a00
#endif
Packit Service cd2a00
		{{ 8, 0xcf },
Packit Service cd2a00
		 { 9, 0xa7b },
Packit Service cd2a00
		 { 10, 0xcd7 },
Packit Service cd2a00
		 { 11, 0x5b3 },
Packit Service cd2a00
		 { 12, 0x859 },
Packit Service cd2a00
		 { 13, 0xaf },
Packit Service cd2a00
		 { 80, 0x38b },
Packit Service cd2a00
		 { 81, 0x454 },
Packit Service cd2a00
		 { 82, 0x218 },
Packit Service cd2a00
		 { 83, 0x2c1 },
Packit Service cd2a00
		 { 268, 0x58b },
Packit Service cd2a00
		 { 275, 0xc2 },
Packit Service cd2a00
		 { 397, 0xbd },
Packit Service cd2a00
		 { 398, 0x1e8 },
Packit Service cd2a00
		 { 506, 0xfffffeac },
Packit Service cd2a00
		 { 507, 0x636 },
Packit Service cd2a00
		 { 508, 0xfffffeac }},
Packit Service cd2a00
	},
Packit Service cd2a00
Packit Service cd2a00
	{
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
		11,
Packit Service cd2a00
#else
Packit Service cd2a00
		1,
Packit Service cd2a00
#endif
Packit Service cd2a00
		{{ 3, 0x4000 },
Packit Service cd2a00
		 { 125, 0x12a },
Packit Service cd2a00
		 { 126, 0xda1 },
Packit Service cd2a00
		 { 127, 0x12a },
Packit Service cd2a00
		 { 193, 0xfffffed3 },
Packit Service cd2a00
		 { 194, 0xdb9 },
Packit Service cd2a00
		 { 195, 0xfffffed3 },
Packit Service cd2a00
		 { 454, 0x10a },
Packit Service cd2a00
		 { 483, 0xfffffe97 },
Packit Service cd2a00
		 { 484, 0x698 },
Packit Service cd2a00
		 { 485, 0xfffffe97 }},
Packit Service cd2a00
	},
Packit Service cd2a00
Packit Service cd2a00
	{
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
		25,
Packit Service cd2a00
#else
Packit Service cd2a00
		10,
Packit Service cd2a00
#endif
Packit Service cd2a00
		{{ 5, 0x1cb },
Packit Service cd2a00
		 { 6, 0x9c5 },
Packit Service cd2a00
		 { 7, 0x117e },
Packit Service cd2a00
		 { 8, 0x200 },
Packit Service cd2a00
		 { 9, 0x533 },
Packit Service cd2a00
		 { 10, 0x1c6 },
Packit Service cd2a00
		 { 11, 0x167 },
Packit Service cd2a00
		 { 12, 0x5ff },
Packit Service cd2a00
		 { 13, 0x425 },
Packit Service cd2a00
		 { 14, 0xd9 },
Packit Service cd2a00
		 { 128, 0x247 },
Packit Service cd2a00
		 { 129, 0x5e1 },
Packit Service cd2a00
		 { 130, 0xb7 },
Packit Service cd2a00
		 { 131, 0x122 },
Packit Service cd2a00
		 { 135, 0x10a },
Packit Service cd2a00
		 { 200, 0x1b6 },
Packit Service cd2a00
		 { 201, 0xa7 },
Packit Service cd2a00
		 { 202, 0x188 },
Packit Service cd2a00
		 { 203, 0x1d9 },
Packit Service cd2a00
		 { 445, 0xffffff44 },
Packit Service cd2a00
		 { 446, 0x5e2 },
Packit Service cd2a00
		 { 447, 0xffffff44 },
Packit Service cd2a00
		 { 484, 0xffffff51 },
Packit Service cd2a00
		 { 485, 0x449 },
Packit Service cd2a00
		 { 486, 0xffffff51 }},
Packit Service cd2a00
	},
Packit Service cd2a00
Packit Service cd2a00
	{
Packit Service cd2a00
#ifdef I_AM_POWERFUL
Packit Service cd2a00
		21,
Packit Service cd2a00
#else
Packit Service cd2a00
		7,
Packit Service cd2a00
#endif
Packit Service cd2a00
		{{ 0, 0xfffffdee },
Packit Service cd2a00
		 { 1, 0x28b },
Packit Service cd2a00
		 { 2, 0xffffed1e },
Packit Service cd2a00
		 { 3, 0x6336 },
Packit Service cd2a00
		 { 4, 0xffffed1e },
Packit Service cd2a00
		 { 5, 0x28b },
Packit Service cd2a00
		 { 6, 0xfffffdee },
Packit Service cd2a00
		 { 51, 0xffffff2c },
Packit Service cd2a00
		 { 52, 0x105 },
Packit Service cd2a00
		 { 53, 0xfffff86b },
Packit Service cd2a00
		 { 54, 0x27d9 },
Packit Service cd2a00
		 { 55, 0xfffff86b },
Packit Service cd2a00
		 { 56, 0x105 },
Packit Service cd2a00
		 { 57, 0xffffff2c },
Packit Service cd2a00
		 { 333, 0xfffffd69 },
Packit Service cd2a00
		 { 334, 0xb2f },
Packit Service cd2a00
		 { 335, 0xfffffd69 },
Packit Service cd2a00
		 { 339, 0xdf },
Packit Service cd2a00
		 { 340, 0x168 },
Packit Service cd2a00
		 { 342, 0xa6 },
Packit Service cd2a00
		 { 343, 0xba }},
Packit Service cd2a00
	},
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
static const int tap_index[5][2] = {
Packit Service cd2a00
	/* left */
Packit Service cd2a00
	{ 0, 1 },
Packit Service cd2a00
	/* right */
Packit Service cd2a00
	{ 1, 0 },
Packit Service cd2a00
	/* rear left */
Packit Service cd2a00
	{ 2, 3 },
Packit Service cd2a00
	/* rear right */
Packit Service cd2a00
	{ 3, 2 },
Packit Service cd2a00
	/* center */
Packit Service cd2a00
	{ 4, 4 },
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
static inline void *area_addr(const snd_pcm_channel_area_t *area, 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 inline unsigned int area_step(const snd_pcm_channel_area_t *area)
Packit Service cd2a00
{
Packit Service cd2a00
	return area->step / 8;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static snd_pcm_sframes_t
Packit Service cd2a00
vdownmix_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_vdownmix_t *mix = (snd_pcm_vdownmix_t *)ext;
Packit Service cd2a00
	short *src[mix->channels], *ptr[2];
Packit Service cd2a00
	unsigned int src_step[mix->channels], step[2];
Packit Service cd2a00
	int i, ch, curpos, p, idx;
Packit Service cd2a00
	int acc[2];
Packit Service cd2a00
	int fr;
Packit Service cd2a00
Packit Service cd2a00
	ptr[0] = area_addr(dst_areas, dst_offset);
Packit Service cd2a00
	step[0] = area_step(dst_areas) / 2;
Packit Service cd2a00
	ptr[1] = area_addr(dst_areas + 1, dst_offset);
Packit Service cd2a00
	step[1] = area_step(dst_areas + 1) / 2;
Packit Service cd2a00
	for (ch = 0; ch < mix->channels; ch++) {
Packit Service cd2a00
		const snd_pcm_channel_area_t *src_area = &src_areas[ch];
Packit Service cd2a00
		src[ch] = area_addr(src_area, src_offset);
Packit Service cd2a00
		src_step[ch] = area_step(src_area) / 2;
Packit Service cd2a00
	}
Packit Service cd2a00
	curpos = mix->curpos;
Packit Service cd2a00
	fr = size;
Packit Service cd2a00
	while (fr--) {
Packit Service cd2a00
		acc[0] = acc[1] = 0;
Packit Service cd2a00
		for (ch = 0; ch < mix->channels; ch++) {
Packit Service cd2a00
			mix->rbuf[curpos][ch] = *src[ch];
Packit Service cd2a00
			for (idx = 0; idx < 2; idx++) {
Packit Service cd2a00
				int f = tap_index[ch][idx];
Packit Service cd2a00
				const struct vdownmix_filter *filter;
Packit Service cd2a00
				filter = &tap_filters[f];
Packit Service cd2a00
				for (i = 0; i < filter->taps; i++) {
Packit Service cd2a00
					p = (curpos + RINGBUF_SIZE - filter->tap[i].delay)
Packit Service cd2a00
						& RINGBUF_MASK;
Packit Service cd2a00
					acc[idx] += mix->rbuf[p][ch] * filter->tap[i].weight;
Packit Service cd2a00
				}
Packit Service cd2a00
			}
Packit Service cd2a00
			src[ch] += src_step[ch];
Packit Service cd2a00
		}
Packit Service cd2a00
		for (idx = 0; idx < 2; idx++) {
Packit Service cd2a00
			acc[idx] >>= 14;
Packit Service cd2a00
			if (acc[idx] < -32768)
Packit Service cd2a00
				*ptr[idx] = -32768;
Packit Service cd2a00
			else if (acc[idx] > 32767)
Packit Service cd2a00
				*ptr[idx] = 32767;
Packit Service cd2a00
			else
Packit Service cd2a00
				*ptr[idx] = acc[idx];
Packit Service cd2a00
			ptr[idx] += step[idx];
Packit Service cd2a00
		}
Packit Service cd2a00
		curpos = (curpos + 1) & RINGBUF_MASK;
Packit Service cd2a00
	}
Packit Service cd2a00
	mix->curpos = curpos;
Packit Service cd2a00
	return size;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
Packit Service cd2a00
static int vdownmix_init(snd_pcm_extplug_t *ext)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_pcm_vdownmix_t *mix = (snd_pcm_vdownmix_t *)ext;
Packit Service cd2a00
	mix->channels = ext->channels;
Packit Service cd2a00
	if (mix->channels > 5) /* ignore LFE */
Packit Service cd2a00
		mix->channels = 5;
Packit Service cd2a00
	mix->curpos = 0;
Packit Service cd2a00
	memset(mix->rbuf, 0, sizeof(mix->rbuf));
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
#if SND_PCM_EXTPLUG_VERSION >= 0x10002
Packit Service cd2a00
static unsigned int chmap[6] = {
Packit Service cd2a00
	SND_CHMAP_FL, SND_CHMAP_FR,
Packit Service cd2a00
	SND_CHMAP_RL, SND_CHMAP_RR,
Packit Service cd2a00
	SND_CHMAP_FC, SND_CHMAP_LFE,
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
static snd_pcm_chmap_query_t **vdownmix_query_chmaps(snd_pcm_extplug_t *ext ATTRIBUTE_UNUSED)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_pcm_chmap_query_t **maps;
Packit Service cd2a00
	int i;
Packit Service cd2a00
Packit Service cd2a00
	maps = calloc(4, sizeof(void *));
Packit Service cd2a00
	if (!maps)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	for (i = 0; i < 3; i++) {
Packit Service cd2a00
		snd_pcm_chmap_query_t *p;
Packit Service cd2a00
		p = maps[i] = calloc(i + 4 + 2, sizeof(int));
Packit Service cd2a00
		if (!p) {
Packit Service cd2a00
			snd_pcm_free_chmaps(maps);
Packit Service cd2a00
			return NULL;
Packit Service cd2a00
		}
Packit Service cd2a00
		p->type = SND_CHMAP_TYPE_FIXED;
Packit Service cd2a00
		p->map.channels = i + 4;
Packit Service cd2a00
		memcpy(p->map.pos, chmap, (i + 4) * sizeof(int));
Packit Service cd2a00
	}
Packit Service cd2a00
	return maps;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static snd_pcm_chmap_t *vdownmix_get_chmap(snd_pcm_extplug_t *ext)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_pcm_chmap_t *map;
Packit Service cd2a00
Packit Service cd2a00
	if (ext->channels < 4 || ext->channels > 6)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	map = malloc((ext->channels + 1) * sizeof(int));
Packit Service cd2a00
	if (!map)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	map->channels = ext->channels;
Packit Service cd2a00
	memcpy(map->pos, chmap, ext->channels * sizeof(int));
Packit Service cd2a00
	return map;
Packit Service cd2a00
}
Packit Service cd2a00
#endif /* SND_PCM_EXTPLUG_VERSION >= 0x10002 */
Packit Service cd2a00
Packit Service cd2a00
static const snd_pcm_extplug_callback_t vdownmix_callback = {
Packit Service cd2a00
	.transfer = vdownmix_transfer,
Packit Service cd2a00
	.init = vdownmix_init,
Packit Service cd2a00
	/* .dump = filr_dump, */
Packit Service cd2a00
#if SND_PCM_EXTPLUG_VERSION >= 0x10002
Packit Service cd2a00
	.query_chmaps = vdownmix_query_chmaps,
Packit Service cd2a00
	.get_chmap = vdownmix_get_chmap,
Packit Service cd2a00
#endif
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
SND_PCM_PLUGIN_DEFINE_FUNC(vdownmix)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_config_iterator_t i, next;
Packit Service cd2a00
	snd_pcm_vdownmix_t *mix;
Packit Service cd2a00
	snd_config_t *sconf = NULL;
Packit Service cd2a00
	int err;
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 || 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
		SNDERR("Unknown field %s", id);
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (! sconf) {
Packit Service cd2a00
		SNDERR("No slave configuration for vdownmix pcm");
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	mix = calloc(1, sizeof(*mix));
Packit Service cd2a00
	if (mix == NULL)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
Packit Service cd2a00
	mix->ext.version = SND_PCM_EXTPLUG_VERSION;
Packit Service cd2a00
	mix->ext.name = "Vdownmix Plugin";
Packit Service cd2a00
	mix->ext.callback = &vdownmix_callback;
Packit Service cd2a00
	mix->ext.private_data = mix;
Packit Service cd2a00
Packit Service cd2a00
	err = snd_pcm_extplug_create(&mix->ext, name, root, sconf, stream, mode);
Packit Service cd2a00
	if (err < 0) {
Packit Service cd2a00
		free(mix);
Packit Service cd2a00
		return err;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	/* 4/5/6 -> 2 downmix */
Packit Service cd2a00
	snd_pcm_extplug_set_param_minmax(&mix->ext, SND_PCM_EXTPLUG_HW_CHANNELS,
Packit Service cd2a00
					 4, 6);
Packit Service cd2a00
	snd_pcm_extplug_set_slave_param(&mix->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2);
Packit Service cd2a00
	snd_pcm_extplug_set_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
Packit Service cd2a00
				  SND_PCM_FORMAT_S16);
Packit Service cd2a00
	snd_pcm_extplug_set_slave_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
Packit Service cd2a00
					SND_PCM_FORMAT_S16);
Packit Service cd2a00
Packit Service cd2a00
	*pcmp = mix->ext.pcm;
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
SND_PCM_PLUGIN_SYMBOL(vdownmix);