Blame src/pcm/pcm_route.c

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