Blame src/pcm/pcm_route.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file pcm/pcm_route.c
Packit 4a16fb
 * \ingroup PCM_Plugins
Packit 4a16fb
 * \brief PCM Route & Volume Plugin Interface
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \date 2000-2001
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Route & Volume Plugin
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
  
Packit 4a16fb
#include "bswap.h"
Packit 4a16fb
#include <math.h>
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "pcm_plugin.h"
Packit 4a16fb
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
Packit 4a16fb
#ifndef PIC
Packit 4a16fb
/* entry for static linking */
Packit 4a16fb
const char *_snd_module_pcm_route = "";
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
Packit 4a16fb
#define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
Packit 4a16fb
#elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
Packit 4a16fb
#define div(a) a >>= 4
Packit 4a16fb
#else
Packit 4a16fb
#error "Add some code here"
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	int channel;
Packit 4a16fb
	int as_int;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
	float as_float;
Packit 4a16fb
#endif
Packit 4a16fb
} snd_pcm_route_ttable_src_t;
Packit 4a16fb
Packit 4a16fb
typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	enum {UINT64, FLOAT} sum_idx;
Packit 4a16fb
	unsigned int get_idx;
Packit 4a16fb
	unsigned int put_idx;
Packit 4a16fb
	unsigned int conv_idx;
Packit 4a16fb
	int use_getput;
Packit 4a16fb
	unsigned int src_size;
Packit 4a16fb
	snd_pcm_format_t dst_sfmt;
Packit 4a16fb
	unsigned int nsrcs;
Packit 4a16fb
	unsigned int ndsts;
Packit 4a16fb
	snd_pcm_route_ttable_dst_t *dsts;
Packit 4a16fb
} snd_pcm_route_params_t;
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
Packit 4a16fb
			snd_pcm_uframes_t dst_offset,
Packit 4a16fb
			const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
			snd_pcm_uframes_t src_offset,
Packit 4a16fb
			unsigned int src_channels,
Packit 4a16fb
			snd_pcm_uframes_t frames,
Packit 4a16fb
			const snd_pcm_route_ttable_dst_t *ttable,
Packit 4a16fb
			const snd_pcm_route_params_t *params);
Packit 4a16fb
Packit 4a16fb
struct snd_pcm_route_ttable_dst {
Packit 4a16fb
	int att;	/* Attenuated */
Packit 4a16fb
	unsigned int nsrcs;
Packit 4a16fb
	snd_pcm_route_ttable_src_t* srcs;
Packit 4a16fb
	route_f func;
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
typedef union {
Packit 4a16fb
	int32_t as_sint32;
Packit 4a16fb
	int64_t as_sint64;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
	float as_float;
Packit 4a16fb
#endif
Packit 4a16fb
} sum_t;
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	/* This field need to be the first */
Packit 4a16fb
	snd_pcm_plugin_t plug;
Packit 4a16fb
	snd_pcm_format_t sformat;
Packit 4a16fb
	int schannels;
Packit 4a16fb
	snd_pcm_route_params_t params;
Packit 4a16fb
	snd_pcm_chmap_t *chmap;
Packit 4a16fb
} snd_pcm_route_t;
Packit 4a16fb
Packit 4a16fb
#endif /* DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
Packit 4a16fb
					snd_pcm_uframes_t dst_offset,
Packit 4a16fb
					const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
Packit 4a16fb
					snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
Packit 4a16fb
					unsigned int src_channels ATTRIBUTE_UNUSED,
Packit 4a16fb
					snd_pcm_uframes_t frames,
Packit 4a16fb
					const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
Packit 4a16fb
					const snd_pcm_route_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
Packit 4a16fb
				       snd_pcm_uframes_t dst_offset,
Packit 4a16fb
				       const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
				       snd_pcm_uframes_t src_offset,
Packit 4a16fb
				       unsigned int src_channels,
Packit 4a16fb
				       snd_pcm_uframes_t frames,
Packit 4a16fb
				       const snd_pcm_route_ttable_dst_t* ttable,
Packit 4a16fb
				       const snd_pcm_route_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
#define CONV_LABELS
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef CONV_LABELS
Packit 4a16fb
	void *conv;
Packit 4a16fb
	const snd_pcm_channel_area_t *src_area = 0;
Packit 4a16fb
	unsigned int srcidx;
Packit 4a16fb
	const char *src;
Packit 4a16fb
	char *dst;
Packit 4a16fb
	int src_step, dst_step;
Packit 4a16fb
	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
Packit 4a16fb
		unsigned int channel = ttable->srcs[srcidx].channel;
Packit 4a16fb
		if (channel >= src_channels)
Packit 4a16fb
			continue;
Packit 4a16fb
		src_area = &src_areas[channel];
Packit 4a16fb
		if (src_area->addr != NULL)
Packit 4a16fb
			break;
Packit 4a16fb
	}
Packit 4a16fb
	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
Packit 4a16fb
		snd_pcm_route_convert1_zero(dst_area, dst_offset,
Packit 4a16fb
					    src_areas, src_offset,
Packit 4a16fb
					    src_channels,
Packit 4a16fb
					    frames, ttable, params);
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	conv = conv_labels[params->conv_idx];
Packit 4a16fb
	src = snd_pcm_channel_area_addr(src_area, src_offset);
Packit 4a16fb
	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit 4a16fb
	src_step = snd_pcm_channel_area_step(src_area);
Packit 4a16fb
	dst_step = snd_pcm_channel_area_step(dst_area);
Packit 4a16fb
	while (frames-- > 0) {
Packit 4a16fb
		goto *conv;
Packit 4a16fb
#define CONV_END after
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef CONV_END
Packit 4a16fb
	after:
Packit 4a16fb
		src += src_step;
Packit 4a16fb
		dst += dst_step;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
Packit 4a16fb
					      snd_pcm_uframes_t dst_offset,
Packit 4a16fb
					      const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
					      snd_pcm_uframes_t src_offset,
Packit 4a16fb
					      unsigned int src_channels,
Packit 4a16fb
					      snd_pcm_uframes_t frames,
Packit 4a16fb
					      const snd_pcm_route_ttable_dst_t* ttable,
Packit 4a16fb
					      const snd_pcm_route_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
#define CONV24_LABELS
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef CONV24_LABELS
Packit 4a16fb
	void *get, *put;
Packit 4a16fb
	const snd_pcm_channel_area_t *src_area = 0;
Packit 4a16fb
	unsigned int srcidx;
Packit 4a16fb
	const char *src;
Packit 4a16fb
	char *dst;
Packit 4a16fb
	int src_step, dst_step;
Packit 4a16fb
	uint32_t sample = 0;
Packit 4a16fb
	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
Packit 4a16fb
		unsigned int channel = ttable->srcs[srcidx].channel;
Packit 4a16fb
		if (channel >= src_channels)
Packit 4a16fb
			continue;
Packit 4a16fb
		src_area = &src_areas[channel];
Packit 4a16fb
		if (src_area->addr != NULL)
Packit 4a16fb
			break;
Packit 4a16fb
	}
Packit 4a16fb
	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
Packit 4a16fb
		snd_pcm_route_convert1_zero(dst_area, dst_offset,
Packit 4a16fb
					    src_areas, src_offset,
Packit 4a16fb
					    src_channels,
Packit 4a16fb
					    frames, ttable, params);
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	get = get32_labels[params->get_idx];
Packit 4a16fb
	put = put32_labels[params->put_idx];
Packit 4a16fb
	src = snd_pcm_channel_area_addr(src_area, src_offset);
Packit 4a16fb
	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit 4a16fb
	src_step = snd_pcm_channel_area_step(src_area);
Packit 4a16fb
	dst_step = snd_pcm_channel_area_step(dst_area);
Packit 4a16fb
	while (frames-- > 0) {
Packit 4a16fb
		goto *get;
Packit 4a16fb
#define CONV24_END after
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef CONV24_END
Packit 4a16fb
	after:
Packit 4a16fb
		src += src_step;
Packit 4a16fb
		dst += dst_step;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
Packit 4a16fb
					snd_pcm_uframes_t dst_offset,
Packit 4a16fb
					const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
					snd_pcm_uframes_t src_offset,
Packit 4a16fb
					unsigned int src_channels,
Packit 4a16fb
					snd_pcm_uframes_t frames,
Packit 4a16fb
					const snd_pcm_route_ttable_dst_t* ttable,
Packit 4a16fb
					const snd_pcm_route_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
#define GET32_LABELS
Packit 4a16fb
#define PUT32_LABELS
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef GET32_LABELS
Packit 4a16fb
#undef PUT32_LABELS
Packit 4a16fb
	static void *const zero_labels[2] = {
Packit 4a16fb
		&&zero_int64,
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
		&&zero_float
Packit 4a16fb
#endif
Packit 4a16fb
	};
Packit 4a16fb
	/* sum_type att */
Packit 4a16fb
	static void *const add_labels[2 * 2] = {
Packit 4a16fb
		&&add_int64_noatt, &&add_int64_att,
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
		&&add_float_noatt, &&add_float_att
Packit 4a16fb
#endif
Packit 4a16fb
	};
Packit 4a16fb
	/* sum_type att */
Packit 4a16fb
	static void *const norm_labels[2 * 2] = {
Packit 4a16fb
		&&norm_int64_noatt,
Packit 4a16fb
		&&norm_int64_att,
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
		&&norm_float,
Packit 4a16fb
		&&norm_float,
Packit 4a16fb
#endif
Packit 4a16fb
	};
Packit 4a16fb
	void *zero, *get32, *add, *norm, *put32;
Packit 4a16fb
	int nsrcs = ttable->nsrcs;
Packit 4a16fb
	char *dst;
Packit 4a16fb
	int dst_step;
Packit 4a16fb
	const char *srcs[nsrcs];
Packit 4a16fb
	int src_steps[nsrcs];
Packit 4a16fb
	snd_pcm_route_ttable_src_t src_tt[nsrcs];
Packit 4a16fb
	int32_t sample = 0;
Packit 4a16fb
	int srcidx, srcidx1 = 0;
Packit 4a16fb
	for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
Packit 4a16fb
		const snd_pcm_channel_area_t *src_area;
Packit 4a16fb
		unsigned int channel = ttable->srcs[srcidx].channel;
Packit 4a16fb
		if (channel >= src_channels)
Packit 4a16fb
			continue;
Packit 4a16fb
		src_area = &src_areas[channel];
Packit 4a16fb
		srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
Packit 4a16fb
		src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
Packit 4a16fb
		src_tt[srcidx1] = ttable->srcs[srcidx];
Packit 4a16fb
		srcidx1++;
Packit 4a16fb
	}
Packit 4a16fb
	nsrcs = srcidx1;
Packit 4a16fb
	if (nsrcs == 0) {
Packit 4a16fb
		snd_pcm_route_convert1_zero(dst_area, dst_offset,
Packit 4a16fb
					    src_areas, src_offset,
Packit 4a16fb
					    src_channels,
Packit 4a16fb
					    frames, ttable, params);
Packit 4a16fb
		return;
Packit 4a16fb
	} else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
Packit 4a16fb
		if (params->use_getput)
Packit 4a16fb
			snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
Packit 4a16fb
							  src_areas, src_offset,
Packit 4a16fb
							  src_channels,
Packit 4a16fb
							  frames, ttable, params);
Packit 4a16fb
		else
Packit 4a16fb
			snd_pcm_route_convert1_one(dst_area, dst_offset,
Packit 4a16fb
						   src_areas, src_offset,
Packit 4a16fb
						   src_channels,
Packit 4a16fb
						   frames, ttable, params);
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	zero = zero_labels[params->sum_idx];
Packit 4a16fb
	get32 = get32_labels[params->get_idx];
Packit 4a16fb
	add = add_labels[params->sum_idx * 2 + ttable->att];
Packit 4a16fb
	norm = norm_labels[params->sum_idx * 2 + ttable->att];
Packit 4a16fb
	put32 = put32_labels[params->put_idx];
Packit 4a16fb
	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit 4a16fb
	dst_step = snd_pcm_channel_area_step(dst_area);
Packit 4a16fb
Packit 4a16fb
	while (frames-- > 0) {
Packit 4a16fb
		snd_pcm_route_ttable_src_t *ttp = src_tt;
Packit 4a16fb
		sum_t sum;
Packit 4a16fb
Packit 4a16fb
		/* Zero sum */
Packit 4a16fb
		goto *zero;
Packit 4a16fb
	zero_int64: 
Packit 4a16fb
		sum.as_sint64 = 0;
Packit 4a16fb
		goto zero_end;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
	zero_float:
Packit 4a16fb
		sum.as_float = 0.0;
Packit 4a16fb
		goto zero_end;
Packit 4a16fb
#endif
Packit 4a16fb
	zero_end:
Packit 4a16fb
		for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
Packit 4a16fb
			const char *src = srcs[srcidx];
Packit 4a16fb
			
Packit 4a16fb
			/* Get sample */
Packit 4a16fb
			goto *get32;
Packit 4a16fb
#define GET32_END after_get
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef GET32_END
Packit 4a16fb
		after_get:
Packit 4a16fb
Packit 4a16fb
			/* Sum */
Packit 4a16fb
			goto *add;
Packit 4a16fb
		add_int64_att:
Packit 4a16fb
			sum.as_sint64 += (int64_t) sample * ttp->as_int;
Packit 4a16fb
			goto after_sum;
Packit 4a16fb
		add_int64_noatt:
Packit 4a16fb
			if (ttp->as_int)
Packit 4a16fb
				sum.as_sint64 += sample;
Packit 4a16fb
			goto after_sum;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
		add_float_att:
Packit 4a16fb
			sum.as_float += sample * ttp->as_float;
Packit 4a16fb
			goto after_sum;
Packit 4a16fb
		add_float_noatt:
Packit 4a16fb
			if (ttp->as_int)
Packit 4a16fb
				sum.as_float += sample;
Packit 4a16fb
			goto after_sum;
Packit 4a16fb
#endif
Packit 4a16fb
		after_sum:
Packit 4a16fb
			srcs[srcidx] += src_steps[srcidx];
Packit 4a16fb
			ttp++;
Packit 4a16fb
		}
Packit 4a16fb
		
Packit 4a16fb
		/* Normalization */
Packit 4a16fb
		goto *norm;
Packit 4a16fb
	norm_int64_att:
Packit 4a16fb
		div(sum.as_sint64);
Packit 4a16fb
		/* fallthru */
Packit 4a16fb
	norm_int64_noatt:
Packit 4a16fb
		if (sum.as_sint64 > (int64_t)0x7fffffff)
Packit 4a16fb
			sample = 0x7fffffff;	/* maximum positive value */
Packit 4a16fb
		else if (sum.as_sint64 < -(int64_t)0x80000000)
Packit 4a16fb
			sample = 0x80000000;	/* maximum negative value */
Packit 4a16fb
		else
Packit 4a16fb
			sample = sum.as_sint64;
Packit 4a16fb
		goto after_norm;
Packit 4a16fb
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
	norm_float:
Packit 4a16fb
		sum.as_float = rint(sum.as_float);
Packit 4a16fb
		if (sum.as_float > (int64_t)0x7fffffff)
Packit 4a16fb
			sample = 0x7fffffff;	/* maximum positive value */
Packit 4a16fb
		else if (sum.as_float < -(int64_t)0x80000000)
Packit 4a16fb
			sample = 0x80000000;	/* maximum negative value */
Packit 4a16fb
		else
Packit 4a16fb
			sample = sum.as_float;
Packit 4a16fb
		goto after_norm;
Packit 4a16fb
#endif
Packit 4a16fb
	after_norm:
Packit 4a16fb
		
Packit 4a16fb
		/* Put sample */
Packit 4a16fb
		goto *put32;
Packit 4a16fb
#define PUT32_END after_put32
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef PUT32_END
Packit 4a16fb
	after_put32:
Packit 4a16fb
		
Packit 4a16fb
		dst += dst_step;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#endif /* DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
				  snd_pcm_uframes_t dst_offset,
Packit 4a16fb
				  const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
				  snd_pcm_uframes_t src_offset,
Packit 4a16fb
				  unsigned int src_channels,
Packit 4a16fb
				  unsigned int dst_channels,
Packit 4a16fb
				  snd_pcm_uframes_t frames,
Packit 4a16fb
				  snd_pcm_route_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int dst_channel;
Packit 4a16fb
	snd_pcm_route_ttable_dst_t *dstp;
Packit 4a16fb
	const snd_pcm_channel_area_t *dst_area;
Packit 4a16fb
Packit 4a16fb
	dstp = params->dsts;
Packit 4a16fb
	dst_area = dst_areas;
Packit 4a16fb
	for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
Packit 4a16fb
		if (dst_channel >= params->ndsts)
Packit 4a16fb
			snd_pcm_route_convert1_zero(dst_area, dst_offset,
Packit 4a16fb
						    src_areas, src_offset,
Packit 4a16fb
						    src_channels,
Packit 4a16fb
						    frames, dstp, params);
Packit 4a16fb
		else
Packit 4a16fb
			dstp->func(dst_area, dst_offset,
Packit 4a16fb
				   src_areas, src_offset,
Packit 4a16fb
				   src_channels,
Packit 4a16fb
				   frames, dstp, params);
Packit 4a16fb
		dstp++;
Packit 4a16fb
		dst_area++;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_close(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_route_params_t *params = &route->params;
Packit 4a16fb
	unsigned int dst_channel;
Packit 4a16fb
Packit 4a16fb
	if (params->dsts) {
Packit 4a16fb
		for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
Packit 4a16fb
			free(params->dsts[dst_channel].srcs);
Packit 4a16fb
		}
Packit 4a16fb
		free(params->dsts);
Packit 4a16fb
	}
Packit 4a16fb
	free(route->chmap);
Packit 4a16fb
	return snd_pcm_generic_close(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
Packit 4a16fb
	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
Packit 4a16fb
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
					 &access_mask);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit 4a16fb
					 &format_mask);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
Packit 4a16fb
	_snd_pcm_hw_params_any(sparams);
Packit 4a16fb
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
				   &saccess_mask);
Packit 4a16fb
	if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
Packit 4a16fb
		_snd_pcm_hw_params_set_format(sparams, route->sformat);
Packit 4a16fb
		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	}
Packit 4a16fb
	if (route->schannels >= 0) {
Packit 4a16fb
		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
Packit 4a16fb
				      (unsigned int) route->schannels, 0);
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
Packit 4a16fb
					    snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
Packit 4a16fb
	if (route->schannels < 0)
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
	
Packit 4a16fb
static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
Packit 4a16fb
					    snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
Packit 4a16fb
	if (route->schannels < 0)
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_hw_refine_slave(pcm, params,
Packit 4a16fb
				       snd_pcm_route_hw_refine_cprepare,
Packit 4a16fb
				       snd_pcm_route_hw_refine_cchange,
Packit 4a16fb
				       snd_pcm_route_hw_refine_sprepare,
Packit 4a16fb
				       snd_pcm_route_hw_refine_schange,
Packit 4a16fb
				       snd_pcm_generic_hw_refine);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = route->plug.gen.slave;
Packit 4a16fb
	snd_pcm_format_t src_format, dst_format;
Packit 4a16fb
	int err = snd_pcm_hw_params_slave(pcm, params,
Packit 4a16fb
					  snd_pcm_route_hw_refine_cchange,
Packit 4a16fb
					  snd_pcm_route_hw_refine_sprepare,
Packit 4a16fb
					  snd_pcm_route_hw_refine_schange,
Packit 4a16fb
					  snd_pcm_generic_hw_params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
Packit 4a16fb
		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
Packit 4a16fb
		dst_format = slave->format;
Packit 4a16fb
	} else {
Packit 4a16fb
		src_format = slave->format;
Packit 4a16fb
		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
Packit 4a16fb
	}
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	/* 3 bytes or 20-bit formats? */
Packit 4a16fb
	route->params.use_getput =
Packit 4a16fb
		(snd_pcm_format_physical_width(src_format) + 7) / 8 == 3 ||
Packit 4a16fb
		(snd_pcm_format_physical_width(dst_format) + 7) / 8 == 3 ||
Packit 4a16fb
		snd_pcm_format_width(src_format) == 20 ||
Packit 4a16fb
		snd_pcm_format_width(dst_format) == 20;
Packit 4a16fb
	route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32);
Packit 4a16fb
	route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, dst_format);
Packit 4a16fb
	route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
Packit 4a16fb
	route->params.src_size = snd_pcm_format_width(src_format) / 8;
Packit 4a16fb
	route->params.dst_sfmt = dst_format;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
	route->params.sum_idx = FLOAT;
Packit 4a16fb
#else
Packit 4a16fb
	route->params.sum_idx = UINT64;
Packit 4a16fb
#endif
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_route_write_areas(snd_pcm_t *pcm,
Packit 4a16fb
			  const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			  snd_pcm_uframes_t offset,
Packit 4a16fb
			  snd_pcm_uframes_t size,
Packit 4a16fb
			  const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			  snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			  snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = route->plug.gen.slave;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	snd_pcm_route_convert(slave_areas, slave_offset,
Packit 4a16fb
			      areas, offset, 
Packit 4a16fb
			      pcm->channels,
Packit 4a16fb
			      slave->channels,
Packit 4a16fb
			      size, &route->params);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_route_read_areas(snd_pcm_t *pcm,
Packit 4a16fb
			 const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			 snd_pcm_uframes_t offset,
Packit 4a16fb
			 snd_pcm_uframes_t size,
Packit 4a16fb
			 const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			 snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			 snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = route->plug.gen.slave;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	snd_pcm_route_convert(areas, offset, 
Packit 4a16fb
			      slave_areas, slave_offset,
Packit 4a16fb
			      slave->channels,
Packit 4a16fb
			      pcm->channels,
Packit 4a16fb
			      size, &route->params);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	snd_pcm_chmap_t *map, *slave_map;
Packit 4a16fb
	unsigned int src, dst, nsrcs;
Packit 4a16fb
Packit 4a16fb
	slave_map = snd_pcm_generic_get_chmap(pcm);
Packit 4a16fb
	if (!slave_map)
Packit 4a16fb
		return NULL;
Packit 4a16fb
	nsrcs = route->params.nsrcs;
Packit 4a16fb
	map = calloc(4, nsrcs + 1);
Packit 4a16fb
	if (!map) {
Packit 4a16fb
		free(slave_map);
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
	map->channels = nsrcs;
Packit 4a16fb
	for (src = 0; src < nsrcs; src++)
Packit 4a16fb
		map->pos[src] = SND_CHMAP_NA;
Packit 4a16fb
	for (dst = 0; dst < route->params.ndsts; dst++) {
Packit 4a16fb
		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
Packit 4a16fb
		for (src = 0; src < d->nsrcs; src++) {
Packit 4a16fb
			unsigned int c = d->srcs[src].channel;
Packit 4a16fb
			if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
Packit 4a16fb
				map->pos[c] = slave_map->pos[dst];
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	free(slave_map);
Packit 4a16fb
	return map;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_chmap_query_t **maps;
Packit 4a16fb
	snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm);
Packit 4a16fb
	if (!map)
Packit 4a16fb
		return NULL;
Packit 4a16fb
	maps = _snd_pcm_make_single_query_chmaps(map);
Packit 4a16fb
	free(map);
Packit 4a16fb
	return maps;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	unsigned int dst;
Packit 4a16fb
	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
		snd_output_printf(out, "Route conversion PCM\n");
Packit 4a16fb
	else
Packit 4a16fb
		snd_output_printf(out, "Route conversion PCM (sformat=%s)\n", 
Packit 4a16fb
			snd_pcm_format_name(route->sformat));
Packit 4a16fb
	snd_output_puts(out, "  Transformation table:\n");
Packit 4a16fb
	for (dst = 0; dst < route->params.ndsts; dst++) {
Packit 4a16fb
		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
Packit 4a16fb
		unsigned int src;
Packit 4a16fb
		snd_output_printf(out, "    %d <- ", dst);
Packit 4a16fb
		if (d->nsrcs == 0) {
Packit 4a16fb
			snd_output_printf(out, "none\n");
Packit 4a16fb
			continue;
Packit 4a16fb
		}
Packit 4a16fb
		src = 0;
Packit 4a16fb
		while (1) {
Packit 4a16fb
			snd_pcm_route_ttable_src_t *s = &d->srcs[src];
Packit 4a16fb
			if (d->att)
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
				snd_output_printf(out, "%d*%g", s->channel, s->as_float);
Packit 4a16fb
#else
Packit 4a16fb
				snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
Packit 4a16fb
#endif
Packit 4a16fb
			else
Packit 4a16fb
				snd_output_printf(out, "%d", s->channel);
Packit 4a16fb
			src++;
Packit 4a16fb
			if (src == d->nsrcs)
Packit 4a16fb
				break;
Packit 4a16fb
			snd_output_puts(out, " + ");
Packit 4a16fb
		}
Packit 4a16fb
		snd_output_putc(out, '\n');
Packit 4a16fb
	}
Packit 4a16fb
	if (pcm->setup) {
Packit 4a16fb
		snd_output_printf(out, "Its setup is:\n");
Packit 4a16fb
		snd_pcm_dump_setup(pcm, out);
Packit 4a16fb
	}
Packit 4a16fb
	snd_output_printf(out, "Slave: ");
Packit 4a16fb
	snd_pcm_dump(route->plug.gen.slave, out);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * Converts a string to an array of channel indices:
Packit 4a16fb
 * - Given a number, the result is an array with one element,
Packit 4a16fb
 *   containing that number
Packit 4a16fb
 * - Given a channel name (e g "FL") and a chmap,
Packit 4a16fb
 *   it will look this up in the chmap and return all matches
Packit 4a16fb
 * - Given a channel name and no chmap, the result is an array with one element,
Packit 4a16fb
     containing alsa standard channel map. Note that this might be a negative
Packit 4a16fb
     number in case of "UNKNOWN", "NA" or "MONO".
Packit 4a16fb
 * Return value is number of matches written.
Packit 4a16fb
 */
Packit 4a16fb
static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
Packit 4a16fb
			 long *channel, int channel_size)
Packit 4a16fb
{
Packit 4a16fb
	int ch;
Packit 4a16fb
	if (safe_strtol(id, channel) >= 0)
Packit 4a16fb
		return 1;
Packit 4a16fb
Packit 4a16fb
	ch = (int) snd_pcm_chmap_from_string(id);
Packit 4a16fb
	if (ch == -1)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
Packit 4a16fb
	if (chmap) {
Packit 4a16fb
		int i, r = 0;
Packit 4a16fb
		/* Start with highest channel to simplify implementation of
Packit 4a16fb
		   determine ttable size */
Packit 4a16fb
		for (i = chmap->channels - 1; i >= 0; i--) {
Packit 4a16fb
			if ((int) chmap->pos[i] != ch)
Packit 4a16fb
				continue;
Packit 4a16fb
			if (r >= channel_size)
Packit 4a16fb
				continue;
Packit 4a16fb
			channel[r++] = i;
Packit 4a16fb
		}
Packit 4a16fb
		return r;
Packit 4a16fb
	}
Packit 4a16fb
	else {
Packit 4a16fb
		/* Assume ALSA standard channel mapping */
Packit 4a16fb
		*channel = ch - SND_CHMAP_FL;
Packit 4a16fb
		return 1;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#define MAX_CHMAP_CHANNELS 256
Packit 4a16fb
Packit 4a16fb
static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
Packit 4a16fb
{
Packit 4a16fb
	snd_config_iterator_t i, inext;
Packit 4a16fb
	snd_pcm_chmap_t *chmap;
Packit 4a16fb
Packit 4a16fb
	assert(tt && tt_chmap);
Packit 4a16fb
	chmap = malloc(sizeof(snd_pcm_chmap_t) +
Packit 4a16fb
		       MAX_CHMAP_CHANNELS * sizeof(unsigned int));
Packit 4a16fb
Packit 4a16fb
	chmap->channels = 0;
Packit 4a16fb
	snd_config_for_each(i, inext, tt) {
Packit 4a16fb
		const char *id;
Packit 4a16fb
		snd_config_iterator_t j, jnext;
Packit 4a16fb
		snd_config_t *in = snd_config_iterator_entry(i);
Packit 4a16fb
Packit 4a16fb
		if (snd_config_get_id(in, &id) < 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
Packit 4a16fb
			goto err;
Packit 4a16fb
		snd_config_for_each(j, jnext, in) {
Packit 4a16fb
			int ch, k, found;
Packit 4a16fb
			long schannel;
Packit 4a16fb
			snd_config_t *jnode = snd_config_iterator_entry(j);
Packit 4a16fb
			if (snd_config_get_id(jnode, &id) < 0)
Packit 4a16fb
				continue;
Packit 4a16fb
			if (safe_strtol(id, &schannel) >= 0)
Packit 4a16fb
				continue;
Packit 4a16fb
			ch = (int) snd_pcm_chmap_from_string(id);
Packit 4a16fb
			if (ch == -1)
Packit 4a16fb
				goto err;
Packit 4a16fb
Packit 4a16fb
			found = 0;
Packit 4a16fb
			for (k = 0; k < (int) chmap->channels; k++)
Packit 4a16fb
				if (ch == (int) chmap->pos[k]) {
Packit 4a16fb
					found = 1;
Packit 4a16fb
					break;
Packit 4a16fb
				}
Packit 4a16fb
			if (found)
Packit 4a16fb
				continue;
Packit 4a16fb
Packit 4a16fb
			if (chmap->channels >= MAX_CHMAP_CHANNELS) {
Packit 4a16fb
				SNDERR("Too many channels in ttable chmap");
Packit 4a16fb
				goto err;
Packit 4a16fb
			}
Packit 4a16fb
			chmap->pos[chmap->channels++] = ch;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (chmap->channels == 0) {
Packit 4a16fb
		free(chmap);
Packit 4a16fb
		chmap = NULL;
Packit 4a16fb
	}
Packit 4a16fb
	*tt_chmap = chmap;
Packit 4a16fb
	return 0;
Packit 4a16fb
Packit 4a16fb
err:
Packit 4a16fb
	*tt_chmap = NULL;
Packit 4a16fb
	free(chmap);
Packit 4a16fb
	return -EINVAL;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int find_matching_chmap(snd_pcm_t *spcm, snd_pcm_chmap_t *tt_chmap,
Packit 4a16fb
			       snd_pcm_chmap_t **found_chmap, int *schannels)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(spcm);
Packit 4a16fb
	int i;
Packit 4a16fb
Packit 4a16fb
	*found_chmap = NULL;
Packit 4a16fb
Packit 4a16fb
	if (chmaps == NULL)
Packit 4a16fb
		return 0; /* chmap API not supported for this slave */
Packit 4a16fb
Packit 4a16fb
	for (i = 0; chmaps[i]; i++) {
Packit 4a16fb
		unsigned int j, k;
Packit 4a16fb
		int match = 1;
Packit 4a16fb
		snd_pcm_chmap_t *c = &chmaps[i]->map;
Packit 4a16fb
		if (*schannels >= 0 && (int) c->channels != *schannels)
Packit 4a16fb
			continue;
Packit 4a16fb
Packit 4a16fb
		for (j = 0; j < tt_chmap->channels; j++) {
Packit 4a16fb
			int found = 0;
Packit 4a16fb
			unsigned int ch = tt_chmap->pos[j];
Packit 4a16fb
			for (k = 0; k < c->channels; k++)
Packit 4a16fb
				if (c->pos[k] == ch) {
Packit 4a16fb
					found = 1;
Packit 4a16fb
					break;
Packit 4a16fb
				}
Packit 4a16fb
			if (!found) {
Packit 4a16fb
				match = 0;
Packit 4a16fb
				break;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
Packit 4a16fb
		if (match) {
Packit 4a16fb
			int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
Packit 4a16fb
			*found_chmap = malloc(size);
Packit 4a16fb
			if (!*found_chmap) {
Packit 4a16fb
				snd_pcm_free_chmaps(chmaps);
Packit 4a16fb
				return -ENOMEM;
Packit 4a16fb
			}
Packit 4a16fb
			memcpy(*found_chmap, c, size);
Packit 4a16fb
			*schannels = c->channels;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	snd_pcm_free_chmaps(chmaps);
Packit 4a16fb
Packit 4a16fb
	if (*found_chmap == NULL) {
Packit 4a16fb
		SNDERR("Found no matching channel map");
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int route_chmap_init(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	int set_map = 0;
Packit 4a16fb
	snd_pcm_chmap_t *current;
Packit 4a16fb
	snd_pcm_route_t *route = pcm->private_data;
Packit 4a16fb
	if (!route->chmap)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/* Check if we really need to set the chmap or not.
Packit 4a16fb
	   This is important in case set_chmap is not implemented. */
Packit 4a16fb
	current = snd_pcm_get_chmap(route->plug.gen.slave);
Packit 4a16fb
	if (!current)
Packit 4a16fb
		return -ENOSYS;
Packit 4a16fb
	if (current->channels != route->chmap->channels)
Packit 4a16fb
		set_map = 1;
Packit 4a16fb
	else
Packit 4a16fb
		set_map = memcmp(current->pos, route->chmap->pos,
Packit 4a16fb
				 current->channels);
Packit 4a16fb
	free(current);
Packit 4a16fb
	if (!set_map)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_ops_t snd_pcm_route_ops = {
Packit 4a16fb
	.close = snd_pcm_route_close,
Packit 4a16fb
	.info = snd_pcm_generic_info,
Packit 4a16fb
	.hw_refine = snd_pcm_route_hw_refine,
Packit 4a16fb
	.hw_params = snd_pcm_route_hw_params,
Packit 4a16fb
	.hw_free = snd_pcm_generic_hw_free,
Packit 4a16fb
	.sw_params = snd_pcm_generic_sw_params,
Packit 4a16fb
	.channel_info = snd_pcm_generic_channel_info,
Packit 4a16fb
	.dump = snd_pcm_route_dump,
Packit 4a16fb
	.nonblock = snd_pcm_generic_nonblock,
Packit 4a16fb
	.async = snd_pcm_generic_async,
Packit 4a16fb
	.mmap = snd_pcm_generic_mmap,
Packit 4a16fb
	.munmap = snd_pcm_generic_munmap,
Packit 4a16fb
	.query_chmaps = snd_pcm_route_query_chmaps,
Packit 4a16fb
	.get_chmap = snd_pcm_route_get_chmap,
Packit 4a16fb
	.set_chmap = NULL, /* NYI */
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
Packit 4a16fb
			     unsigned int tt_ssize,
Packit 4a16fb
			     snd_pcm_route_ttable_entry_t *ttable,
Packit 4a16fb
			     unsigned int tt_cused, unsigned int tt_sused)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int src_channel, dst_channel;
Packit 4a16fb
	snd_pcm_route_ttable_dst_t *dptr;
Packit 4a16fb
	unsigned int sused, dused, smul, dmul;
Packit 4a16fb
	if (stream == SND_PCM_STREAM_PLAYBACK) {
Packit 4a16fb
		sused = tt_cused;
Packit 4a16fb
		dused = tt_sused;
Packit 4a16fb
		smul = tt_ssize;
Packit 4a16fb
		dmul = 1;
Packit 4a16fb
	} else {
Packit 4a16fb
		sused = tt_sused;
Packit 4a16fb
		dused = tt_cused;
Packit 4a16fb
		smul = 1;
Packit 4a16fb
		dmul = tt_ssize;
Packit 4a16fb
	}
Packit 4a16fb
	params->ndsts = dused;
Packit 4a16fb
	params->nsrcs = sused;
Packit 4a16fb
	dptr = calloc(dused, sizeof(*params->dsts));
Packit 4a16fb
	if (!dptr)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	params->dsts = dptr;
Packit 4a16fb
	for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
Packit 4a16fb
		snd_pcm_route_ttable_entry_t t = 0;
Packit 4a16fb
		int att = 0;
Packit 4a16fb
		int nsrcs = 0;
Packit 4a16fb
		snd_pcm_route_ttable_src_t srcs[sused];
Packit 4a16fb
		for (src_channel = 0; src_channel < sused; ++src_channel) {
Packit 4a16fb
			snd_pcm_route_ttable_entry_t v;
Packit 4a16fb
			v = ttable[src_channel * smul + dst_channel * dmul];
Packit 4a16fb
			if (v != 0) {
Packit 4a16fb
				srcs[nsrcs].channel = src_channel;
Packit 4a16fb
#if SND_PCM_PLUGIN_ROUTE_FLOAT
Packit 4a16fb
				/* Also in user space for non attenuated */
Packit 4a16fb
				srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
Packit 4a16fb
				srcs[nsrcs].as_float = v;
Packit 4a16fb
#else
Packit 4a16fb
				assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
Packit 4a16fb
				srcs[nsrcs].as_int = v;
Packit 4a16fb
#endif
Packit 4a16fb
				if (v != SND_PCM_PLUGIN_ROUTE_FULL)
Packit 4a16fb
					att = 1;
Packit 4a16fb
				t += v;
Packit 4a16fb
				nsrcs++;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
#if 0
Packit 4a16fb
		assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
Packit 4a16fb
#endif
Packit 4a16fb
		dptr->att = att;
Packit 4a16fb
		dptr->nsrcs = nsrcs;
Packit 4a16fb
		if (nsrcs == 0)
Packit 4a16fb
			dptr->func = snd_pcm_route_convert1_zero;
Packit 4a16fb
		else
Packit 4a16fb
			dptr->func = snd_pcm_route_convert1_many;
Packit 4a16fb
		if (nsrcs > 0) {
Packit 4a16fb
			dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
Packit 4a16fb
			if (!dptr->srcs)
Packit 4a16fb
				return -ENOMEM;
Packit 4a16fb
			memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
Packit 4a16fb
		} else
Packit 4a16fb
			dptr->srcs = 0;
Packit 4a16fb
		dptr++;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Creates a new Route & Volume PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param sformat Slave format
Packit 4a16fb
 * \param schannels Slave channels
Packit 4a16fb
 * \param ttable Attenuation table
Packit 4a16fb
 * \param tt_ssize Attenuation table - slave size
Packit 4a16fb
 * \param tt_cused Attenuation table - client used count
Packit 4a16fb
 * \param tt_sused Attenuation table - slave used count
Packit 4a16fb
 * \param slave Slave PCM handle
Packit 4a16fb
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 * \warning Using of this function might be dangerous in the sense
Packit 4a16fb
 *          of compatibility reasons. The prototype might be freely
Packit 4a16fb
 *          changed in future.
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
Packit 4a16fb
		       snd_pcm_format_t sformat, int schannels,
Packit 4a16fb
		       snd_pcm_route_ttable_entry_t *ttable,
Packit 4a16fb
		       unsigned int tt_ssize,
Packit 4a16fb
		       unsigned int tt_cused, unsigned int tt_sused,
Packit 4a16fb
		       snd_pcm_t *slave, int close_slave)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_t *pcm;
Packit 4a16fb
	snd_pcm_route_t *route;
Packit 4a16fb
	int err;
Packit 4a16fb
	assert(pcmp && slave && ttable);
Packit 4a16fb
	if (sformat != SND_PCM_FORMAT_UNKNOWN && 
Packit 4a16fb
	    snd_pcm_format_linear(sformat) != 1)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	route = calloc(1, sizeof(snd_pcm_route_t));
Packit 4a16fb
	if (!route) {
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
	snd_pcm_plugin_init(&route->plug);
Packit 4a16fb
	route->sformat = sformat;
Packit 4a16fb
	route->schannels = schannels;
Packit 4a16fb
	route->plug.read = snd_pcm_route_read_areas;
Packit 4a16fb
	route->plug.write = snd_pcm_route_write_areas;
Packit 4a16fb
	route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
Packit 4a16fb
	route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
Packit 4a16fb
	route->plug.gen.slave = slave;
Packit 4a16fb
	route->plug.gen.close_slave = close_slave;
Packit 4a16fb
	route->plug.init = route_chmap_init;
Packit 4a16fb
Packit 4a16fb
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		free(route);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	pcm->ops = &snd_pcm_route_ops;
Packit 4a16fb
	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
Packit 4a16fb
	pcm->private_data = route;
Packit 4a16fb
	pcm->poll_fd = slave->poll_fd;
Packit 4a16fb
	pcm->poll_events = slave->poll_events;
Packit 4a16fb
	pcm->tstamp_type = slave->tstamp_type;
Packit 4a16fb
	snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
Packit 4a16fb
	snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
Packit 4a16fb
	err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		snd_pcm_close(pcm);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	*pcmp = pcm;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int _snd_pcm_route_determine_ttable(snd_config_t *tt,
Packit 4a16fb
					   unsigned int *tt_csize,
Packit 4a16fb
					   unsigned int *tt_ssize,
Packit 4a16fb
					   snd_pcm_chmap_t *chmap)
Packit 4a16fb
{
Packit 4a16fb
	snd_config_iterator_t i, inext;
Packit 4a16fb
	long csize = 0, ssize = 0;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	assert(tt && tt_csize && tt_ssize);
Packit 4a16fb
	snd_config_for_each(i, inext, tt) {
Packit 4a16fb
		snd_config_t *in = snd_config_iterator_entry(i);
Packit 4a16fb
		snd_config_iterator_t j, jnext;
Packit 4a16fb
		long cchannel;
Packit 4a16fb
		const char *id;
Packit 4a16fb
		if (snd_config_get_id(in, &id) < 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		err = safe_strtol(id, &cchannel);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			SNDERR("Invalid client channel: %s", id);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		if (cchannel + 1 > csize)
Packit 4a16fb
			csize = cchannel + 1;
Packit 4a16fb
		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		snd_config_for_each(j, jnext, in) {
Packit 4a16fb
			snd_config_t *jnode = snd_config_iterator_entry(j);
Packit 4a16fb
			long schannel;
Packit 4a16fb
			const char *id;
Packit 4a16fb
			if (snd_config_get_id(jnode, &id) < 0)
Packit 4a16fb
				continue;
Packit 4a16fb
			err = strtochannel(id, chmap, &schannel, 1);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SNDERR("Invalid slave channel: %s", id);
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			}
Packit 4a16fb
			if (schannel + 1 > ssize)
Packit 4a16fb
				ssize = schannel + 1;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	if (csize == 0 || ssize == 0) {
Packit 4a16fb
		SNDERR("Invalid null ttable configuration");
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	*tt_csize = csize;
Packit 4a16fb
	*tt_ssize = ssize;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Determine route matrix sizes
Packit 4a16fb
 * \param tt Configuration root describing route matrix
Packit 4a16fb
 * \param tt_csize Returned client size in elements
Packit 4a16fb
 * \param tt_ssize Returned slave size in elements
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_route_determine_ttable(snd_config_t *tt,
Packit 4a16fb
				   unsigned int *tt_csize,
Packit 4a16fb
				   unsigned int *tt_ssize)
Packit 4a16fb
{
Packit 4a16fb
	return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Load route matrix
Packit 4a16fb
 * \param tt Configuration root describing route matrix
Packit 4a16fb
 * \param ttable Returned route matrix
Packit 4a16fb
 * \param tt_csize Client size in elements
Packit 4a16fb
 * \param tt_ssize Slave size in elements
Packit 4a16fb
 * \param tt_cused Used client elements
Packit 4a16fb
 * \param tt_sused Used slave elements
Packit 4a16fb
 * \param schannels Slave channels
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 */
Packit 4a16fb
static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
Packit 4a16fb
				      unsigned int tt_csize, unsigned int tt_ssize,
Packit 4a16fb
				      unsigned int *tt_cused, unsigned int *tt_sused,
Packit 4a16fb
				      int schannels, snd_pcm_chmap_t *chmap)
Packit 4a16fb
{
Packit 4a16fb
	int cused = -1;
Packit 4a16fb
	int sused = -1;
Packit 4a16fb
	snd_config_iterator_t i, inext;
Packit 4a16fb
	unsigned int k;
Packit 4a16fb
	int err;
Packit 4a16fb
	for (k = 0; k < tt_csize * tt_ssize; ++k)
Packit 4a16fb
		ttable[k] = 0.0;
Packit 4a16fb
	snd_config_for_each(i, inext, tt) {
Packit 4a16fb
		snd_config_t *in = snd_config_iterator_entry(i);
Packit 4a16fb
		snd_config_iterator_t j, jnext;
Packit 4a16fb
		long cchannel;
Packit 4a16fb
		const char *id;
Packit 4a16fb
		if (snd_config_get_id(in, &id) < 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		err = safe_strtol(id, &cchannel);
Packit 4a16fb
		if (err < 0 || 
Packit 4a16fb
		    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
Packit 4a16fb
			SNDERR("Invalid client channel: %s", id);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		snd_config_for_each(j, jnext, in) {
Packit 4a16fb
			snd_config_t *jnode = snd_config_iterator_entry(j);
Packit 4a16fb
			double value;
Packit 4a16fb
			int ss;
Packit 4a16fb
			long *scha = alloca(tt_ssize * sizeof(long));
Packit 4a16fb
			const char *id;
Packit 4a16fb
			if (snd_config_get_id(jnode, &id) < 0)
Packit 4a16fb
				continue;
Packit 4a16fb
Packit 4a16fb
			ss = strtochannel(id, chmap, scha, tt_ssize);
Packit 4a16fb
			if (ss < 0) {
Packit 4a16fb
				SNDERR("Invalid slave channel: %s", id);
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			}
Packit 4a16fb
Packit 4a16fb
			err = snd_config_get_real(jnode, &value);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				long v;
Packit 4a16fb
				err = snd_config_get_integer(jnode, &v);
Packit 4a16fb
				if (err < 0) {
Packit 4a16fb
					SNDERR("Invalid type for %s", id);
Packit 4a16fb
					return -EINVAL;
Packit 4a16fb
				}
Packit 4a16fb
				value = v;
Packit 4a16fb
			}
Packit 4a16fb
Packit 4a16fb
			for (k = 0; (int) k < ss; k++) {
Packit 4a16fb
				long schannel = scha[k];
Packit 4a16fb
				if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
Packit 4a16fb
				    (schannels > 0 && schannel >= schannels)) {
Packit 4a16fb
					SNDERR("Invalid slave channel: %s", id);
Packit 4a16fb
					return -EINVAL;
Packit 4a16fb
				}
Packit 4a16fb
				ttable[cchannel * tt_ssize + schannel] = value;
Packit 4a16fb
				if (schannel > sused)
Packit 4a16fb
					sused = schannel;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		if (cchannel > cused)
Packit 4a16fb
			cused = cchannel;
Packit 4a16fb
	}
Packit 4a16fb
	*tt_sused = sused + 1;
Packit 4a16fb
	*tt_cused = cused + 1;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Load route matrix
Packit 4a16fb
 * \param tt Configuration root describing route matrix
Packit 4a16fb
 * \param ttable Returned route matrix
Packit 4a16fb
 * \param tt_csize Client size in elements
Packit 4a16fb
 * \param tt_ssize Slave size in elements
Packit 4a16fb
 * \param tt_cused Used client elements
Packit 4a16fb
 * \param tt_sused Used slave elements
Packit 4a16fb
 * \param schannels Slave channels
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
Packit 4a16fb
			      unsigned int tt_csize, unsigned int tt_ssize,
Packit 4a16fb
			      unsigned int *tt_cused, unsigned int *tt_sused,
Packit 4a16fb
			      int schannels)
Packit 4a16fb
{
Packit 4a16fb
	return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
Packit 4a16fb
					  tt_cused, tt_sused, schannels, NULL);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*! \page pcm_plugins
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_route Plugin: Route & Volume
Packit 4a16fb
Packit 4a16fb
This plugin converts channels and applies volume during the conversion.
Packit 4a16fb
The format and rate must match for both of them.
Packit 4a16fb
Packit 4a16fb
SCHANNEL can be a channel name instead of a number (e g FL, LFE).
Packit 4a16fb
If so, a matching channel map will be selected for the slave.
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.name {
Packit 4a16fb
        type route              # Route & Volume conversion PCM
Packit 4a16fb
        slave STR               # Slave name
Packit 4a16fb
        # or
Packit 4a16fb
        slave {                 # Slave definition
Packit 4a16fb
                pcm STR         # Slave PCM name
Packit 4a16fb
                # or
Packit 4a16fb
                pcm { }         # Slave PCM definition
Packit 4a16fb
                [format STR]    # Slave format
Packit 4a16fb
                [channels INT]  # Slave channels
Packit 4a16fb
        }
Packit 4a16fb
        ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
Packit 4a16fb
                CCHANNEL {
Packit 4a16fb
                        SCHANNEL REAL   # route value (0.0 - 1.0)
Packit 4a16fb
                }
Packit 4a16fb
        }
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
\subsection pcm_plugins_route_funcref Function reference
Packit 4a16fb
Packit 4a16fb
    Packit 4a16fb
      
  • snd_pcm_route_open()
  • Packit 4a16fb
      
  • _snd_pcm_route_open()
  • Packit 4a16fb
    Packit 4a16fb
    Packit 4a16fb
    */
    Packit 4a16fb
    Packit 4a16fb
    /**
    Packit 4a16fb
     * \brief Creates a new Route & Volume PCM
    Packit 4a16fb
     * \param pcmp Returns created PCM handle
    Packit 4a16fb
     * \param name Name of PCM
    Packit 4a16fb
     * \param root Root configuration node
    Packit 4a16fb
     * \param conf Configuration node with Route & Volume PCM description
    Packit 4a16fb
     * \param stream Stream type
    Packit 4a16fb
     * \param mode Stream mode
    Packit 4a16fb
     * \retval zero on success otherwise a negative error code
    Packit 4a16fb
     * \warning Using of this function might be dangerous in the sense
    Packit 4a16fb
     *          of compatibility reasons. The prototype might be freely
    Packit 4a16fb
     *          changed in future.
    Packit 4a16fb
     */
    Packit 4a16fb
    int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
    Packit 4a16fb
    			snd_config_t *root, snd_config_t *conf, 
    Packit 4a16fb
    			snd_pcm_stream_t stream, int mode)
    Packit 4a16fb
    {
    Packit 4a16fb
    	snd_config_iterator_t i, next;
    Packit 4a16fb
    	int err;
    Packit 4a16fb
    	snd_pcm_t *spcm;
    Packit 4a16fb
    	snd_config_t *slave = NULL, *sconf;
    Packit 4a16fb
    	snd_pcm_chmap_t *tt_chmap = NULL, *chmap = NULL;
    Packit 4a16fb
    	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    Packit 4a16fb
    	int schannels = -1;
    Packit 4a16fb
    	snd_config_t *tt = NULL;
    Packit 4a16fb
    	snd_pcm_route_ttable_entry_t *ttable = NULL;
    Packit 4a16fb
    	unsigned int csize, ssize;
    Packit 4a16fb
    	unsigned int cused, sused;
    Packit 4a16fb
    	snd_config_for_each(i, next, conf) {
    Packit 4a16fb
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit 4a16fb
    		const char *id;
    Packit 4a16fb
    		if (snd_config_get_id(n, &id) < 0)
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (snd_pcm_conf_generic_id(id))
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (strcmp(id, "slave") == 0) {
    Packit 4a16fb
    			slave = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "ttable") == 0) {
    Packit 4a16fb
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				return -EINVAL;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			tt = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		SNDERR("Unknown field %s", id);
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (!slave) {
    Packit 4a16fb
    		SNDERR("slave is not defined");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (!tt) {
    Packit 4a16fb
    		SNDERR("ttable is not defined");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
    Packit 4a16fb
    	    snd_pcm_format_linear(sformat) != 1) {
    Packit 4a16fb
    	    	snd_config_delete(sconf);
    Packit 4a16fb
    		SNDERR("slave format is not linear");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	err = determine_chmap(tt, &tt_chmap);
    Packit 4a16fb
    	if (err < 0) {
    Packit 4a16fb
    		free(ttable);
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit 4a16fb
    	snd_config_delete(sconf);
    Packit 4a16fb
    	if (err < 0) {
    Packit 4a16fb
    		free(tt_chmap);
    Packit 4a16fb
    		free(ttable);
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	if (tt_chmap) {
    Packit 4a16fb
    		err = find_matching_chmap(spcm, tt_chmap, &chmap, &schannels);
    Packit 4a16fb
    		free(tt_chmap);
    Packit 4a16fb
    		if (err < 0) {
    Packit 4a16fb
    			snd_pcm_close(spcm);
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		}
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
    Packit 4a16fb
    	if (err < 0) {
    Packit 4a16fb
    		free(chmap);
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
    Packit 4a16fb
    	if (ttable == NULL) {
    Packit 4a16fb
    		free(chmap);
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    		return -ENOMEM;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
    Packit 4a16fb
    					&cused, &sused, schannels, chmap);
    Packit 4a16fb
    	if (err < 0) {
    Packit 4a16fb
    		free(chmap);
    Packit 4a16fb
    		free(ttable);
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	err = snd_pcm_route_open(pcmp, name, sformat, schannels,
    Packit 4a16fb
    				 ttable, ssize,
    Packit 4a16fb
    				 cused, sused,
    Packit 4a16fb
    				 spcm, 1);
    Packit 4a16fb
    	free(ttable);
    Packit 4a16fb
    	if (err < 0) {
    Packit 4a16fb
    		free(chmap);
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    	} else {
    Packit 4a16fb
    		((snd_pcm_route_t*) (*pcmp)->private_data)->chmap = chmap;
    Packit 4a16fb
    	}
    Packit 4a16fb
    Packit 4a16fb
    	return err;
    Packit 4a16fb
    }
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
    Packit 4a16fb
    #endif