2002-01-16 16:42:40 +00:00
|
|
|
/**
|
|
|
|
|
* \file pcm/pcm_route.c
|
|
|
|
|
* \ingroup PCM_Plugins
|
|
|
|
|
* \brief PCM Route & Volume Plugin Interface
|
|
|
|
|
* \author Abramo Bagnara <abramo@alsa-project.org>
|
|
|
|
|
* \date 2000-2001
|
|
|
|
|
*/
|
2000-09-24 09:57:26 +00:00
|
|
|
/*
|
2002-01-16 16:42:40 +00:00
|
|
|
* PCM - Route & Volume Plugin
|
2000-09-24 09:57:26 +00:00
|
|
|
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
2001-12-30 09:22:54 +00:00
|
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
* published by the Free Software Foundation; either version 2.1 of
|
2000-09-24 09:57:26 +00:00
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2001-12-30 09:22:54 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2000-09-24 09:57:26 +00:00
|
|
|
*
|
2001-12-30 09:22:54 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2000-09-24 09:57:26 +00:00
|
|
|
* License along with this library; if not, write to the Free Software
|
2001-12-30 09:22:54 +00:00
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2000-09-24 09:57:26 +00:00
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2016-03-03 12:16:57 +01:00
|
|
|
#include "bswap.h"
|
2000-09-24 09:57:26 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
#include "pcm_local.h"
|
|
|
|
|
#include "pcm_plugin.h"
|
|
|
|
|
|
2006-01-09 14:40:08 +00:00
|
|
|
#include "plugin_ops.h"
|
|
|
|
|
|
2001-10-24 14:14:11 +00:00
|
|
|
#ifndef PIC
|
|
|
|
|
/* entry for static linking */
|
|
|
|
|
const char *_snd_module_pcm_route = "";
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
|
|
|
|
|
#define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
|
|
|
|
|
#elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
|
2000-09-24 09:57:26 +00:00
|
|
|
#define div(a) a >>= 4
|
|
|
|
|
#else
|
|
|
|
|
#error "Add some code here"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
int channel;
|
|
|
|
|
int as_int;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
float as_float;
|
|
|
|
|
#endif
|
2001-01-26 09:56:30 +00:00
|
|
|
} snd_pcm_route_ttable_src_t;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-01-26 09:56:30 +00:00
|
|
|
typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
typedef struct {
|
2014-07-22 11:55:40 +02:00
|
|
|
enum {UINT64, FLOAT} sum_idx;
|
2001-03-29 17:50:28 +00:00
|
|
|
unsigned int get_idx;
|
|
|
|
|
unsigned int put_idx;
|
|
|
|
|
unsigned int conv_idx;
|
2002-06-26 02:04:11 +00:00
|
|
|
int use_getput;
|
2001-03-29 17:50:28 +00:00
|
|
|
unsigned int src_size;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t dst_sfmt;
|
2012-09-12 15:09:57 +02:00
|
|
|
unsigned int nsrcs;
|
2001-01-15 11:06:53 +00:00
|
|
|
unsigned int ndsts;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_dst_t *dsts;
|
|
|
|
|
} snd_pcm_route_params_t;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
|
2001-01-31 17:26:56 +00:00
|
|
|
typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t dst_offset,
|
2001-01-31 17:26:56 +00:00
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels,
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
2001-01-26 09:56:30 +00:00
|
|
|
const snd_pcm_route_ttable_dst_t *ttable,
|
|
|
|
|
const snd_pcm_route_params_t *params);
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-01-26 09:56:30 +00:00
|
|
|
struct snd_pcm_route_ttable_dst {
|
2000-09-24 09:57:26 +00:00
|
|
|
int att; /* Attenuated */
|
|
|
|
|
unsigned int nsrcs;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_src_t* srcs;
|
2000-09-24 09:57:26 +00:00
|
|
|
route_f func;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef union {
|
2001-10-25 18:24:22 +00:00
|
|
|
int32_t as_sint32;
|
|
|
|
|
int64_t as_sint64;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
float as_float;
|
|
|
|
|
#endif
|
|
|
|
|
} sum_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
/* This field need to be the first */
|
|
|
|
|
snd_pcm_plugin_t plug;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t sformat;
|
2000-09-24 09:57:26 +00:00
|
|
|
int schannels;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_params_t params;
|
2014-02-28 08:57:06 +01:00
|
|
|
snd_pcm_chmap_t *chmap;
|
2000-09-24 09:57:26 +00:00
|
|
|
} snd_pcm_route_t;
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
#endif /* DOC_HIDDEN */
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
|
|
|
|
|
snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels ATTRIBUTE_UNUSED,
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
|
|
|
|
const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
|
|
|
|
|
const snd_pcm_route_params_t *params)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels,
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
|
|
|
|
const snd_pcm_route_ttable_dst_t* ttable,
|
|
|
|
|
const snd_pcm_route_params_t *params)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
#define CONV_LABELS
|
|
|
|
|
#include "plugin_ops.h"
|
|
|
|
|
#undef CONV_LABELS
|
|
|
|
|
void *conv;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *src_area = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int srcidx;
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *src;
|
|
|
|
|
char *dst;
|
2000-09-24 09:57:26 +00:00
|
|
|
int src_step, dst_step;
|
2003-08-12 12:30:53 +00:00
|
|
|
for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
|
|
|
|
|
unsigned int channel = ttable->srcs[srcidx].channel;
|
|
|
|
|
if (channel >= src_channels)
|
|
|
|
|
continue;
|
|
|
|
|
src_area = &src_areas[channel];
|
2000-09-24 09:57:26 +00:00
|
|
|
if (src_area->addr != NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-08-12 12:30:53 +00:00
|
|
|
if (srcidx == ttable->nsrcs || srcidx == src_channels) {
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_route_convert1_zero(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2001-01-31 17:26:56 +00:00
|
|
|
frames, ttable, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conv = conv_labels[params->conv_idx];
|
|
|
|
|
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
src_step = snd_pcm_channel_area_step(src_area);
|
|
|
|
|
dst_step = snd_pcm_channel_area_step(dst_area);
|
|
|
|
|
while (frames-- > 0) {
|
|
|
|
|
goto *conv;
|
|
|
|
|
#define CONV_END after
|
|
|
|
|
#include "plugin_ops.h"
|
|
|
|
|
#undef CONV_END
|
|
|
|
|
after:
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-06-26 02:04:11 +00:00
|
|
|
static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels,
|
2002-06-26 02:04:11 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
|
|
|
|
const snd_pcm_route_ttable_dst_t* ttable,
|
|
|
|
|
const snd_pcm_route_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
#define CONV24_LABELS
|
|
|
|
|
#include "plugin_ops.h"
|
|
|
|
|
#undef CONV24_LABELS
|
|
|
|
|
void *get, *put;
|
|
|
|
|
const snd_pcm_channel_area_t *src_area = 0;
|
|
|
|
|
unsigned int srcidx;
|
|
|
|
|
const char *src;
|
|
|
|
|
char *dst;
|
|
|
|
|
int src_step, dst_step;
|
2017-07-14 16:18:11 +02:00
|
|
|
uint32_t sample = 0;
|
2003-08-12 12:30:53 +00:00
|
|
|
for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
|
|
|
|
|
unsigned int channel = ttable->srcs[srcidx].channel;
|
|
|
|
|
if (channel >= src_channels)
|
|
|
|
|
continue;
|
|
|
|
|
src_area = &src_areas[channel];
|
2002-06-26 02:04:11 +00:00
|
|
|
if (src_area->addr != NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-08-12 12:30:53 +00:00
|
|
|
if (srcidx == ttable->nsrcs || srcidx == src_channels) {
|
2002-06-26 02:04:11 +00:00
|
|
|
snd_pcm_route_convert1_zero(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2002-06-26 02:04:11 +00:00
|
|
|
frames, ttable, params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get = get32_labels[params->get_idx];
|
|
|
|
|
put = put32_labels[params->put_idx];
|
|
|
|
|
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
src_step = snd_pcm_channel_area_step(src_area);
|
|
|
|
|
dst_step = snd_pcm_channel_area_step(dst_area);
|
|
|
|
|
while (frames-- > 0) {
|
|
|
|
|
goto *get;
|
|
|
|
|
#define CONV24_END after
|
|
|
|
|
#include "plugin_ops.h"
|
|
|
|
|
#undef CONV24_END
|
|
|
|
|
after:
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels,
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
|
|
|
|
const snd_pcm_route_ttable_dst_t* ttable,
|
|
|
|
|
const snd_pcm_route_params_t *params)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2014-07-22 11:55:40 +02:00
|
|
|
#define GET32_LABELS
|
2000-10-22 09:50:20 +00:00
|
|
|
#define PUT32_LABELS
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2014-07-22 11:55:40 +02:00
|
|
|
#undef GET32_LABELS
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef PUT32_LABELS
|
2014-07-22 11:55:40 +02:00
|
|
|
static void *const zero_labels[2] = {
|
|
|
|
|
&&zero_int64,
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2001-03-17 16:34:43 +00:00
|
|
|
&&zero_float
|
2000-09-24 09:57:26 +00:00
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
/* sum_type att */
|
2014-07-22 11:55:40 +02:00
|
|
|
static void *const add_labels[2 * 2] = {
|
2001-03-17 16:34:43 +00:00
|
|
|
&&add_int64_noatt, &&add_int64_att,
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2001-03-17 16:34:43 +00:00
|
|
|
&&add_float_noatt, &&add_float_att
|
2000-09-24 09:57:26 +00:00
|
|
|
#endif
|
|
|
|
|
};
|
2014-07-22 11:55:40 +02:00
|
|
|
/* sum_type att */
|
|
|
|
|
static void *const norm_labels[2 * 2] = {
|
|
|
|
|
&&norm_int64_noatt,
|
|
|
|
|
&&norm_int64_att,
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2014-07-22 11:55:40 +02:00
|
|
|
&&norm_float,
|
|
|
|
|
&&norm_float,
|
2000-09-24 09:57:26 +00:00
|
|
|
#endif
|
|
|
|
|
};
|
2014-07-22 11:55:40 +02:00
|
|
|
void *zero, *get32, *add, *norm, *put32;
|
2000-09-24 09:57:26 +00:00
|
|
|
int nsrcs = ttable->nsrcs;
|
|
|
|
|
char *dst;
|
|
|
|
|
int dst_step;
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *srcs[nsrcs];
|
2000-09-24 09:57:26 +00:00
|
|
|
int src_steps[nsrcs];
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_src_t src_tt[nsrcs];
|
2001-10-25 18:24:22 +00:00
|
|
|
int32_t sample = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
int srcidx, srcidx1 = 0;
|
2003-08-12 12:30:53 +00:00
|
|
|
for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
|
|
|
|
|
const snd_pcm_channel_area_t *src_area;
|
|
|
|
|
unsigned int channel = ttable->srcs[srcidx].channel;
|
|
|
|
|
if (channel >= src_channels)
|
|
|
|
|
continue;
|
|
|
|
|
src_area = &src_areas[channel];
|
2000-09-24 09:57:26 +00:00
|
|
|
srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
|
|
|
|
|
src_tt[srcidx1] = ttable->srcs[srcidx];
|
|
|
|
|
srcidx1++;
|
|
|
|
|
}
|
|
|
|
|
nsrcs = srcidx1;
|
|
|
|
|
if (nsrcs == 0) {
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_route_convert1_zero(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2001-01-31 17:26:56 +00:00
|
|
|
frames, ttable, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
return;
|
2002-01-12 10:52:42 +00:00
|
|
|
} else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
|
2002-06-26 02:04:11 +00:00
|
|
|
if (params->use_getput)
|
|
|
|
|
snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2002-06-26 02:04:11 +00:00
|
|
|
frames, ttable, params);
|
|
|
|
|
else
|
|
|
|
|
snd_pcm_route_convert1_one(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2002-06-26 02:04:11 +00:00
|
|
|
frames, ttable, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
zero = zero_labels[params->sum_idx];
|
2014-07-22 11:55:40 +02:00
|
|
|
get32 = get32_labels[params->get_idx];
|
2000-09-24 09:57:26 +00:00
|
|
|
add = add_labels[params->sum_idx * 2 + ttable->att];
|
2014-07-22 11:55:40 +02:00
|
|
|
norm = norm_labels[params->sum_idx * 2 + ttable->att];
|
2000-10-22 09:50:20 +00:00
|
|
|
put32 = put32_labels[params->put_idx];
|
2000-09-24 09:57:26 +00:00
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
dst_step = snd_pcm_channel_area_step(dst_area);
|
|
|
|
|
|
|
|
|
|
while (frames-- > 0) {
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_src_t *ttp = src_tt;
|
2000-09-24 09:57:26 +00:00
|
|
|
sum_t sum;
|
|
|
|
|
|
|
|
|
|
/* Zero sum */
|
|
|
|
|
goto *zero;
|
|
|
|
|
zero_int64:
|
2001-10-25 18:24:22 +00:00
|
|
|
sum.as_sint64 = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
goto zero_end;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
zero_float:
|
|
|
|
|
sum.as_float = 0.0;
|
|
|
|
|
goto zero_end;
|
|
|
|
|
#endif
|
|
|
|
|
zero_end:
|
|
|
|
|
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *src = srcs[srcidx];
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
/* Get sample */
|
2014-07-22 11:55:40 +02:00
|
|
|
goto *get32;
|
|
|
|
|
#define GET32_END after_get
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2014-07-22 11:55:40 +02:00
|
|
|
#undef GET32_END
|
2000-09-24 09:57:26 +00:00
|
|
|
after_get:
|
|
|
|
|
|
|
|
|
|
/* Sum */
|
|
|
|
|
goto *add;
|
|
|
|
|
add_int64_att:
|
2002-07-11 09:03:47 +00:00
|
|
|
sum.as_sint64 += (int64_t) sample * ttp->as_int;
|
2000-09-24 09:57:26 +00:00
|
|
|
goto after_sum;
|
|
|
|
|
add_int64_noatt:
|
|
|
|
|
if (ttp->as_int)
|
2001-10-25 18:24:22 +00:00
|
|
|
sum.as_sint64 += sample;
|
2000-09-24 09:57:26 +00:00
|
|
|
goto after_sum;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
add_float_att:
|
|
|
|
|
sum.as_float += sample * ttp->as_float;
|
|
|
|
|
goto after_sum;
|
|
|
|
|
add_float_noatt:
|
|
|
|
|
if (ttp->as_int)
|
|
|
|
|
sum.as_float += sample;
|
|
|
|
|
goto after_sum;
|
|
|
|
|
#endif
|
|
|
|
|
after_sum:
|
|
|
|
|
srcs[srcidx] += src_steps[srcidx];
|
|
|
|
|
ttp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Normalization */
|
|
|
|
|
goto *norm;
|
2014-07-22 11:55:40 +02:00
|
|
|
norm_int64_att:
|
2001-10-25 18:24:22 +00:00
|
|
|
div(sum.as_sint64);
|
2014-07-22 11:55:40 +02:00
|
|
|
/* fallthru */
|
|
|
|
|
norm_int64_noatt:
|
2002-07-10 12:13:45 +00:00
|
|
|
if (sum.as_sint64 > (int64_t)0x7fffffff)
|
|
|
|
|
sample = 0x7fffffff; /* maximum positive value */
|
2002-07-11 09:03:47 +00:00
|
|
|
else if (sum.as_sint64 < -(int64_t)0x80000000)
|
2002-07-10 12:13:45 +00:00
|
|
|
sample = 0x80000000; /* maximum negative value */
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
2001-10-25 18:24:22 +00:00
|
|
|
sample = sum.as_sint64;
|
2000-09-24 09:57:26 +00:00
|
|
|
goto after_norm;
|
|
|
|
|
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
norm_float:
|
2002-07-11 08:01:58 +00:00
|
|
|
sum.as_float = rint(sum.as_float);
|
2002-07-10 12:13:45 +00:00
|
|
|
if (sum.as_float > (int64_t)0x7fffffff)
|
|
|
|
|
sample = 0x7fffffff; /* maximum positive value */
|
2002-07-11 09:03:47 +00:00
|
|
|
else if (sum.as_float < -(int64_t)0x80000000)
|
2002-07-10 12:13:45 +00:00
|
|
|
sample = 0x80000000; /* maximum negative value */
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
|
|
|
|
sample = sum.as_float;
|
|
|
|
|
goto after_norm;
|
|
|
|
|
#endif
|
|
|
|
|
after_norm:
|
|
|
|
|
|
|
|
|
|
/* Put sample */
|
2000-10-22 09:50:20 +00:00
|
|
|
goto *put32;
|
|
|
|
|
#define PUT32_END after_put32
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef PUT32_END
|
|
|
|
|
after_put32:
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
#endif /* DOC_HIDDEN */
|
|
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
unsigned int src_channels,
|
|
|
|
|
unsigned int dst_channels,
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_pcm_uframes_t frames,
|
|
|
|
|
snd_pcm_route_params_t *params)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-01-15 11:06:53 +00:00
|
|
|
unsigned int dst_channel;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_dst_t *dstp;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *dst_area;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
dstp = params->dsts;
|
|
|
|
|
dst_area = dst_areas;
|
|
|
|
|
for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
|
|
|
|
|
if (dst_channel >= params->ndsts)
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_route_convert1_zero(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2001-01-31 17:26:56 +00:00
|
|
|
frames, dstp, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
2001-01-31 17:26:56 +00:00
|
|
|
dstp->func(dst_area, dst_offset,
|
|
|
|
|
src_areas, src_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
src_channels,
|
2001-01-31 17:26:56 +00:00
|
|
|
frames, dstp, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
dstp++;
|
|
|
|
|
dst_area++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_route_close(snd_pcm_t *pcm)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_params_t *params = &route->params;
|
2001-01-15 11:06:53 +00:00
|
|
|
unsigned int dst_channel;
|
2005-01-20 15:07:51 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
if (params->dsts) {
|
|
|
|
|
for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
|
2006-02-27 09:58:32 +00:00
|
|
|
free(params->dsts[dst_channel].srcs);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
free(params->dsts);
|
|
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
free(route->chmap);
|
2005-01-20 15:07:51 +00:00
|
|
|
return snd_pcm_generic_close(pcm);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
int err;
|
2001-03-21 16:31:31 +00:00
|
|
|
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
|
2001-02-04 18:57:05 +00:00
|
|
|
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
2001-02-04 18:57:05 +00:00
|
|
|
&access_mask);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
|
2001-02-04 18:57:05 +00:00
|
|
|
&format_mask);
|
2001-01-18 18:20:31 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-01-18 18:20:31 +00:00
|
|
|
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-12-21 20:44:10 +00:00
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2001-02-04 18:57:05 +00:00
|
|
|
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
|
2001-01-18 18:20:31 +00:00
|
|
|
_snd_pcm_hw_params_any(sparams);
|
2001-02-04 17:03:17 +00:00
|
|
|
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
2001-02-04 18:57:05 +00:00
|
|
|
&saccess_mask);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
|
2001-02-04 17:03:17 +00:00
|
|
|
_snd_pcm_hw_params_set_format(sparams, route->sformat);
|
|
|
|
|
_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
|
2001-01-18 18:20:31 +00:00
|
|
|
}
|
|
|
|
|
if (route->schannels >= 0) {
|
|
|
|
|
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
|
2001-03-29 17:50:28 +00:00
|
|
|
(unsigned int) route->schannels, 0);
|
2001-01-18 18:20:31 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
|
|
|
|
|
snd_pcm_hw_params_t *sparams)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2001-01-18 18:20:31 +00:00
|
|
|
int err;
|
|
|
|
|
unsigned int links = (SND_PCM_HW_PARBIT_RATE |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIODS |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
|
|
|
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
|
|
|
|
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
|
|
|
SND_PCM_HW_PARBIT_TICK_TIME);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
|
2000-12-21 20:44:10 +00:00
|
|
|
links |= (SND_PCM_HW_PARBIT_FORMAT |
|
|
|
|
|
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
|
|
|
SND_PCM_HW_PARBIT_SAMPLE_BITS);
|
2001-01-18 18:20:31 +00:00
|
|
|
if (route->schannels < 0)
|
2000-12-21 20:44:10 +00:00
|
|
|
links |= SND_PCM_HW_PARBIT_CHANNELS;
|
2001-01-18 18:20:31 +00:00
|
|
|
err = _snd_pcm_hw_params_refine(sparams, links, params);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-18 18:20:31 +00:00
|
|
|
|
|
|
|
|
static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
|
|
|
|
|
snd_pcm_hw_params_t *sparams)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2000-09-24 09:57:26 +00:00
|
|
|
int err;
|
2001-01-15 15:15:24 +00:00
|
|
|
unsigned int links = (SND_PCM_HW_PARBIT_RATE |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIODS |
|
2001-01-15 11:06:53 +00:00
|
|
|
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
2000-12-21 20:44:10 +00:00
|
|
|
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
2001-01-15 11:06:53 +00:00
|
|
|
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
|
|
|
SND_PCM_HW_PARBIT_TICK_TIME);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
|
2000-12-21 20:44:10 +00:00
|
|
|
links |= (SND_PCM_HW_PARBIT_FORMAT |
|
|
|
|
|
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
|
|
|
SND_PCM_HW_PARBIT_SAMPLE_BITS);
|
2001-01-18 18:20:31 +00:00
|
|
|
if (route->schannels < 0)
|
2000-12-21 20:44:10 +00:00
|
|
|
links |= SND_PCM_HW_PARBIT_CHANNELS;
|
2001-01-18 18:20:31 +00:00
|
|
|
err = _snd_pcm_hw_params_refine(params, links, sparams);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
return snd_pcm_hw_refine_slave(pcm, params,
|
|
|
|
|
snd_pcm_route_hw_refine_cprepare,
|
|
|
|
|
snd_pcm_route_hw_refine_cchange,
|
|
|
|
|
snd_pcm_route_hw_refine_sprepare,
|
|
|
|
|
snd_pcm_route_hw_refine_schange,
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_generic_hw_refine);
|
2001-01-18 18:20:31 +00:00
|
|
|
}
|
2000-12-21 20:44:10 +00:00
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_t *slave = route->plug.gen.slave;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t src_format, dst_format;
|
2001-01-18 18:20:31 +00:00
|
|
|
int err = snd_pcm_hw_params_slave(pcm, params,
|
|
|
|
|
snd_pcm_route_hw_refine_cchange,
|
|
|
|
|
snd_pcm_route_hw_refine_sprepare,
|
|
|
|
|
snd_pcm_route_hw_refine_schange,
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_generic_hw_params);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-01-18 18:20:31 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
2002-09-17 12:33:05 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
|
2000-11-20 20:10:46 +00:00
|
|
|
dst_format = slave->format;
|
2000-09-24 09:57:26 +00:00
|
|
|
} else {
|
2000-11-20 20:10:46 +00:00
|
|
|
src_format = slave->format;
|
2002-09-17 12:33:05 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2002-09-09 19:47:50 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2014-07-21 16:30:54 +02:00
|
|
|
/* 3 bytes formats? */
|
|
|
|
|
route->params.use_getput =
|
|
|
|
|
(snd_pcm_format_physical_width(src_format) + 7) / 3 == 3 ||
|
|
|
|
|
(snd_pcm_format_physical_width(dst_format) + 7) / 3 == 3;
|
2014-07-22 12:20:50 +02:00
|
|
|
route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32);
|
|
|
|
|
route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, dst_format);
|
2001-01-26 09:56:30 +00:00
|
|
|
route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
|
2000-09-24 09:57:26 +00:00
|
|
|
route->params.src_size = snd_pcm_format_width(src_format) / 8;
|
|
|
|
|
route->params.dst_sfmt = dst_format;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
route->params.sum_idx = FLOAT;
|
|
|
|
|
#else
|
2014-07-22 11:55:40 +02:00
|
|
|
route->params.sum_idx = UINT64;
|
2000-09-24 09:57:26 +00:00
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-02-27 13:42:12 +00:00
|
|
|
static snd_pcm_uframes_t
|
|
|
|
|
snd_pcm_route_write_areas(snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_channel_area_t *areas,
|
|
|
|
|
snd_pcm_uframes_t offset,
|
|
|
|
|
snd_pcm_uframes_t size,
|
|
|
|
|
const snd_pcm_channel_area_t *slave_areas,
|
|
|
|
|
snd_pcm_uframes_t slave_offset,
|
|
|
|
|
snd_pcm_uframes_t *slave_sizep)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_t *slave = route->plug.gen.slave;
|
2001-02-27 13:42:12 +00:00
|
|
|
if (size > *slave_sizep)
|
2000-09-24 09:57:26 +00:00
|
|
|
size = *slave_sizep;
|
2001-02-27 13:42:12 +00:00
|
|
|
snd_pcm_route_convert(slave_areas, slave_offset,
|
|
|
|
|
areas, offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
pcm->channels,
|
|
|
|
|
slave->channels,
|
|
|
|
|
size, &route->params);
|
2001-02-27 13:42:12 +00:00
|
|
|
*slave_sizep = size;
|
|
|
|
|
return size;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2001-02-27 13:42:12 +00:00
|
|
|
static snd_pcm_uframes_t
|
|
|
|
|
snd_pcm_route_read_areas(snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_channel_area_t *areas,
|
|
|
|
|
snd_pcm_uframes_t offset,
|
|
|
|
|
snd_pcm_uframes_t size,
|
|
|
|
|
const snd_pcm_channel_area_t *slave_areas,
|
|
|
|
|
snd_pcm_uframes_t slave_offset,
|
|
|
|
|
snd_pcm_uframes_t *slave_sizep)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_t *slave = route->plug.gen.slave;
|
2001-02-27 13:42:12 +00:00
|
|
|
if (size > *slave_sizep)
|
2000-09-24 09:57:26 +00:00
|
|
|
size = *slave_sizep;
|
2001-02-27 13:42:12 +00:00
|
|
|
snd_pcm_route_convert(areas, offset,
|
|
|
|
|
slave_areas, slave_offset,
|
2003-08-12 12:30:53 +00:00
|
|
|
slave->channels,
|
|
|
|
|
pcm->channels,
|
|
|
|
|
size, &route->params);
|
2001-02-27 13:42:12 +00:00
|
|
|
*slave_sizep = size;
|
|
|
|
|
return size;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-04 17:26:43 +02:00
|
|
|
static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
|
2012-07-25 15:36:16 +02:00
|
|
|
{
|
|
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2012-09-04 17:26:43 +02:00
|
|
|
snd_pcm_chmap_t *map, *slave_map;
|
2012-09-12 15:09:57 +02:00
|
|
|
unsigned int src, dst, nsrcs;
|
2012-07-25 15:36:16 +02:00
|
|
|
|
|
|
|
|
slave_map = snd_pcm_generic_get_chmap(pcm);
|
|
|
|
|
if (!slave_map)
|
|
|
|
|
return NULL;
|
2012-09-12 15:09:57 +02:00
|
|
|
nsrcs = route->params.nsrcs;
|
|
|
|
|
map = calloc(4, nsrcs + 1);
|
2012-07-25 15:36:16 +02:00
|
|
|
if (!map) {
|
|
|
|
|
free(slave_map);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2012-09-12 15:09:57 +02:00
|
|
|
map->channels = nsrcs;
|
2012-09-20 13:43:12 +02:00
|
|
|
for (src = 0; src < nsrcs; src++)
|
|
|
|
|
map->pos[src] = SND_CHMAP_NA;
|
2012-07-25 15:36:16 +02:00
|
|
|
for (dst = 0; dst < route->params.ndsts; dst++) {
|
|
|
|
|
snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
|
|
|
|
|
for (src = 0; src < d->nsrcs; src++) {
|
2012-10-30 13:07:48 +01:00
|
|
|
unsigned int c = d->srcs[src].channel;
|
2012-09-20 13:43:12 +02:00
|
|
|
if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
|
2012-09-04 17:26:43 +02:00
|
|
|
map->pos[c] = slave_map->pos[dst];
|
2012-07-25 15:36:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(slave_map);
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-12 15:09:57 +02:00
|
|
|
static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_chmap_query_t **maps;
|
|
|
|
|
snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm);
|
|
|
|
|
if (!map)
|
|
|
|
|
return NULL;
|
|
|
|
|
maps = _snd_pcm_make_single_query_chmaps(map);
|
|
|
|
|
free(map);
|
|
|
|
|
return maps;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-17 11:00:32 +00:00
|
|
|
static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int dst;
|
2001-02-05 15:44:42 +00:00
|
|
|
if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "Route conversion PCM\n");
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
|
2000-11-20 20:10:46 +00:00
|
|
|
snd_pcm_format_name(route->sformat));
|
2005-12-11 08:24:13 +00:00
|
|
|
snd_output_puts(out, " Transformation table:\n");
|
2000-09-24 09:57:26 +00:00
|
|
|
for (dst = 0; dst < route->params.ndsts; dst++) {
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int src;
|
2005-12-11 08:24:13 +00:00
|
|
|
snd_output_printf(out, " %d <- ", dst);
|
2001-11-06 12:34:02 +00:00
|
|
|
if (d->nsrcs == 0) {
|
2001-11-19 11:54:06 +00:00
|
|
|
snd_output_printf(out, "none\n");
|
2001-11-06 12:34:02 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
src = 0;
|
|
|
|
|
while (1) {
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_src_t *s = &d->srcs[src];
|
2000-09-24 09:57:26 +00:00
|
|
|
if (d->att)
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "%d*%g", s->channel, s->as_float);
|
2001-07-19 09:11:05 +00:00
|
|
|
#else
|
2002-01-12 10:52:42 +00:00
|
|
|
snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
|
2001-07-19 09:11:05 +00:00
|
|
|
#endif
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "%d", s->channel);
|
2000-09-24 09:57:26 +00:00
|
|
|
src++;
|
|
|
|
|
if (src == d->nsrcs)
|
|
|
|
|
break;
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_puts(out, " + ");
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_putc(out, '\n');
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
if (pcm->setup) {
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "Its setup is:\n");
|
|
|
|
|
snd_pcm_dump_setup(pcm, out);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "Slave: ");
|
2005-01-20 15:07:51 +00:00
|
|
|
snd_pcm_dump(route->plug.gen.slave, out);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
/*
|
|
|
|
|
* Converts a string to an array of channel indices:
|
|
|
|
|
* - Given a number, the result is an array with one element,
|
|
|
|
|
* containing that number
|
|
|
|
|
* - Given a channel name (e g "FL") and a chmap,
|
|
|
|
|
* it will look this up in the chmap and return all matches
|
|
|
|
|
* - Given a channel name and no chmap, the result is an array with one element,
|
|
|
|
|
containing alsa standard channel map. Note that this might be a negative
|
|
|
|
|
number in case of "UNKNOWN", "NA" or "MONO".
|
|
|
|
|
* Return value is number of matches written.
|
|
|
|
|
*/
|
|
|
|
|
static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
|
|
|
|
|
long *channel, int channel_size)
|
2014-02-28 08:57:05 +01:00
|
|
|
{
|
|
|
|
|
int ch;
|
2014-02-28 08:57:06 +01:00
|
|
|
if (safe_strtol(id, channel) >= 0)
|
|
|
|
|
return 1;
|
2014-02-28 08:57:05 +01:00
|
|
|
|
|
|
|
|
ch = (int) snd_pcm_chmap_from_string(id);
|
|
|
|
|
if (ch == -1)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
if (chmap) {
|
|
|
|
|
int i, r = 0;
|
|
|
|
|
/* Start with highest channel to simplify implementation of
|
|
|
|
|
determine ttable size */
|
|
|
|
|
for (i = chmap->channels - 1; i >= 0; i--) {
|
|
|
|
|
if ((int) chmap->pos[i] != ch)
|
|
|
|
|
continue;
|
|
|
|
|
if (r >= channel_size)
|
|
|
|
|
continue;
|
|
|
|
|
channel[r++] = i;
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Assume ALSA standard channel mapping */
|
|
|
|
|
*channel = ch - SND_CHMAP_FL;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MAX_CHMAP_CHANNELS 256
|
|
|
|
|
|
|
|
|
|
static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
|
|
|
|
|
{
|
|
|
|
|
snd_config_iterator_t i, inext;
|
|
|
|
|
snd_pcm_chmap_t *chmap;
|
|
|
|
|
|
|
|
|
|
assert(tt && tt_chmap);
|
|
|
|
|
chmap = malloc(sizeof(snd_pcm_chmap_t) +
|
|
|
|
|
MAX_CHMAP_CHANNELS * sizeof(unsigned int));
|
|
|
|
|
|
|
|
|
|
chmap->channels = 0;
|
|
|
|
|
snd_config_for_each(i, inext, tt) {
|
|
|
|
|
const char *id;
|
|
|
|
|
snd_config_iterator_t j, jnext;
|
|
|
|
|
snd_config_t *in = snd_config_iterator_entry(i);
|
|
|
|
|
|
2015-09-08 21:48:17 +02:00
|
|
|
if (snd_config_get_id(in, &id) < 0)
|
2014-02-28 08:57:06 +01:00
|
|
|
continue;
|
|
|
|
|
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
|
|
|
|
goto err;
|
|
|
|
|
snd_config_for_each(j, jnext, in) {
|
|
|
|
|
int ch, k, found;
|
|
|
|
|
long schannel;
|
|
|
|
|
snd_config_t *jnode = snd_config_iterator_entry(j);
|
|
|
|
|
if (snd_config_get_id(jnode, &id) < 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (safe_strtol(id, &schannel) >= 0)
|
|
|
|
|
continue;
|
|
|
|
|
ch = (int) snd_pcm_chmap_from_string(id);
|
|
|
|
|
if (ch == -1)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
found = 0;
|
|
|
|
|
for (k = 0; k < (int) chmap->channels; k++)
|
|
|
|
|
if (ch == (int) chmap->pos[k]) {
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (found)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (chmap->channels >= MAX_CHMAP_CHANNELS) {
|
|
|
|
|
SNDERR("Too many channels in ttable chmap");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
chmap->pos[chmap->channels++] = ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-18 23:07:19 +01:00
|
|
|
if (chmap->channels == 0) {
|
|
|
|
|
free(chmap);
|
|
|
|
|
chmap = NULL;
|
|
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
*tt_chmap = chmap;
|
2014-02-28 08:57:05 +01:00
|
|
|
return 0;
|
2014-02-28 08:57:06 +01:00
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
*tt_chmap = NULL;
|
|
|
|
|
free(chmap);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int find_matching_chmap(snd_pcm_t *spcm, snd_pcm_chmap_t *tt_chmap,
|
|
|
|
|
snd_pcm_chmap_t **found_chmap, int *schannels)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(spcm);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
*found_chmap = NULL;
|
|
|
|
|
|
|
|
|
|
if (chmaps == NULL)
|
|
|
|
|
return 0; /* chmap API not supported for this slave */
|
|
|
|
|
|
|
|
|
|
for (i = 0; chmaps[i]; i++) {
|
|
|
|
|
unsigned int j, k;
|
|
|
|
|
int match = 1;
|
|
|
|
|
snd_pcm_chmap_t *c = &chmaps[i]->map;
|
|
|
|
|
if (*schannels >= 0 && (int) c->channels != *schannels)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < tt_chmap->channels; j++) {
|
|
|
|
|
int found = 0;
|
|
|
|
|
unsigned int ch = tt_chmap->pos[j];
|
|
|
|
|
for (k = 0; k < c->channels; k++)
|
|
|
|
|
if (c->pos[k] == ch) {
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
match = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
|
int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
|
|
|
|
|
*found_chmap = malloc(size);
|
|
|
|
|
if (!*found_chmap) {
|
|
|
|
|
snd_pcm_free_chmaps(chmaps);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
memcpy(*found_chmap, c, size);
|
|
|
|
|
*schannels = c->channels;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snd_pcm_free_chmaps(chmaps);
|
|
|
|
|
|
2014-03-19 10:54:08 +01:00
|
|
|
if (*found_chmap == NULL) {
|
2014-02-28 08:57:06 +01:00
|
|
|
SNDERR("Found no matching channel map");
|
2014-03-19 10:54:08 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int route_chmap_init(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
int set_map = 0;
|
|
|
|
|
snd_pcm_chmap_t *current;
|
|
|
|
|
snd_pcm_route_t *route = pcm->private_data;
|
|
|
|
|
if (!route->chmap)
|
|
|
|
|
return 0;
|
pcm: Add thread-safety to PCM API
Traditionally, many of ALSA library functions are supposed to be
thread-unsafe, and applications are required to take care of thread
safety by themselves. However, people never be careful enough, and
almost all applications fail in this regard.
This patch is an attempt to harden the thread safety in exported PCM
functions in a simplistic way: just wrap some of exported functions
with the pthread mutex of each PCM object. Not all API functions are
wrapped by the mutex since it doesn't make sense. Instead, the
patchset covers only the functions that may be likely called
concurrently. The supposedly thread-safe API functions are marked in
the document.
For achieving the feature, two new fields are added snd_pcm_t when the
option is enabled: thread_safe and lock. The former indicates that
the plugin is thread-safe that doesn't need this workaround and the
latter is the pthread mutex. Currently only hw plugin have
thread_safe=1. So, the most of real-time sensitive apps won't be
influenced by this patchset.
Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are
left without the extra mutex locking: namely, the ones that may have
blocking behavior, i.e. resume, drain, readi, writei, readn and
writen. These are supposed to handle own locking in the callbacks.
Also, if anyone wants to disable this new thread-safe API feature, it
can be still turned off via --disable-thread-safety configure option.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
|
|
|
if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
|
2014-02-28 08:57:06 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Check if we really need to set the chmap or not.
|
|
|
|
|
This is important in case set_chmap is not implemented. */
|
|
|
|
|
current = snd_pcm_get_chmap(route->plug.gen.slave);
|
|
|
|
|
if (!current)
|
|
|
|
|
return -ENOSYS;
|
|
|
|
|
if (current->channels != route->chmap->channels)
|
|
|
|
|
set_map = 1;
|
|
|
|
|
else
|
|
|
|
|
set_map = memcmp(current->pos, route->chmap->pos,
|
|
|
|
|
current->channels);
|
|
|
|
|
free(current);
|
|
|
|
|
if (!set_map)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
|
2014-02-28 08:57:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-11-21 20:38:09 +01:00
|
|
|
static const snd_pcm_ops_t snd_pcm_route_ops = {
|
2003-07-25 17:02:00 +00:00
|
|
|
.close = snd_pcm_route_close,
|
2005-01-20 15:07:51 +00:00
|
|
|
.info = snd_pcm_generic_info,
|
2003-07-25 17:02:00 +00:00
|
|
|
.hw_refine = snd_pcm_route_hw_refine,
|
|
|
|
|
.hw_params = snd_pcm_route_hw_params,
|
2005-01-20 15:07:51 +00:00
|
|
|
.hw_free = snd_pcm_generic_hw_free,
|
|
|
|
|
.sw_params = snd_pcm_generic_sw_params,
|
|
|
|
|
.channel_info = snd_pcm_generic_channel_info,
|
2003-07-25 17:02:00 +00:00
|
|
|
.dump = snd_pcm_route_dump,
|
2005-01-20 15:07:51 +00:00
|
|
|
.nonblock = snd_pcm_generic_nonblock,
|
|
|
|
|
.async = snd_pcm_generic_async,
|
|
|
|
|
.mmap = snd_pcm_generic_mmap,
|
|
|
|
|
.munmap = snd_pcm_generic_munmap,
|
2012-09-12 15:09:57 +02:00
|
|
|
.query_chmaps = snd_pcm_route_query_chmaps,
|
2012-07-25 15:36:16 +02:00
|
|
|
.get_chmap = snd_pcm_route_get_chmap,
|
|
|
|
|
.set_chmap = NULL, /* NYI */
|
2000-09-24 09:57:26 +00:00
|
|
|
};
|
|
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
|
|
|
|
|
unsigned int tt_ssize,
|
|
|
|
|
snd_pcm_route_ttable_entry_t *ttable,
|
|
|
|
|
unsigned int tt_cused, unsigned int tt_sused)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
unsigned int src_channel, dst_channel;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_dst_t *dptr;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int sused, dused, smul, dmul;
|
|
|
|
|
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
|
|
|
|
sused = tt_cused;
|
|
|
|
|
dused = tt_sused;
|
|
|
|
|
smul = tt_ssize;
|
|
|
|
|
dmul = 1;
|
|
|
|
|
} else {
|
|
|
|
|
sused = tt_sused;
|
|
|
|
|
dused = tt_cused;
|
|
|
|
|
smul = 1;
|
|
|
|
|
dmul = tt_ssize;
|
|
|
|
|
}
|
|
|
|
|
params->ndsts = dused;
|
2012-09-12 15:09:57 +02:00
|
|
|
params->nsrcs = sused;
|
2000-09-24 09:57:26 +00:00
|
|
|
dptr = calloc(dused, sizeof(*params->dsts));
|
|
|
|
|
if (!dptr)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
params->dsts = dptr;
|
|
|
|
|
for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_entry_t t = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
int att = 0;
|
|
|
|
|
int nsrcs = 0;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_src_t srcs[sused];
|
2000-09-24 09:57:26 +00:00
|
|
|
for (src_channel = 0; src_channel < sused; ++src_channel) {
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_entry_t v;
|
2000-09-24 09:57:26 +00:00
|
|
|
v = ttable[src_channel * smul + dst_channel * dmul];
|
|
|
|
|
if (v != 0) {
|
|
|
|
|
srcs[nsrcs].channel = src_channel;
|
2002-01-12 10:52:42 +00:00
|
|
|
#if SND_PCM_PLUGIN_ROUTE_FLOAT
|
2000-09-24 09:57:26 +00:00
|
|
|
/* Also in user space for non attenuated */
|
2002-01-12 10:52:42 +00:00
|
|
|
srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
|
2000-09-24 09:57:26 +00:00
|
|
|
srcs[nsrcs].as_float = v;
|
|
|
|
|
#else
|
2006-04-06 19:03:16 +02:00
|
|
|
assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
|
2000-09-24 09:57:26 +00:00
|
|
|
srcs[nsrcs].as_int = v;
|
|
|
|
|
#endif
|
2002-01-12 10:52:42 +00:00
|
|
|
if (v != SND_PCM_PLUGIN_ROUTE_FULL)
|
2000-09-24 09:57:26 +00:00
|
|
|
att = 1;
|
|
|
|
|
t += v;
|
|
|
|
|
nsrcs++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#if 0
|
2002-01-12 10:52:42 +00:00
|
|
|
assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
|
2000-09-24 09:57:26 +00:00
|
|
|
#endif
|
|
|
|
|
dptr->att = att;
|
|
|
|
|
dptr->nsrcs = nsrcs;
|
|
|
|
|
if (nsrcs == 0)
|
2001-01-26 09:56:30 +00:00
|
|
|
dptr->func = snd_pcm_route_convert1_zero;
|
2003-05-16 14:36:26 +00:00
|
|
|
else
|
2001-01-26 09:56:30 +00:00
|
|
|
dptr->func = snd_pcm_route_convert1_many;
|
2000-09-24 09:57:26 +00:00
|
|
|
if (nsrcs > 0) {
|
2001-03-29 17:50:28 +00:00
|
|
|
dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
|
2000-09-24 09:57:26 +00:00
|
|
|
if (!dptr->srcs)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
|
|
|
|
|
} else
|
|
|
|
|
dptr->srcs = 0;
|
|
|
|
|
dptr++;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Creates a new Route & Volume PCM
|
|
|
|
|
* \param pcmp Returns created PCM handle
|
|
|
|
|
* \param name Name of PCM
|
|
|
|
|
* \param sformat Slave format
|
|
|
|
|
* \param schannels Slave channels
|
|
|
|
|
* \param ttable Attenuation table
|
|
|
|
|
* \param tt_ssize Attenuation table - slave size
|
|
|
|
|
* \param tt_cused Attenuation table - client used count
|
|
|
|
|
* \param tt_sused Attenuation table - slave used count
|
|
|
|
|
* \param slave Slave PCM handle
|
|
|
|
|
* \param close_slave When set, the slave PCM handle is closed with copy PCM
|
|
|
|
|
* \retval zero on success otherwise a negative error code
|
|
|
|
|
* \warning Using of this function might be dangerous in the sense
|
|
|
|
|
* of compatibility reasons. The prototype might be freely
|
|
|
|
|
* changed in future.
|
|
|
|
|
*/
|
2001-02-11 15:45:35 +00:00
|
|
|
int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_pcm_format_t sformat, int schannels,
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_route_ttable_entry_t *ttable,
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int tt_ssize,
|
|
|
|
|
unsigned int tt_cused, unsigned int tt_sused,
|
|
|
|
|
snd_pcm_t *slave, int close_slave)
|
|
|
|
|
{
|
2000-10-14 10:31:34 +00:00
|
|
|
snd_pcm_t *pcm;
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_pcm_route_t *route;
|
|
|
|
|
int err;
|
2000-10-14 10:31:34 +00:00
|
|
|
assert(pcmp && slave && ttable);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_linear(sformat) != 1)
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
route = calloc(1, sizeof(snd_pcm_route_t));
|
|
|
|
|
if (!route) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
2002-02-21 15:01:34 +00:00
|
|
|
snd_pcm_plugin_init(&route->plug);
|
2000-11-20 20:10:46 +00:00
|
|
|
route->sformat = sformat;
|
|
|
|
|
route->schannels = schannels;
|
2000-09-24 09:57:26 +00:00
|
|
|
route->plug.read = snd_pcm_route_read_areas;
|
|
|
|
|
route->plug.write = snd_pcm_route_write_areas;
|
2002-02-21 15:01:34 +00:00
|
|
|
route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
|
|
|
|
|
route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
|
2005-01-20 15:07:51 +00:00
|
|
|
route->plug.gen.slave = slave;
|
|
|
|
|
route->plug.gen.close_slave = close_slave;
|
2014-02-28 08:57:06 +01:00
|
|
|
route->plug.init = route_chmap_init;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-06-20 20:52:12 +00:00
|
|
|
err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
|
|
|
|
|
if (err < 0) {
|
2000-09-24 09:57:26 +00:00
|
|
|
free(route);
|
2001-06-20 20:52:12 +00:00
|
|
|
return err;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2000-10-14 10:31:34 +00:00
|
|
|
pcm->ops = &snd_pcm_route_ops;
|
|
|
|
|
pcm->fast_ops = &snd_pcm_plugin_fast_ops;
|
2001-02-11 15:45:35 +00:00
|
|
|
pcm->private_data = route;
|
2000-10-14 10:31:34 +00:00
|
|
|
pcm->poll_fd = slave->poll_fd;
|
2003-02-11 18:14:43 +00:00
|
|
|
pcm->poll_events = slave->poll_events;
|
2014-07-10 14:37:49 +02:00
|
|
|
pcm->tstamp_type = slave->tstamp_type;
|
2002-04-23 15:51:29 +00:00
|
|
|
snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
|
|
|
|
|
snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
|
2000-10-14 10:31:34 +00:00
|
|
|
err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0) {
|
2000-10-14 10:31:34 +00:00
|
|
|
snd_pcm_close(pcm);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
|
|
|
|
}
|
2000-10-14 10:31:34 +00:00
|
|
|
*pcmp = pcm;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
static int _snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
|
|
|
unsigned int *tt_csize,
|
|
|
|
|
unsigned int *tt_ssize,
|
|
|
|
|
snd_pcm_chmap_t *chmap)
|
2001-12-12 18:19:43 +00:00
|
|
|
{
|
|
|
|
|
snd_config_iterator_t i, inext;
|
|
|
|
|
long csize = 0, ssize = 0;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
assert(tt && tt_csize && tt_ssize);
|
|
|
|
|
snd_config_for_each(i, inext, tt) {
|
|
|
|
|
snd_config_t *in = snd_config_iterator_entry(i);
|
|
|
|
|
snd_config_iterator_t j, jnext;
|
|
|
|
|
long cchannel;
|
|
|
|
|
const char *id;
|
2015-09-08 21:48:17 +02:00
|
|
|
if (snd_config_get_id(in, &id) < 0)
|
2001-12-12 18:19:43 +00:00
|
|
|
continue;
|
|
|
|
|
err = safe_strtol(id, &cchannel);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
SNDERR("Invalid client channel: %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (cchannel + 1 > csize)
|
|
|
|
|
csize = cchannel + 1;
|
|
|
|
|
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
snd_config_for_each(j, jnext, in) {
|
|
|
|
|
snd_config_t *jnode = snd_config_iterator_entry(j);
|
|
|
|
|
long schannel;
|
|
|
|
|
const char *id;
|
|
|
|
|
if (snd_config_get_id(jnode, &id) < 0)
|
|
|
|
|
continue;
|
2014-02-28 08:57:06 +01:00
|
|
|
err = strtochannel(id, chmap, &schannel, 1);
|
2001-12-12 18:19:43 +00:00
|
|
|
if (err < 0) {
|
|
|
|
|
SNDERR("Invalid slave channel: %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (schannel + 1 > ssize)
|
|
|
|
|
ssize = schannel + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (csize == 0 || ssize == 0) {
|
|
|
|
|
SNDERR("Invalid null ttable configuration");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
*tt_csize = csize;
|
|
|
|
|
*tt_ssize = ssize;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
/**
|
|
|
|
|
* \brief Determine route matrix sizes
|
|
|
|
|
* \param tt Configuration root describing route matrix
|
|
|
|
|
* \param tt_csize Returned client size in elements
|
|
|
|
|
* \param tt_ssize Returned slave size in elements
|
|
|
|
|
* \retval zero on success otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
|
|
|
unsigned int *tt_csize,
|
|
|
|
|
unsigned int *tt_ssize)
|
|
|
|
|
{
|
|
|
|
|
return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Load route matrix
|
|
|
|
|
* \param tt Configuration root describing route matrix
|
|
|
|
|
* \param ttable Returned route matrix
|
|
|
|
|
* \param tt_csize Client size in elements
|
|
|
|
|
* \param tt_ssize Slave size in elements
|
|
|
|
|
* \param tt_cused Used client elements
|
|
|
|
|
* \param tt_sused Used slave elements
|
|
|
|
|
* \param schannels Slave channels
|
|
|
|
|
* \retval zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2014-02-28 08:57:06 +01:00
|
|
|
static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
|
|
|
|
|
unsigned int tt_csize, unsigned int tt_ssize,
|
|
|
|
|
unsigned int *tt_cused, unsigned int *tt_sused,
|
|
|
|
|
int schannels, snd_pcm_chmap_t *chmap)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
int cused = -1;
|
|
|
|
|
int sused = -1;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, inext;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int k;
|
2001-06-06 11:49:52 +00:00
|
|
|
int err;
|
2000-09-24 09:57:26 +00:00
|
|
|
for (k = 0; k < tt_csize * tt_ssize; ++k)
|
|
|
|
|
ttable[k] = 0.0;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_for_each(i, inext, tt) {
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *in = snd_config_iterator_entry(i);
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t j, jnext;
|
2000-09-24 09:57:26 +00:00
|
|
|
long cchannel;
|
2001-11-19 08:14:21 +00:00
|
|
|
const char *id;
|
2014-09-16 09:00:39 +02:00
|
|
|
if (snd_config_get_id(in, &id) < 0)
|
2001-11-19 08:14:21 +00:00
|
|
|
continue;
|
|
|
|
|
err = safe_strtol(id, &cchannel);
|
2001-06-06 11:49:52 +00:00
|
|
|
if (err < 0 ||
|
2000-11-30 09:40:50 +00:00
|
|
|
cchannel < 0 || (unsigned int) cchannel > tt_csize) {
|
2001-11-19 08:14:21 +00:00
|
|
|
SNDERR("Invalid client channel: %s", id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-11-30 09:40:50 +00:00
|
|
|
}
|
2001-02-07 11:34:33 +00:00
|
|
|
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_for_each(j, jnext, in) {
|
2001-03-29 17:50:28 +00:00
|
|
|
snd_config_t *jnode = snd_config_iterator_entry(j);
|
2000-09-24 09:57:26 +00:00
|
|
|
double value;
|
2014-02-28 08:57:06 +01:00
|
|
|
int ss;
|
|
|
|
|
long *scha = alloca(tt_ssize * sizeof(long));
|
2001-11-19 08:14:21 +00:00
|
|
|
const char *id;
|
|
|
|
|
if (snd_config_get_id(jnode, &id) < 0)
|
|
|
|
|
continue;
|
2014-02-28 08:57:06 +01:00
|
|
|
|
|
|
|
|
ss = strtochannel(id, chmap, scha, tt_ssize);
|
|
|
|
|
if (ss < 0) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Invalid slave channel: %s", id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-11-30 09:40:50 +00:00
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
err = snd_config_get_real(jnode, &value);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0) {
|
|
|
|
|
long v;
|
2001-03-29 17:50:28 +00:00
|
|
|
err = snd_config_get_integer(jnode, &v);
|
2000-11-30 09:40:50 +00:00
|
|
|
if (err < 0) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Invalid type for %s", id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-11-30 09:40:50 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
value = v;
|
|
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
|
|
|
|
|
for (k = 0; (int) k < ss; k++) {
|
|
|
|
|
long schannel = scha[k];
|
|
|
|
|
if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
|
|
|
|
(schannels > 0 && schannel >= schannels)) {
|
|
|
|
|
SNDERR("Invalid slave channel: %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
ttable[cchannel * tt_ssize + schannel] = value;
|
|
|
|
|
if (schannel > sused)
|
|
|
|
|
sused = schannel;
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
if (cchannel > cused)
|
|
|
|
|
cused = cchannel;
|
|
|
|
|
}
|
|
|
|
|
*tt_sused = sused + 1;
|
|
|
|
|
*tt_cused = cused + 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
/**
|
|
|
|
|
* \brief Load route matrix
|
|
|
|
|
* \param tt Configuration root describing route matrix
|
|
|
|
|
* \param ttable Returned route matrix
|
|
|
|
|
* \param tt_csize Client size in elements
|
|
|
|
|
* \param tt_ssize Slave size in elements
|
|
|
|
|
* \param tt_cused Used client elements
|
|
|
|
|
* \param tt_sused Used slave elements
|
|
|
|
|
* \param schannels Slave channels
|
|
|
|
|
* \retval zero on success otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
|
|
|
|
|
unsigned int tt_csize, unsigned int tt_ssize,
|
|
|
|
|
unsigned int *tt_cused, unsigned int *tt_sused,
|
|
|
|
|
int schannels)
|
|
|
|
|
{
|
|
|
|
|
return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
|
|
|
|
|
tt_cused, tt_sused, schannels, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
/*! \page pcm_plugins
|
|
|
|
|
|
|
|
|
|
\section pcm_plugins_route Plugin: Route & Volume
|
|
|
|
|
|
|
|
|
|
This plugin converts channels and applies volume during the conversion.
|
|
|
|
|
The format and rate must match for both of them.
|
|
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
SCHANNEL can be a channel name instead of a number (e g FL, LFE).
|
|
|
|
|
If so, a matching channel map will be selected for the slave.
|
|
|
|
|
|
2002-01-16 16:42:40 +00:00
|
|
|
\code
|
|
|
|
|
pcm.name {
|
|
|
|
|
type route # Route & Volume conversion PCM
|
|
|
|
|
slave STR # Slave name
|
|
|
|
|
# or
|
|
|
|
|
slave { # Slave definition
|
|
|
|
|
pcm STR # Slave PCM name
|
|
|
|
|
# or
|
|
|
|
|
pcm { } # Slave PCM definition
|
2004-04-26 07:40:12 +00:00
|
|
|
[format STR] # Slave format
|
|
|
|
|
[channels INT] # Slave channels
|
|
|
|
|
}
|
|
|
|
|
ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
|
|
|
|
|
CCHANNEL {
|
|
|
|
|
SCHANNEL REAL # route value (0.0 - 1.0)
|
|
|
|
|
}
|
2002-01-16 16:42:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
|
|
\subsection pcm_plugins_route_funcref Function reference
|
|
|
|
|
|
|
|
|
|
<UL>
|
|
|
|
|
<LI>snd_pcm_route_open()
|
|
|
|
|
<LI>_snd_pcm_route_open()
|
|
|
|
|
</UL>
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Creates a new Route & Volume PCM
|
|
|
|
|
* \param pcmp Returns created PCM handle
|
|
|
|
|
* \param name Name of PCM
|
|
|
|
|
* \param root Root configuration node
|
|
|
|
|
* \param conf Configuration node with Route & Volume PCM description
|
|
|
|
|
* \param stream Stream type
|
|
|
|
|
* \param mode Stream mode
|
|
|
|
|
* \retval zero on success otherwise a negative error code
|
|
|
|
|
* \warning Using of this function might be dangerous in the sense
|
|
|
|
|
* of compatibility reasons. The prototype might be freely
|
|
|
|
|
* changed in future.
|
|
|
|
|
*/
|
2001-02-11 15:45:35 +00:00
|
|
|
int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
2001-06-11 13:35:48 +00:00
|
|
|
snd_config_t *root, snd_config_t *conf,
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_stream_t stream, int mode)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, next;
|
2000-09-24 09:57:26 +00:00
|
|
|
int err;
|
|
|
|
|
snd_pcm_t *spcm;
|
2001-05-14 15:44:37 +00:00
|
|
|
snd_config_t *slave = NULL, *sconf;
|
2014-03-19 10:52:24 +01:00
|
|
|
snd_pcm_chmap_t *tt_chmap = NULL, *chmap = NULL;
|
2001-02-05 15:44:42 +00:00
|
|
|
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
|
2001-03-17 16:34:43 +00:00
|
|
|
int schannels = -1;
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_config_t *tt = NULL;
|
2001-12-12 18:19:43 +00:00
|
|
|
snd_pcm_route_ttable_entry_t *ttable = NULL;
|
|
|
|
|
unsigned int csize, ssize;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int cused, sused;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_for_each(i, next, conf) {
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
2001-11-19 08:14:21 +00:00
|
|
|
const char *id;
|
|
|
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
|
|
|
continue;
|
2001-06-04 18:04:18 +00:00
|
|
|
if (snd_pcm_conf_generic_id(id))
|
2000-09-24 09:57:26 +00:00
|
|
|
continue;
|
2001-03-17 16:34:43 +00:00
|
|
|
if (strcmp(id, "slave") == 0) {
|
|
|
|
|
slave = n;
|
2000-09-24 09:57:26 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2001-02-07 11:34:33 +00:00
|
|
|
if (strcmp(id, "ttable") == 0) {
|
|
|
|
|
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Invalid type for %s", id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-11-30 09:40:50 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
tt = n;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Unknown field %s", id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
if (!slave) {
|
|
|
|
|
SNDERR("slave is not defined");
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-11-30 09:40:50 +00:00
|
|
|
}
|
|
|
|
|
if (!tt) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("ttable is not defined");
|
2000-11-30 09:40:50 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2001-06-15 08:47:59 +00:00
|
|
|
err = snd_pcm_slave_conf(root, slave, &sconf, 2,
|
2001-03-17 16:34:43 +00:00
|
|
|
SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
|
|
|
|
|
SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
|
|
|
|
|
snd_pcm_format_linear(sformat) != 1) {
|
2001-06-16 22:03:23 +00:00
|
|
|
snd_config_delete(sconf);
|
2001-03-17 16:34:43 +00:00
|
|
|
SNDERR("slave format is not linear");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
err = determine_chmap(tt, &tt_chmap);
|
2001-12-12 18:19:43 +00:00
|
|
|
if (err < 0) {
|
2014-02-28 08:57:06 +01:00
|
|
|
free(ttable);
|
2001-12-12 18:19:43 +00:00
|
|
|
return err;
|
|
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
|
|
|
|
|
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
|
|
|
|
|
snd_config_delete(sconf);
|
2001-06-16 22:03:23 +00:00
|
|
|
if (err < 0) {
|
2014-02-28 08:57:06 +01:00
|
|
|
free(tt_chmap);
|
2001-12-12 18:19:43 +00:00
|
|
|
free(ttable);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2001-06-16 22:03:23 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2014-02-28 08:57:06 +01:00
|
|
|
if (tt_chmap) {
|
|
|
|
|
err = find_matching_chmap(spcm, tt_chmap, &chmap, &schannels);
|
|
|
|
|
free(tt_chmap);
|
2014-05-27 09:51:21 +02:00
|
|
|
if (err < 0) {
|
|
|
|
|
snd_pcm_close(spcm);
|
2014-02-28 08:57:06 +01:00
|
|
|
return err;
|
2014-05-27 09:51:21 +02:00
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
free(chmap);
|
|
|
|
|
snd_pcm_close(spcm);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
|
|
|
|
|
if (ttable == NULL) {
|
|
|
|
|
free(chmap);
|
|
|
|
|
snd_pcm_close(spcm);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
|
|
|
|
|
&cused, &sused, schannels, chmap);
|
2001-12-12 18:19:43 +00:00
|
|
|
if (err < 0) {
|
2014-02-28 08:57:06 +01:00
|
|
|
free(chmap);
|
2001-12-12 18:19:43 +00:00
|
|
|
free(ttable);
|
2014-02-28 08:57:06 +01:00
|
|
|
snd_pcm_close(spcm);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2001-12-12 18:19:43 +00:00
|
|
|
}
|
2014-02-28 08:57:06 +01:00
|
|
|
|
2000-10-10 09:11:07 +00:00
|
|
|
err = snd_pcm_route_open(pcmp, name, sformat, schannels,
|
2001-12-12 18:19:43 +00:00
|
|
|
ttable, ssize,
|
2000-09-24 09:57:26 +00:00
|
|
|
cused, sused,
|
|
|
|
|
spcm, 1);
|
2001-12-12 18:19:43 +00:00
|
|
|
free(ttable);
|
2014-02-28 08:57:06 +01:00
|
|
|
if (err < 0) {
|
|
|
|
|
free(chmap);
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_pcm_close(spcm);
|
2014-03-19 10:52:24 +01:00
|
|
|
} else {
|
|
|
|
|
((snd_pcm_route_t*) (*pcmp)->private_data)->chmap = chmap;
|
2014-02-28 08:57:06 +01:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
|
|
|
|
}
|
2002-01-16 16:42:40 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
2001-10-24 14:14:11 +00:00
|
|
|
SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
|
2002-01-16 16:42:40 +00:00
|
|
|
#endif
|