|
Packit |
675970 |
/*
|
|
Packit |
675970 |
* Automatic upmix plugin
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* Copyright (c) 2006 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 <alsa/asoundlib.h>
|
|
Packit |
675970 |
#include <alsa/pcm_external.h>
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef struct snd_pcm_upmix snd_pcm_upmix_t;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef void (*upmixer_t)(snd_pcm_upmix_t *mix,
|
|
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 |
struct snd_pcm_upmix {
|
|
Packit |
675970 |
snd_pcm_extplug_t ext;
|
|
Packit |
675970 |
/* setup */
|
|
Packit |
675970 |
int delay_ms;
|
|
Packit |
675970 |
/* privates */
|
|
Packit |
675970 |
upmixer_t upmix;
|
|
Packit |
675970 |
unsigned int curpos;
|
|
Packit |
675970 |
int delay;
|
|
Packit |
675970 |
short *delayline[2];
|
|
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 inline unsigned int area_step(const snd_pcm_channel_area_t *area)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
return area->step / 8;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* Delayed copy SL & SR */
|
|
Packit |
675970 |
static void delayed_copy(snd_pcm_upmix_t *mix,
|
|
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 |
unsigned int size)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
unsigned int i, p, delay, curpos, dst_step, src_step;
|
|
Packit |
675970 |
short *dst, *src;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (! mix->delay_ms) {
|
|
Packit |
675970 |
snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
return;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
delay = mix->delay;
|
|
Packit |
675970 |
if (delay > size)
|
|
Packit |
675970 |
delay = size;
|
|
Packit |
675970 |
for (i = 0; i < 2; i++) {
|
|
Packit |
675970 |
dst = (short *)area_addr(dst_areas + i, dst_offset);
|
|
Packit |
675970 |
dst_step = area_step(dst_areas + i) / 2;
|
|
Packit |
675970 |
curpos = mix->curpos;
|
|
Packit |
675970 |
for (p = 0; p < delay; p++) {
|
|
Packit |
675970 |
*dst = mix->delayline[i][curpos];
|
|
Packit |
675970 |
dst += dst_step;
|
|
Packit |
675970 |
curpos = (curpos + 1) % mix->delay;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
snd_pcm_area_copy(dst_areas + i, dst_offset + delay,
|
|
Packit |
675970 |
src_areas + i, src_offset,
|
|
Packit |
675970 |
size - delay, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
src = (short *)area_addr(src_areas + i,
|
|
Packit |
675970 |
src_offset + size - delay);
|
|
Packit |
675970 |
src_step = area_step(src_areas + i) / 2;
|
|
Packit |
675970 |
curpos = mix->curpos;
|
|
Packit |
675970 |
for (p = 0; p < delay; p++) {
|
|
Packit |
675970 |
mix->delayline[i][curpos] = *src;
|
|
Packit |
675970 |
src += src_step;
|
|
Packit |
675970 |
curpos = (curpos + 1) % mix->delay;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
mix->curpos = curpos;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* Average of L+R -> C and LFE */
|
|
Packit |
675970 |
static void average_copy(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 |
unsigned int nchns,
|
|
Packit |
675970 |
unsigned int size)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
short *dst[2], *src[2];
|
|
Packit |
675970 |
unsigned int i, dst_step[2], src_step[2];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (i = 0; i < nchns; i++) {
|
|
Packit |
675970 |
dst[i] = (short *)area_addr(dst_areas + i, dst_offset);
|
|
Packit |
675970 |
dst_step[i] = area_step(dst_areas + i) / 2;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
for (i = 0; i < 2; i++) {
|
|
Packit |
675970 |
src[i] = (short *)area_addr(src_areas + i, src_offset);
|
|
Packit |
675970 |
src_step[i] = area_step(src_areas + i) / 2;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
while (size--) {
|
|
Packit |
675970 |
short val = (*src[0] >> 1) + (*src[1] >> 1);
|
|
Packit |
675970 |
for (i = 0; i < nchns; i++) {
|
|
Packit |
675970 |
*dst[i] = val;
|
|
Packit |
675970 |
dst[i] += dst_step[i];
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
src[0] += src_step[0];
|
|
Packit |
675970 |
src[1] += src_step[1];
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_1_to_71(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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 |
int i;
|
|
Packit |
675970 |
for (i = 0; i < 8; i++)
|
|
Packit |
675970 |
snd_pcm_area_copy(dst_areas + i, dst_offset,
|
|
Packit |
675970 |
src_areas, src_offset,
|
|
Packit |
675970 |
size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_1_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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 |
int i;
|
|
Packit |
675970 |
for (i = 0; i < 6; i++)
|
|
Packit |
675970 |
snd_pcm_area_copy(dst_areas + i, dst_offset,
|
|
Packit |
675970 |
src_areas, src_offset,
|
|
Packit |
675970 |
size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_1_to_40(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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 |
int i;
|
|
Packit |
675970 |
for (i = 0; i < 4; i++)
|
|
Packit |
675970 |
snd_pcm_area_copy(dst_areas + i, dst_offset,
|
|
Packit |
675970 |
src_areas, src_offset,
|
|
Packit |
675970 |
size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_2_to_71(snd_pcm_upmix_t *mix,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
size);
|
|
Packit |
675970 |
average_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size);
|
|
Packit |
675970 |
snd_pcm_areas_copy(dst_areas + 6, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_2_to_51(snd_pcm_upmix_t *mix,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
size);
|
|
Packit |
675970 |
average_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_2_to_40(snd_pcm_upmix_t *mix,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
size);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_3_to_51(snd_pcm_upmix_t *mix,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
size);
|
|
Packit |
675970 |
snd_pcm_areas_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_3_to_40(snd_pcm_upmix_t *mix,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
size);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_4_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
4, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
snd_pcm_areas_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
2, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_4_to_40(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
4, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_5_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
5, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
snd_pcm_area_copy(dst_areas + 5, dst_offset, src_areas + 4, src_offset,
|
|
Packit |
675970 |
size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_6_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
6, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void upmix_8_to_71(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
|
|
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_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
|
|
Packit |
675970 |
8, size, SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static const upmixer_t do_upmix[8][3] = {
|
|
Packit |
675970 |
{ upmix_1_to_40, upmix_1_to_51, upmix_1_to_71 },
|
|
Packit |
675970 |
{ upmix_2_to_40, upmix_2_to_51, upmix_2_to_71 },
|
|
Packit |
675970 |
{ upmix_3_to_40, upmix_3_to_51, upmix_3_to_51 },
|
|
Packit |
675970 |
{ upmix_4_to_40, upmix_4_to_51, upmix_4_to_51 },
|
|
Packit |
675970 |
{ upmix_4_to_40, upmix_5_to_51, upmix_5_to_51 },
|
|
Packit |
675970 |
{ upmix_4_to_40, upmix_6_to_51, upmix_6_to_51 },
|
|
Packit |
675970 |
{ upmix_4_to_40, upmix_6_to_51, upmix_6_to_51 },
|
|
Packit |
675970 |
{ upmix_4_to_40, upmix_6_to_51, upmix_8_to_71 },
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t
|
|
Packit |
675970 |
upmix_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_upmix_t *mix = (snd_pcm_upmix_t *)ext;
|
|
Packit |
675970 |
mix->upmix(mix, dst_areas, dst_offset,
|
|
Packit |
675970 |
src_areas, src_offset, size);
|
|
Packit |
675970 |
return size;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int upmix_init(snd_pcm_extplug_t *ext)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_upmix_t *mix = (snd_pcm_upmix_t *)ext;
|
|
Packit |
675970 |
int ctype, stype;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
switch (ext->slave_channels) {
|
|
Packit |
675970 |
case 6:
|
|
Packit |
675970 |
stype = 1;
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
case 8:
|
|
Packit |
675970 |
stype = 2;
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
default:
|
|
Packit |
675970 |
stype = 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
ctype = ext->channels - 1;
|
|
Packit |
675970 |
if (ctype < 0 || ctype > 7) {
|
|
Packit |
675970 |
SNDERR("Invalid channel numbers for upmix: %d", ctype + 1);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
mix->upmix = do_upmix[ctype][stype];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (mix->delay_ms) {
|
|
Packit |
675970 |
free(mix->delayline[0]);
|
|
Packit |
675970 |
free(mix->delayline[1]);
|
|
Packit |
675970 |
mix->delay = ext->rate * mix->delay_ms / 1000;
|
|
Packit |
675970 |
mix->delayline[0] = calloc(2, mix->delay);
|
|
Packit |
675970 |
mix->delayline[1] = calloc(2, mix->delay);
|
|
Packit |
675970 |
if (! mix->delayline[0] || ! mix->delayline[1])
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
mix->curpos = 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int upmix_close(snd_pcm_extplug_t *ext)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_upmix_t *mix = (snd_pcm_upmix_t *)ext;
|
|
Packit |
675970 |
free(mix->delayline[0]);
|
|
Packit |
675970 |
free(mix->delayline[1]);
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#if SND_PCM_EXTPLUG_VERSION >= 0x10002
|
|
Packit |
675970 |
static unsigned int chmap[8][8] = {
|
|
Packit |
675970 |
{ SND_CHMAP_MONO },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_UNKNOWN },
|
|
Packit |
675970 |
{ SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_SL, SND_CHMAP_SR },
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_chmap_query_t **upmix_query_chmaps(snd_pcm_extplug_t *ext ATTRIBUTE_UNUSED)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_chmap_query_t **maps;
|
|
Packit |
675970 |
int i;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
maps = calloc(9, sizeof(void *));
|
|
Packit |
675970 |
if (!maps)
|
|
Packit |
675970 |
return NULL;
|
|
Packit |
675970 |
for (i = 0; i < 8; i++) {
|
|
Packit |
675970 |
snd_pcm_chmap_query_t *p;
|
|
Packit |
675970 |
p = maps[i] = calloc(i + 1 + 2, sizeof(int));
|
|
Packit |
675970 |
if (!p) {
|
|
Packit |
675970 |
snd_pcm_free_chmaps(maps);
|
|
Packit |
675970 |
return NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
p->type = SND_CHMAP_TYPE_FIXED;
|
|
Packit |
675970 |
p->map.channels = i + 1;
|
|
Packit |
675970 |
memcpy(p->map.pos, &chmap[i][0], (i + 1) * sizeof(int));
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
return maps;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_chmap_t *upmix_get_chmap(snd_pcm_extplug_t *ext)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_chmap_t *map;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (ext->channels < 1 || ext->channels > 8)
|
|
Packit |
675970 |
return NULL;
|
|
Packit |
675970 |
map = malloc((ext->channels + 1) * sizeof(int));
|
|
Packit |
675970 |
if (!map)
|
|
Packit |
675970 |
return NULL;
|
|
Packit |
675970 |
map->channels = ext->channels;
|
|
Packit |
675970 |
memcpy(map->pos, &chmap[ext->channels - 1][0], ext->channels * sizeof(int));
|
|
Packit |
675970 |
return map;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
#endif /* SND_PCM_EXTPLUG_VERSION >= 0x10002 */
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static const snd_pcm_extplug_callback_t upmix_callback = {
|
|
Packit |
675970 |
.transfer = upmix_transfer,
|
|
Packit |
675970 |
.init = upmix_init,
|
|
Packit |
675970 |
.close = upmix_close,
|
|
Packit |
675970 |
#if SND_PCM_EXTPLUG_VERSION >= 0x10002
|
|
Packit |
675970 |
.query_chmaps = upmix_query_chmaps,
|
|
Packit |
675970 |
.get_chmap = upmix_get_chmap,
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_DEFINE_FUNC(upmix)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_config_iterator_t i, next;
|
|
Packit |
675970 |
snd_pcm_upmix_t *mix;
|
|
Packit |
675970 |
snd_config_t *sconf = NULL;
|
|
Packit |
675970 |
static const unsigned int chlist[3] = {4, 6, 8};
|
|
Packit |
675970 |
unsigned int channels = 0;
|
|
Packit |
675970 |
int delay = 10;
|
|
Packit |
675970 |
int err;
|
|
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 || 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 |
if (strcmp(id, "delay") == 0) {
|
|
Packit |
675970 |
long val;
|
|
Packit |
675970 |
err = snd_config_get_integer(n, &val;;
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
SNDERR("Invalid value for %s", id);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
delay = val;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (strcmp(id, "channels") == 0) {
|
|
Packit |
675970 |
long val;
|
|
Packit |
675970 |
err = snd_config_get_integer(n, &val;;
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
SNDERR("Invalid value for %s", id);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
channels = val;
|
|
Packit |
675970 |
if (channels != 4 && channels != 6 && channels != 0 && channels != 8) {
|
|
Packit |
675970 |
SNDERR("channels must be 4, 6, 8 or 0");
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
SNDERR("Unknown field %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (! sconf) {
|
|
Packit |
675970 |
SNDERR("No slave configuration for filrmix pcm");
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
mix = calloc(1, sizeof(*mix));
|
|
Packit |
675970 |
if (mix == NULL)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
mix->ext.version = SND_PCM_EXTPLUG_VERSION;
|
|
Packit |
675970 |
mix->ext.name = "Upmix Plugin";
|
|
Packit |
675970 |
mix->ext.callback = &upmix_callback;
|
|
Packit |
675970 |
mix->ext.private_data = mix;
|
|
Packit |
675970 |
if (delay < 0)
|
|
Packit |
675970 |
delay = 0;
|
|
Packit |
675970 |
else if (delay > 1000)
|
|
Packit |
675970 |
delay = 1000;
|
|
Packit |
675970 |
mix->delay_ms = delay;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_extplug_create(&mix->ext, name, root, sconf, stream, mode);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
free(mix);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_pcm_extplug_set_param_minmax(&mix->ext,
|
|
Packit |
675970 |
SND_PCM_EXTPLUG_HW_CHANNELS,
|
|
Packit |
675970 |
1, 8);
|
|
Packit |
675970 |
if (channels)
|
|
Packit |
675970 |
snd_pcm_extplug_set_slave_param_minmax(&mix->ext,
|
|
Packit |
675970 |
SND_PCM_EXTPLUG_HW_CHANNELS,
|
|
Packit |
675970 |
channels, channels);
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
snd_pcm_extplug_set_slave_param_list(&mix->ext,
|
|
Packit |
675970 |
SND_PCM_EXTPLUG_HW_CHANNELS,
|
|
Packit |
675970 |
3, chlist);
|
|
Packit |
675970 |
snd_pcm_extplug_set_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
|
|
Packit |
675970 |
SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
snd_pcm_extplug_set_slave_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
|
|
Packit |
675970 |
SND_PCM_FORMAT_S16);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
*pcmp = mix->ext.pcm;
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_SYMBOL(upmix);
|