Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_route.c
      3  * \ingroup PCM_Plugins
      4  * \brief PCM Route & Volume Plugin Interface
      5  * \author Abramo Bagnara <abramo (at) alsa-project.org>
      6  * \date 2000-2001
      7  */
      8 /*
      9  *  PCM - Route & Volume Plugin
     10  *  Copyright (c) 2000 by Abramo Bagnara <abramo (at) alsa-project.org>
     11  *
     12  *
     13  *   This library is free software; you can redistribute it and/or modify
     14  *   it under the terms of the GNU Lesser General Public License as
     15  *   published by the Free Software Foundation; either version 2.1 of
     16  *   the License, or (at your option) any later version.
     17  *
     18  *   This program is distributed in the hope that it will be useful,
     19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21  *   GNU Lesser General Public License for more details.
     22  *
     23  *   You should have received a copy of the GNU Lesser General Public
     24  *   License along with this library; if not, write to the Free Software
     25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     26  *
     27  */
     28 
     29 #include <byteswap.h>
     30 #include <math.h>
     31 #include "pcm_local.h"
     32 #include "pcm_plugin.h"
     33 
     34 #include "plugin_ops.h"
     35 
     36 #ifndef PIC
     37 /* entry for static linking */
     38 const char *_snd_module_pcm_route = "";
     39 #endif
     40 
     41 #ifndef DOC_HIDDEN
     42 
     43 /* The best possible hack to support missing optimization in gcc 2.7.2.3 */
     44 #if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
     45 #define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
     46 #elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
     47 #define div(a) a >>= 4
     48 #else
     49 #error "Add some code here"
     50 #endif
     51 
     52 typedef struct {
     53 	int channel;
     54 	int as_int;
     55 #if SND_PCM_PLUGIN_ROUTE_FLOAT
     56 	float as_float;
     57 #endif
     58 } snd_pcm_route_ttable_src_t;
     59 
     60 typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
     61 
     62 typedef struct {
     63 	enum {UINT32=0, UINT64=1, FLOAT=2} sum_idx;
     64 	unsigned int get_idx;
     65 	unsigned int put_idx;
     66 	unsigned int conv_idx;
     67 	int use_getput;
     68 	unsigned int src_size;
     69 	snd_pcm_format_t dst_sfmt;
     70 	unsigned int ndsts;
     71 	snd_pcm_route_ttable_dst_t *dsts;
     72 } snd_pcm_route_params_t;
     73 
     74 
     75 typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
     76 			snd_pcm_uframes_t dst_offset,
     77 			const snd_pcm_channel_area_t *src_areas,
     78 			snd_pcm_uframes_t src_offset,
     79 			unsigned int src_channels,
     80 			snd_pcm_uframes_t frames,
     81 			const snd_pcm_route_ttable_dst_t *ttable,
     82 			const snd_pcm_route_params_t *params);
     83 
     84 struct snd_pcm_route_ttable_dst {
     85 	int att;	/* Attenuated */
     86 	unsigned int nsrcs;
     87 	snd_pcm_route_ttable_src_t* srcs;
     88 	route_f func;
     89 };
     90 
     91 typedef union {
     92 	int32_t as_sint32;
     93 	int64_t as_sint64;
     94 #if SND_PCM_PLUGIN_ROUTE_FLOAT
     95 	float as_float;
     96 #endif
     97 } sum_t;
     98 
     99 typedef struct {
    100 	/* This field need to be the first */
    101 	snd_pcm_plugin_t plug;
    102 	snd_pcm_format_t sformat;
    103 	int schannels;
    104 	snd_pcm_route_params_t params;
    105 } snd_pcm_route_t;
    106 
    107 #endif /* DOC_HIDDEN */
    108 
    109 static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
    110 					snd_pcm_uframes_t dst_offset,
    111 					const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
    112 					snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
    113 					unsigned int src_channels ATTRIBUTE_UNUSED,
    114 					snd_pcm_uframes_t frames,
    115 					const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
    116 					const snd_pcm_route_params_t *params)
    117 {
    118 	snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
    119 }
    120 
    121 #ifndef DOC_HIDDEN
    122 
    123 static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
    124 				       snd_pcm_uframes_t dst_offset,
    125 				       const snd_pcm_channel_area_t *src_areas,
    126 				       snd_pcm_uframes_t src_offset,
    127 				       unsigned int src_channels,
    128 				       snd_pcm_uframes_t frames,
    129 				       const snd_pcm_route_ttable_dst_t* ttable,
    130 				       const snd_pcm_route_params_t *params)
    131 {
    132 #define CONV_LABELS
    133 #include "plugin_ops.h"
    134 #undef CONV_LABELS
    135 	void *conv;
    136 	const snd_pcm_channel_area_t *src_area = 0;
    137 	unsigned int srcidx;
    138 	const char *src;
    139 	char *dst;
    140 	int src_step, dst_step;
    141 	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
    142 		unsigned int channel = ttable->srcs[srcidx].channel;
    143 		if (channel >= src_channels)
    144 			continue;
    145 		src_area = &src_areas[channel];
    146 		if (src_area->addr != NULL)
    147 			break;
    148 	}
    149 	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
    150 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
    151 					    src_areas, src_offset,
    152 					    src_channels,
    153 					    frames, ttable, params);
    154 		return;
    155 	}
    156 
    157 	conv = conv_labels[params->conv_idx];
    158 	src = snd_pcm_channel_area_addr(src_area, src_offset);
    159 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
    160 	src_step = snd_pcm_channel_area_step(src_area);
    161 	dst_step = snd_pcm_channel_area_step(dst_area);
    162 	while (frames-- > 0) {
    163 		goto *conv;
    164 #define CONV_END after
    165 #include "plugin_ops.h"
    166 #undef CONV_END
    167 	after:
    168 		src += src_step;
    169 		dst += dst_step;
    170 	}
    171 }
    172 
    173 static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
    174 					      snd_pcm_uframes_t dst_offset,
    175 					      const snd_pcm_channel_area_t *src_areas,
    176 					      snd_pcm_uframes_t src_offset,
    177 					      unsigned int src_channels,
    178 					      snd_pcm_uframes_t frames,
    179 					      const snd_pcm_route_ttable_dst_t* ttable,
    180 					      const snd_pcm_route_params_t *params)
    181 {
    182 #define CONV24_LABELS
    183 #include "plugin_ops.h"
    184 #undef CONV24_LABELS
    185 	void *get, *put;
    186 	const snd_pcm_channel_area_t *src_area = 0;
    187 	unsigned int srcidx;
    188 	const char *src;
    189 	char *dst;
    190 	int src_step, dst_step;
    191 	u_int32_t sample = 0;
    192 	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
    193 		unsigned int channel = ttable->srcs[srcidx].channel;
    194 		if (channel >= src_channels)
    195 			continue;
    196 		src_area = &src_areas[channel];
    197 		if (src_area->addr != NULL)
    198 			break;
    199 	}
    200 	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
    201 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
    202 					    src_areas, src_offset,
    203 					    src_channels,
    204 					    frames, ttable, params);
    205 		return;
    206 	}
    207 
    208 	get = get32_labels[params->get_idx];
    209 	put = put32_labels[params->put_idx];
    210 	src = snd_pcm_channel_area_addr(src_area, src_offset);
    211 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
    212 	src_step = snd_pcm_channel_area_step(src_area);
    213 	dst_step = snd_pcm_channel_area_step(dst_area);
    214 	while (frames-- > 0) {
    215 		goto *get;
    216 #define CONV24_END after
    217 #include "plugin_ops.h"
    218 #undef CONV24_END
    219 	after:
    220 		src += src_step;
    221 		dst += dst_step;
    222 	}
    223 }
    224 
    225 static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
    226 					snd_pcm_uframes_t dst_offset,
    227 					const snd_pcm_channel_area_t *src_areas,
    228 					snd_pcm_uframes_t src_offset,
    229 					unsigned int src_channels,
    230 					snd_pcm_uframes_t frames,
    231 					const snd_pcm_route_ttable_dst_t* ttable,
    232 					const snd_pcm_route_params_t *params)
    233 {
    234 #define GETS_LABELS
    235 #define PUT32_LABELS
    236 #include "plugin_ops.h"
    237 #undef GETS_LABELS
    238 #undef PUT32_LABELS
    239 	static void *const zero_labels[3] = {
    240 		&&zero_int32, &&zero_int64,
    241 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    242 		&&zero_float
    243 #endif
    244 	};
    245 	/* sum_type att */
    246 	static void *const add_labels[3 * 2] = {
    247 		&&add_int32_noatt, &&add_int32_att,
    248 		&&add_int64_noatt, &&add_int64_att,
    249 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    250 		&&add_float_noatt, &&add_float_att
    251 #endif
    252 	};
    253 	/* sum_type att shift */
    254 	static void *const norm_labels[3 * 2 * 4] = {
    255 		0,
    256 		&&norm_int32_8_noatt,
    257 		&&norm_int32_16_noatt,
    258 		&&norm_int32_24_noatt,
    259 		0,
    260 		&&norm_int32_8_att,
    261 		&&norm_int32_16_att,
    262 		&&norm_int32_24_att,
    263 		&&norm_int64_0_noatt,
    264 		&&norm_int64_8_noatt,
    265 		&&norm_int64_16_noatt,
    266 		&&norm_int64_24_noatt,
    267 		&&norm_int64_0_att,
    268 		&&norm_int64_8_att,
    269 		&&norm_int64_16_att,
    270 		&&norm_int64_24_att,
    271 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    272 		&&norm_float_0,
    273 		&&norm_float_8,
    274 		&&norm_float_16,
    275 		&&norm_float_24,
    276 		&&norm_float_0,
    277 		&&norm_float_8,
    278 		&&norm_float_16,
    279 		&&norm_float_24,
    280 #endif
    281 	};
    282 	void *zero, *get, *add, *norm, *put32;
    283 	int nsrcs = ttable->nsrcs;
    284 	char *dst;
    285 	int dst_step;
    286 	const char *srcs[nsrcs];
    287 	int src_steps[nsrcs];
    288 	snd_pcm_route_ttable_src_t src_tt[nsrcs];
    289 	int32_t sample = 0;
    290 	int srcidx, srcidx1 = 0;
    291 	for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
    292 		const snd_pcm_channel_area_t *src_area;
    293 		unsigned int channel = ttable->srcs[srcidx].channel;
    294 		if (channel >= src_channels)
    295 			continue;
    296 		src_area = &src_areas[channel];
    297 		srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
    298 		src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
    299 		src_tt[srcidx1] = ttable->srcs[srcidx];
    300 		srcidx1++;
    301 	}
    302 	nsrcs = srcidx1;
    303 	if (nsrcs == 0) {
    304 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
    305 					    src_areas, src_offset,
    306 					    src_channels,
    307 					    frames, ttable, params);
    308 		return;
    309 	} else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
    310 		if (params->use_getput)
    311 			snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
    312 							  src_areas, src_offset,
    313 							  src_channels,
    314 							  frames, ttable, params);
    315 		else
    316 			snd_pcm_route_convert1_one(dst_area, dst_offset,
    317 						   src_areas, src_offset,
    318 						   src_channels,
    319 						   frames, ttable, params);
    320 		return;
    321 	}
    322 
    323 	zero = zero_labels[params->sum_idx];
    324 	get = gets_labels[params->get_idx];
    325 	add = add_labels[params->sum_idx * 2 + ttable->att];
    326 	norm = norm_labels[params->sum_idx * 8 + ttable->att * 4 + 4 - params->src_size];
    327 	put32 = put32_labels[params->put_idx];
    328 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
    329 	dst_step = snd_pcm_channel_area_step(dst_area);
    330 
    331 	while (frames-- > 0) {
    332 		snd_pcm_route_ttable_src_t *ttp = src_tt;
    333 		sum_t sum;
    334 
    335 		/* Zero sum */
    336 		goto *zero;
    337 	zero_int32:
    338 		sum.as_sint32 = 0;
    339 		goto zero_end;
    340 	zero_int64:
    341 		sum.as_sint64 = 0;
    342 		goto zero_end;
    343 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    344 	zero_float:
    345 		sum.as_float = 0.0;
    346 		goto zero_end;
    347 #endif
    348 	zero_end:
    349 		for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
    350 			const char *src = srcs[srcidx];
    351 
    352 			/* Get sample */
    353 			goto *get;
    354 #define GETS_END after_get
    355 #include "plugin_ops.h"
    356 #undef GETS_END
    357 		after_get:
    358 
    359 			/* Sum */
    360 			goto *add;
    361 		add_int32_att:
    362 			sum.as_sint32 += sample * ttp->as_int;
    363 			goto after_sum;
    364 		add_int32_noatt:
    365 			if (ttp->as_int)
    366 				sum.as_sint32 += sample;
    367 			goto after_sum;
    368 		add_int64_att:
    369 			sum.as_sint64 += (int64_t) sample * ttp->as_int;
    370 			goto after_sum;
    371 		add_int64_noatt:
    372 			if (ttp->as_int)
    373 				sum.as_sint64 += sample;
    374 			goto after_sum;
    375 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    376 		add_float_att:
    377 			sum.as_float += sample * ttp->as_float;
    378 			goto after_sum;
    379 		add_float_noatt:
    380 			if (ttp->as_int)
    381 				sum.as_float += sample;
    382 			goto after_sum;
    383 #endif
    384 		after_sum:
    385 			srcs[srcidx] += src_steps[srcidx];
    386 			ttp++;
    387 		}
    388 
    389 		/* Normalization */
    390 		goto *norm;
    391 	norm_int32_8_att:
    392 		sum.as_sint64 = sum.as_sint32;
    393 	norm_int64_8_att:
    394 		sum.as_sint64 <<= 8;
    395 	norm_int64_0_att:
    396 		div(sum.as_sint64);
    397 		goto norm_int;
    398 
    399 	norm_int32_16_att:
    400 		sum.as_sint64 = sum.as_sint32;
    401 	norm_int64_16_att:
    402 		sum.as_sint64 <<= 16;
    403 		div(sum.as_sint64);
    404 		goto norm_int;
    405 
    406 	norm_int32_24_att:
    407 		sum.as_sint64 = sum.as_sint32;
    408 	norm_int64_24_att:
    409 		sum.as_sint64 <<= 24;
    410 		div(sum.as_sint64);
    411 		goto norm_int;
    412 
    413 	norm_int32_8_noatt:
    414 		sum.as_sint64 = sum.as_sint32;
    415 	norm_int64_8_noatt:
    416 		sum.as_sint64 <<= 8;
    417 		goto norm_int;
    418 
    419 	norm_int32_16_noatt:
    420 		sum.as_sint64 = sum.as_sint32;
    421 	norm_int64_16_noatt:
    422 		sum.as_sint64 <<= 16;
    423 		goto norm_int;
    424 
    425 	norm_int32_24_noatt:
    426 		sum.as_sint64 = sum.as_sint32;
    427 	norm_int64_24_noatt:
    428 		sum.as_sint64 <<= 24;
    429 		goto norm_int;
    430 
    431 	norm_int64_0_noatt:
    432 	norm_int:
    433 		if (sum.as_sint64 > (int64_t)0x7fffffff)
    434 			sample = 0x7fffffff;	/* maximum positive value */
    435 		else if (sum.as_sint64 < -(int64_t)0x80000000)
    436 			sample = 0x80000000;	/* maximum negative value */
    437 		else
    438 			sample = sum.as_sint64;
    439 		goto after_norm;
    440 
    441 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    442 	norm_float_8:
    443 		sum.as_float *= 1 << 8;
    444 		goto norm_float;
    445 	norm_float_16:
    446 		sum.as_float *= 1 << 16;
    447 		goto norm_float;
    448 	norm_float_24:
    449 		sum.as_float *= 1 << 24;
    450 		goto norm_float;
    451 	norm_float_0:
    452 	norm_float:
    453 		sum.as_float = rint(sum.as_float);
    454 		if (sum.as_float > (int64_t)0x7fffffff)
    455 			sample = 0x7fffffff;	/* maximum positive value */
    456 		else if (sum.as_float < -(int64_t)0x80000000)
    457 			sample = 0x80000000;	/* maximum negative value */
    458 		else
    459 			sample = sum.as_float;
    460 		goto after_norm;
    461 #endif
    462 	after_norm:
    463 
    464 		/* Put sample */
    465 		goto *put32;
    466 #define PUT32_END after_put32
    467 #include "plugin_ops.h"
    468 #undef PUT32_END
    469 	after_put32:
    470 
    471 		dst += dst_step;
    472 	}
    473 }
    474 
    475 #endif /* DOC_HIDDEN */
    476 
    477 static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
    478 				  snd_pcm_uframes_t dst_offset,
    479 				  const snd_pcm_channel_area_t *src_areas,
    480 				  snd_pcm_uframes_t src_offset,
    481 				  unsigned int src_channels,
    482 				  unsigned int dst_channels,
    483 				  snd_pcm_uframes_t frames,
    484 				  snd_pcm_route_params_t *params)
    485 {
    486 	unsigned int dst_channel;
    487 	snd_pcm_route_ttable_dst_t *dstp;
    488 	const snd_pcm_channel_area_t *dst_area;
    489 
    490 	dstp = params->dsts;
    491 	dst_area = dst_areas;
    492 	for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
    493 		if (dst_channel >= params->ndsts)
    494 			snd_pcm_route_convert1_zero(dst_area, dst_offset,
    495 						    src_areas, src_offset,
    496 						    src_channels,
    497 						    frames, dstp, params);
    498 		else
    499 			dstp->func(dst_area, dst_offset,
    500 				   src_areas, src_offset,
    501 				   src_channels,
    502 				   frames, dstp, params);
    503 		dstp++;
    504 		dst_area++;
    505 	}
    506 }
    507 
    508 static int snd_pcm_route_close(snd_pcm_t *pcm)
    509 {
    510 	snd_pcm_route_t *route = pcm->private_data;
    511 	snd_pcm_route_params_t *params = &route->params;
    512 	unsigned int dst_channel;
    513 
    514 	if (params->dsts) {
    515 		for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
    516 			free(params->dsts[dst_channel].srcs);
    517 		}
    518 		free(params->dsts);
    519 	}
    520 	return snd_pcm_generic_close(pcm);
    521 }
    522 
    523 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
    524 {
    525 	int err;
    526 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
    527 	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
    528 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
    529 					 &access_mask);
    530 	if (err < 0)
    531 		return err;
    532 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
    533 					 &format_mask);
    534 	if (err < 0)
    535 		return err;
    536 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
    537 	if (err < 0)
    538 		return err;
    539 	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
    540 	if (err < 0)
    541 		return err;
    542 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
    543 	return 0;
    544 }
    545 
    546 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
    547 {
    548 	snd_pcm_route_t *route = pcm->private_data;
    549 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
    550 	_snd_pcm_hw_params_any(sparams);
    551 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
    552 				   &saccess_mask);
    553 	if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
    554 		_snd_pcm_hw_params_set_format(sparams, route->sformat);
    555 		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
    556 	}
    557 	if (route->schannels >= 0) {
    558 		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
    559 				      (unsigned int) route->schannels, 0);
    560 	}
    561 	return 0;
    562 }
    563 
    564 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
    565 					    snd_pcm_hw_params_t *sparams)
    566 {
    567 	snd_pcm_route_t *route = pcm->private_data;
    568 	int err;
    569 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
    570 			      SND_PCM_HW_PARBIT_PERIODS |
    571 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
    572 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
    573 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
    574 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
    575 			      SND_PCM_HW_PARBIT_TICK_TIME);
    576 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
    577 		links |= (SND_PCM_HW_PARBIT_FORMAT |
    578 			  SND_PCM_HW_PARBIT_SUBFORMAT |
    579 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
    580 	if (route->schannels < 0)
    581 		links |= SND_PCM_HW_PARBIT_CHANNELS;
    582 	err = _snd_pcm_hw_params_refine(sparams, links, params);
    583 	if (err < 0)
    584 		return err;
    585 	return 0;
    586 }
    587 
    588 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
    589 					    snd_pcm_hw_params_t *sparams)
    590 {
    591 	snd_pcm_route_t *route = pcm->private_data;
    592 	int err;
    593 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
    594 			      SND_PCM_HW_PARBIT_PERIODS |
    595 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
    596 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
    597 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
    598 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
    599 			      SND_PCM_HW_PARBIT_TICK_TIME);
    600 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
    601 		links |= (SND_PCM_HW_PARBIT_FORMAT |
    602 			  SND_PCM_HW_PARBIT_SUBFORMAT |
    603 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
    604 	if (route->schannels < 0)
    605 		links |= SND_PCM_HW_PARBIT_CHANNELS;
    606 	err = _snd_pcm_hw_params_refine(params, links, sparams);
    607 	if (err < 0)
    608 		return err;
    609 	return 0;
    610 }
    611 
    612 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
    613 {
    614 	return snd_pcm_hw_refine_slave(pcm, params,
    615 				       snd_pcm_route_hw_refine_cprepare,
    616 				       snd_pcm_route_hw_refine_cchange,
    617 				       snd_pcm_route_hw_refine_sprepare,
    618 				       snd_pcm_route_hw_refine_schange,
    619 				       snd_pcm_generic_hw_refine);
    620 }
    621 
    622 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
    623 {
    624 	snd_pcm_route_t *route = pcm->private_data;
    625 	snd_pcm_t *slave = route->plug.gen.slave;
    626 	snd_pcm_format_t src_format, dst_format;
    627 	int err = snd_pcm_hw_params_slave(pcm, params,
    628 					  snd_pcm_route_hw_refine_cchange,
    629 					  snd_pcm_route_hw_refine_sprepare,
    630 					  snd_pcm_route_hw_refine_schange,
    631 					  snd_pcm_generic_hw_params);
    632 	if (err < 0)
    633 		return err;
    634 
    635 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
    636 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
    637 		dst_format = slave->format;
    638 	} else {
    639 		src_format = slave->format;
    640 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
    641 	}
    642 	if (err < 0)
    643 		return err;
    644 	route->params.use_getput = snd_pcm_format_physical_width(src_format) == 24 ||
    645 		snd_pcm_format_physical_width(dst_format) == 24;
    646 	route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S16);
    647 	route->params.put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, dst_format);
    648 	route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
    649 	route->params.src_size = snd_pcm_format_width(src_format) / 8;
    650 	route->params.dst_sfmt = dst_format;
    651 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    652 	route->params.sum_idx = FLOAT;
    653 #else
    654 	if (snd_pcm_format_width(src_format) == 32)
    655 		route->params.sum_idx = UINT64;
    656 	else
    657 		route->params.sum_idx = UINT32;
    658 #endif
    659 	return 0;
    660 }
    661 
    662 static snd_pcm_uframes_t
    663 snd_pcm_route_write_areas(snd_pcm_t *pcm,
    664 			  const snd_pcm_channel_area_t *areas,
    665 			  snd_pcm_uframes_t offset,
    666 			  snd_pcm_uframes_t size,
    667 			  const snd_pcm_channel_area_t *slave_areas,
    668 			  snd_pcm_uframes_t slave_offset,
    669 			  snd_pcm_uframes_t *slave_sizep)
    670 {
    671 	snd_pcm_route_t *route = pcm->private_data;
    672 	snd_pcm_t *slave = route->plug.gen.slave;
    673 	if (size > *slave_sizep)
    674 		size = *slave_sizep;
    675 	snd_pcm_route_convert(slave_areas, slave_offset,
    676 			      areas, offset,
    677 			      pcm->channels,
    678 			      slave->channels,
    679 			      size, &route->params);
    680 	*slave_sizep = size;
    681 	return size;
    682 }
    683 
    684 static snd_pcm_uframes_t
    685 snd_pcm_route_read_areas(snd_pcm_t *pcm,
    686 			 const snd_pcm_channel_area_t *areas,
    687 			 snd_pcm_uframes_t offset,
    688 			 snd_pcm_uframes_t size,
    689 			 const snd_pcm_channel_area_t *slave_areas,
    690 			 snd_pcm_uframes_t slave_offset,
    691 			 snd_pcm_uframes_t *slave_sizep)
    692 {
    693 	snd_pcm_route_t *route = pcm->private_data;
    694 	snd_pcm_t *slave = route->plug.gen.slave;
    695 	if (size > *slave_sizep)
    696 		size = *slave_sizep;
    697 	snd_pcm_route_convert(areas, offset,
    698 			      slave_areas, slave_offset,
    699 			      slave->channels,
    700 			      pcm->channels,
    701 			      size, &route->params);
    702 	*slave_sizep = size;
    703 	return size;
    704 }
    705 
    706 static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
    707 {
    708 	snd_pcm_route_t *route = pcm->private_data;
    709 	unsigned int dst;
    710 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
    711 		snd_output_printf(out, "Route conversion PCM\n");
    712 	else
    713 		snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
    714 			snd_pcm_format_name(route->sformat));
    715 	snd_output_puts(out, "  Transformation table:\n");
    716 	for (dst = 0; dst < route->params.ndsts; dst++) {
    717 		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
    718 		unsigned int src;
    719 		snd_output_printf(out, "    %d <- ", dst);
    720 		if (d->nsrcs == 0) {
    721 			snd_output_printf(out, "none\n");
    722 			continue;
    723 		}
    724 		src = 0;
    725 		while (1) {
    726 			snd_pcm_route_ttable_src_t *s = &d->srcs[src];
    727 			if (d->att)
    728 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    729 				snd_output_printf(out, "%d*%g", s->channel, s->as_float);
    730 #else
    731 				snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
    732 #endif
    733 			else
    734 				snd_output_printf(out, "%d", s->channel);
    735 			src++;
    736 			if (src == d->nsrcs)
    737 				break;
    738 			snd_output_puts(out, " + ");
    739 		}
    740 		snd_output_putc(out, '\n');
    741 	}
    742 	if (pcm->setup) {
    743 		snd_output_printf(out, "Its setup is:\n");
    744 		snd_pcm_dump_setup(pcm, out);
    745 	}
    746 	snd_output_printf(out, "Slave: ");
    747 	snd_pcm_dump(route->plug.gen.slave, out);
    748 }
    749 
    750 static const snd_pcm_ops_t snd_pcm_route_ops = {
    751 	.close = snd_pcm_route_close,
    752 	.info = snd_pcm_generic_info,
    753 	.hw_refine = snd_pcm_route_hw_refine,
    754 	.hw_params = snd_pcm_route_hw_params,
    755 	.hw_free = snd_pcm_generic_hw_free,
    756 	.sw_params = snd_pcm_generic_sw_params,
    757 	.channel_info = snd_pcm_generic_channel_info,
    758 	.dump = snd_pcm_route_dump,
    759 	.nonblock = snd_pcm_generic_nonblock,
    760 	.async = snd_pcm_generic_async,
    761 	.mmap = snd_pcm_generic_mmap,
    762 	.munmap = snd_pcm_generic_munmap,
    763 };
    764 
    765 static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
    766 			     unsigned int tt_ssize,
    767 			     snd_pcm_route_ttable_entry_t *ttable,
    768 			     unsigned int tt_cused, unsigned int tt_sused)
    769 {
    770 	unsigned int src_channel, dst_channel;
    771 	snd_pcm_route_ttable_dst_t *dptr;
    772 	unsigned int sused, dused, smul, dmul;
    773 	if (stream == SND_PCM_STREAM_PLAYBACK) {
    774 		sused = tt_cused;
    775 		dused = tt_sused;
    776 		smul = tt_ssize;
    777 		dmul = 1;
    778 	} else {
    779 		sused = tt_sused;
    780 		dused = tt_cused;
    781 		smul = 1;
    782 		dmul = tt_ssize;
    783 	}
    784 	params->ndsts = dused;
    785 	dptr = calloc(dused, sizeof(*params->dsts));
    786 	if (!dptr)
    787 		return -ENOMEM;
    788 	params->dsts = dptr;
    789 	for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
    790 		snd_pcm_route_ttable_entry_t t = 0;
    791 		int att = 0;
    792 		int nsrcs = 0;
    793 		snd_pcm_route_ttable_src_t srcs[sused];
    794 		for (src_channel = 0; src_channel < sused; ++src_channel) {
    795 			snd_pcm_route_ttable_entry_t v;
    796 			v = ttable[src_channel * smul + dst_channel * dmul];
    797 			if (v != 0) {
    798 				srcs[nsrcs].channel = src_channel;
    799 #if SND_PCM_PLUGIN_ROUTE_FLOAT
    800 				/* Also in user space for non attenuated */
    801 				srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
    802 				srcs[nsrcs].as_float = v;
    803 #else
    804 				assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
    805 				srcs[nsrcs].as_int = v;
    806 #endif
    807 				if (v != SND_PCM_PLUGIN_ROUTE_FULL)
    808 					att = 1;
    809 				t += v;
    810 				nsrcs++;
    811 			}
    812 		}
    813 #if 0
    814 		assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
    815 #endif
    816 		dptr->att = att;
    817 		dptr->nsrcs = nsrcs;
    818 		if (nsrcs == 0)
    819 			dptr->func = snd_pcm_route_convert1_zero;
    820 		else
    821 			dptr->func = snd_pcm_route_convert1_many;
    822 		if (nsrcs > 0) {
    823 			dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
    824 			if (!dptr->srcs)
    825 				return -ENOMEM;
    826 			memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
    827 		} else
    828 			dptr->srcs = 0;
    829 		dptr++;
    830 	}
    831 	return 0;
    832 }
    833 
    834 /**
    835  * \brief Creates a new Route & Volume PCM
    836  * \param pcmp Returns created PCM handle
    837  * \param name Name of PCM
    838  * \param sformat Slave format
    839  * \param schannels Slave channels
    840  * \param ttable Attenuation table
    841  * \param tt_ssize Attenuation table - slave size
    842  * \param tt_cused Attenuation table - client used count
    843  * \param tt_sused Attenuation table - slave used count
    844  * \param slave Slave PCM handle
    845  * \param close_slave When set, the slave PCM handle is closed with copy PCM
    846  * \retval zero on success otherwise a negative error code
    847  * \warning Using of this function might be dangerous in the sense
    848  *          of compatibility reasons. The prototype might be freely
    849  *          changed in future.
    850  */
    851 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
    852 		       snd_pcm_format_t sformat, int schannels,
    853 		       snd_pcm_route_ttable_entry_t *ttable,
    854 		       unsigned int tt_ssize,
    855 		       unsigned int tt_cused, unsigned int tt_sused,
    856 		       snd_pcm_t *slave, int close_slave)
    857 {
    858 	snd_pcm_t *pcm;
    859 	snd_pcm_route_t *route;
    860 	int err;
    861 	assert(pcmp && slave && ttable);
    862 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
    863 	    snd_pcm_format_linear(sformat) != 1)
    864 		return -EINVAL;
    865 	route = calloc(1, sizeof(snd_pcm_route_t));
    866 	if (!route) {
    867 		return -ENOMEM;
    868 	}
    869 	snd_pcm_plugin_init(&route->plug);
    870 	route->sformat = sformat;
    871 	route->schannels = schannels;
    872 	route->plug.read = snd_pcm_route_read_areas;
    873 	route->plug.write = snd_pcm_route_write_areas;
    874 	route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    875 	route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    876 	route->plug.gen.slave = slave;
    877 	route->plug.gen.close_slave = close_slave;
    878 
    879 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
    880 	if (err < 0) {
    881 		free(route);
    882 		return err;
    883 	}
    884 	pcm->ops = &snd_pcm_route_ops;
    885 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
    886 	pcm->private_data = route;
    887 	pcm->poll_fd = slave->poll_fd;
    888 	pcm->poll_events = slave->poll_events;
    889 	pcm->monotonic = slave->monotonic;
    890 	snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
    891 	snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
    892 	err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
    893 	if (err < 0) {
    894 		snd_pcm_close(pcm);
    895 		return err;
    896 	}
    897 	*pcmp = pcm;
    898 
    899 	return 0;
    900 }
    901 
    902 /**
    903  * \brief Determine route matrix sizes
    904  * \param tt Configuration root describing route matrix
    905  * \param tt_csize Returned client size in elements
    906  * \param tt_ssize Returned slave size in elements
    907  * \retval zero on success otherwise a negative error code
    908  */
    909 int snd_pcm_route_determine_ttable(snd_config_t *tt,
    910 				   unsigned int *tt_csize,
    911 				   unsigned int *tt_ssize)
    912 {
    913 	snd_config_iterator_t i, inext;
    914 	long csize = 0, ssize = 0;
    915 	int err;
    916 
    917 	assert(tt && tt_csize && tt_ssize);
    918 	snd_config_for_each(i, inext, tt) {
    919 		snd_config_t *in = snd_config_iterator_entry(i);
    920 		snd_config_iterator_t j, jnext;
    921 		long cchannel;
    922 		const char *id;
    923 		if (!snd_config_get_id(in, &id) < 0)
    924 			continue;
    925 		err = safe_strtol(id, &cchannel);
    926 		if (err < 0) {
    927 			SNDERR("Invalid client channel: %s", id);
    928 			return -EINVAL;
    929 		}
    930 		if (cchannel + 1 > csize)
    931 			csize = cchannel + 1;
    932 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
    933 			return -EINVAL;
    934 		snd_config_for_each(j, jnext, in) {
    935 			snd_config_t *jnode = snd_config_iterator_entry(j);
    936 			long schannel;
    937 			const char *id;
    938 			if (snd_config_get_id(jnode, &id) < 0)
    939 				continue;
    940 			err = safe_strtol(id, &schannel);
    941 			if (err < 0) {
    942 				SNDERR("Invalid slave channel: %s", id);
    943 				return -EINVAL;
    944 			}
    945 			if (schannel + 1 > ssize)
    946 				ssize = schannel + 1;
    947 		}
    948 	}
    949 	if (csize == 0 || ssize == 0) {
    950 		SNDERR("Invalid null ttable configuration");
    951 		return -EINVAL;
    952 	}
    953 	*tt_csize = csize;
    954 	*tt_ssize = ssize;
    955 	return 0;
    956 }
    957 
    958 /**
    959  * \brief Load route matrix
    960  * \param tt Configuration root describing route matrix
    961  * \param ttable Returned route matrix
    962  * \param tt_csize Client size in elements
    963  * \param tt_ssize Slave size in elements
    964  * \param tt_cused Used client elements
    965  * \param tt_sused Used slave elements
    966  * \param schannels Slave channels
    967  * \retval zero on success otherwise a negative error code
    968  */
    969 int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
    970 			      unsigned int tt_csize, unsigned int tt_ssize,
    971 			      unsigned int *tt_cused, unsigned int *tt_sused,
    972 			      int schannels)
    973 {
    974 	int cused = -1;
    975 	int sused = -1;
    976 	snd_config_iterator_t i, inext;
    977 	unsigned int k;
    978 	int err;
    979 	for (k = 0; k < tt_csize * tt_ssize; ++k)
    980 		ttable[k] = 0.0;
    981 	snd_config_for_each(i, inext, tt) {
    982 		snd_config_t *in = snd_config_iterator_entry(i);
    983 		snd_config_iterator_t j, jnext;
    984 		long cchannel;
    985 		const char *id;
    986 		if (!snd_config_get_id(in, &id) < 0)
    987 			continue;
    988 		err = safe_strtol(id, &cchannel);
    989 		if (err < 0 ||
    990 		    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
    991 			SNDERR("Invalid client channel: %s", id);
    992 			return -EINVAL;
    993 		}
    994 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
    995 			return -EINVAL;
    996 		snd_config_for_each(j, jnext, in) {
    997 			snd_config_t *jnode = snd_config_iterator_entry(j);
    998 			double value;
    999 			long schannel;
   1000 			const char *id;
   1001 			if (snd_config_get_id(jnode, &id) < 0)
   1002 				continue;
   1003 			err = safe_strtol(id, &schannel);
   1004 			if (err < 0 ||
   1005 			    schannel < 0 || (unsigned int) schannel > tt_ssize ||
   1006 			    (schannels > 0 && schannel >= schannels)) {
   1007 				SNDERR("Invalid slave channel: %s", id);
   1008 				return -EINVAL;
   1009 			}
   1010 			err = snd_config_get_real(jnode, &value);
   1011 			if (err < 0) {
   1012 				long v;
   1013 				err = snd_config_get_integer(jnode, &v);
   1014 				if (err < 0) {
   1015 					SNDERR("Invalid type for %s", id);
   1016 					return -EINVAL;
   1017 				}
   1018 				value = v;
   1019 			}
   1020 			ttable[cchannel * tt_ssize + schannel] = value;
   1021 			if (schannel > sused)
   1022 				sused = schannel;
   1023 		}
   1024 		if (cchannel > cused)
   1025 			cused = cchannel;
   1026 	}
   1027 	*tt_sused = sused + 1;
   1028 	*tt_cused = cused + 1;
   1029 	return 0;
   1030 }
   1031 
   1032 /*! \page pcm_plugins
   1033 
   1034 \section pcm_plugins_route Plugin: Route & Volume
   1035 
   1036 This plugin converts channels and applies volume during the conversion.
   1037 The format and rate must match for both of them.
   1038 
   1039 \code
   1040 pcm.name {
   1041         type route              # Route & Volume conversion PCM
   1042         slave STR               # Slave name
   1043         # or
   1044         slave {                 # Slave definition
   1045                 pcm STR         # Slave PCM name
   1046                 # or
   1047                 pcm { }         # Slave PCM definition
   1048                 [format STR]    # Slave format
   1049                 [channels INT]  # Slave channels
   1050         }
   1051         ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
   1052                 CCHANNEL {
   1053                         SCHANNEL REAL   # route value (0.0 - 1.0)
   1054                 }
   1055         }
   1056 }
   1057 \endcode
   1058 
   1059 \subsection pcm_plugins_route_funcref Function reference
   1060 
   1061 <UL>
   1062   <LI>snd_pcm_route_open()
   1063   <LI>_snd_pcm_route_open()
   1064 </UL>
   1065 
   1066 */
   1067 
   1068 /**
   1069  * \brief Creates a new Route & Volume PCM
   1070  * \param pcmp Returns created PCM handle
   1071  * \param name Name of PCM
   1072  * \param root Root configuration node
   1073  * \param conf Configuration node with Route & Volume PCM description
   1074  * \param stream Stream type
   1075  * \param mode Stream mode
   1076  * \retval zero on success otherwise a negative error code
   1077  * \warning Using of this function might be dangerous in the sense
   1078  *          of compatibility reasons. The prototype might be freely
   1079  *          changed in future.
   1080  */
   1081 int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
   1082 			snd_config_t *root, snd_config_t *conf,
   1083 			snd_pcm_stream_t stream, int mode)
   1084 {
   1085 	snd_config_iterator_t i, next;
   1086 	int err;
   1087 	snd_pcm_t *spcm;
   1088 	snd_config_t *slave = NULL, *sconf;
   1089 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
   1090 	int schannels = -1;
   1091 	snd_config_t *tt = NULL;
   1092 	snd_pcm_route_ttable_entry_t *ttable = NULL;
   1093 	unsigned int csize, ssize;
   1094 	unsigned int cused, sused;
   1095 	snd_config_for_each(i, next, conf) {
   1096 		snd_config_t *n = snd_config_iterator_entry(i);
   1097 		const char *id;
   1098 		if (snd_config_get_id(n, &id) < 0)
   1099 			continue;
   1100 		if (snd_pcm_conf_generic_id(id))
   1101 			continue;
   1102 		if (strcmp(id, "slave") == 0) {
   1103 			slave = n;
   1104 			continue;
   1105 		}
   1106 		if (strcmp(id, "ttable") == 0) {
   1107 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
   1108 				SNDERR("Invalid type for %s", id);
   1109 				return -EINVAL;
   1110 			}
   1111 			tt = n;
   1112 			continue;
   1113 		}
   1114 		SNDERR("Unknown field %s", id);
   1115 		return -EINVAL;
   1116 	}
   1117 	if (!slave) {
   1118 		SNDERR("slave is not defined");
   1119 		return -EINVAL;
   1120 	}
   1121 	if (!tt) {
   1122 		SNDERR("ttable is not defined");
   1123 		return -EINVAL;
   1124 	}
   1125 	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
   1126 				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
   1127 				 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
   1128 	if (err < 0)
   1129 		return err;
   1130 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
   1131 	    snd_pcm_format_linear(sformat) != 1) {
   1132 	    	snd_config_delete(sconf);
   1133 		SNDERR("slave format is not linear");
   1134 		return -EINVAL;
   1135 	}
   1136 
   1137 	err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
   1138 	if (err < 0) {
   1139 		snd_config_delete(sconf);
   1140 		return err;
   1141 	}
   1142 	ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
   1143 	if (ttable == NULL) {
   1144 		snd_config_delete(sconf);
   1145 		return -ENOMEM;
   1146 	}
   1147 	err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
   1148 					&cused, &sused, schannels);
   1149 	if (err < 0) {
   1150 		free(ttable);
   1151 		snd_config_delete(sconf);
   1152 		return err;
   1153 	}
   1154 
   1155 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
   1156 	snd_config_delete(sconf);
   1157 	if (err < 0) {
   1158 		free(ttable);
   1159 		return err;
   1160 	}
   1161 	err = snd_pcm_route_open(pcmp, name, sformat, schannels,
   1162 				 ttable, ssize,
   1163 				 cused, sused,
   1164 				 spcm, 1);
   1165 	free(ttable);
   1166 	if (err < 0)
   1167 		snd_pcm_close(spcm);
   1168 	return err;
   1169 }
   1170 #ifndef DOC_HIDDEN
   1171 SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
   1172 #endif
   1173