1 /**
2  * \file pcm/pcm_route.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Route & Volume Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Route & Volume Plugin
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28 
29 #include "bswap.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 {UINT64, FLOAT} 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 nsrcs;
71 	unsigned int ndsts;
72 	snd_pcm_route_ttable_dst_t *dsts;
73 } snd_pcm_route_params_t;
74 
75 
76 typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
77 			snd_pcm_uframes_t dst_offset,
78 			const snd_pcm_channel_area_t *src_areas,
79 			snd_pcm_uframes_t src_offset,
80 			unsigned int src_channels,
81 			snd_pcm_uframes_t frames,
82 			const snd_pcm_route_ttable_dst_t *ttable,
83 			const snd_pcm_route_params_t *params);
84 
85 struct snd_pcm_route_ttable_dst {
86 	int att;	/* Attenuated */
87 	unsigned int nsrcs;
88 	snd_pcm_route_ttable_src_t* srcs;
89 	route_f func;
90 };
91 
92 typedef union {
93 	int32_t as_sint32;
94 	int64_t as_sint64;
95 #if SND_PCM_PLUGIN_ROUTE_FLOAT
96 	float as_float;
97 #endif
98 } sum_t;
99 
100 typedef struct {
101 	/* This field need to be the first */
102 	snd_pcm_plugin_t plug;
103 	snd_pcm_format_t sformat;
104 	int schannels;
105 	snd_pcm_route_params_t params;
106 	snd_pcm_chmap_t *chmap;
107 } snd_pcm_route_t;
108 
109 #endif /* DOC_HIDDEN */
110 
snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,unsigned int src_channels ATTRIBUTE_UNUSED,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable ATTRIBUTE_UNUSED,const snd_pcm_route_params_t * params)111 static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
112 					snd_pcm_uframes_t dst_offset,
113 					const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
114 					snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
115 					unsigned int src_channels ATTRIBUTE_UNUSED,
116 					snd_pcm_uframes_t frames,
117 					const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
118 					const snd_pcm_route_params_t *params)
119 {
120 	snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
121 }
122 
123 #ifndef DOC_HIDDEN
124 
snd_pcm_route_convert1_one(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)125 static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
126 				       snd_pcm_uframes_t dst_offset,
127 				       const snd_pcm_channel_area_t *src_areas,
128 				       snd_pcm_uframes_t src_offset,
129 				       unsigned int src_channels,
130 				       snd_pcm_uframes_t frames,
131 				       const snd_pcm_route_ttable_dst_t* ttable,
132 				       const snd_pcm_route_params_t *params)
133 {
134 #define CONV_LABELS
135 #include "plugin_ops.h"
136 #undef CONV_LABELS
137 	void *conv;
138 	const snd_pcm_channel_area_t *src_area = 0;
139 	unsigned int srcidx;
140 	const char *src;
141 	char *dst;
142 	int src_step, dst_step;
143 	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
144 		unsigned int channel = ttable->srcs[srcidx].channel;
145 		if (channel >= src_channels)
146 			continue;
147 		src_area = &src_areas[channel];
148 		if (src_area->addr != NULL)
149 			break;
150 	}
151 	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
152 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
153 					    src_areas, src_offset,
154 					    src_channels,
155 					    frames, ttable, params);
156 		return;
157 	}
158 
159 	conv = conv_labels[params->conv_idx];
160 	src = snd_pcm_channel_area_addr(src_area, src_offset);
161 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
162 	src_step = snd_pcm_channel_area_step(src_area);
163 	dst_step = snd_pcm_channel_area_step(dst_area);
164 	while (frames-- > 0) {
165 		goto *conv;
166 #define CONV_END after
167 #include "plugin_ops.h"
168 #undef CONV_END
169 	after:
170 		src += src_step;
171 		dst += dst_step;
172 	}
173 }
174 
snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)175 static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
176 					      snd_pcm_uframes_t dst_offset,
177 					      const snd_pcm_channel_area_t *src_areas,
178 					      snd_pcm_uframes_t src_offset,
179 					      unsigned int src_channels,
180 					      snd_pcm_uframes_t frames,
181 					      const snd_pcm_route_ttable_dst_t* ttable,
182 					      const snd_pcm_route_params_t *params)
183 {
184 #define CONV24_LABELS
185 #include "plugin_ops.h"
186 #undef CONV24_LABELS
187 	void *get, *put;
188 	const snd_pcm_channel_area_t *src_area = 0;
189 	unsigned int srcidx;
190 	const char *src;
191 	char *dst;
192 	int src_step, dst_step;
193 	uint32_t sample = 0;
194 	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
195 		unsigned int channel = ttable->srcs[srcidx].channel;
196 		if (channel >= src_channels)
197 			continue;
198 		src_area = &src_areas[channel];
199 		if (src_area->addr != NULL)
200 			break;
201 	}
202 	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
203 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
204 					    src_areas, src_offset,
205 					    src_channels,
206 					    frames, ttable, params);
207 		return;
208 	}
209 
210 	get = get32_labels[params->get_idx];
211 	put = put32_labels[params->put_idx];
212 	src = snd_pcm_channel_area_addr(src_area, src_offset);
213 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
214 	src_step = snd_pcm_channel_area_step(src_area);
215 	dst_step = snd_pcm_channel_area_step(dst_area);
216 	while (frames-- > 0) {
217 		goto *get;
218 #define CONV24_END after
219 #include "plugin_ops.h"
220 #undef CONV24_END
221 	after:
222 		src += src_step;
223 		dst += dst_step;
224 	}
225 }
226 
snd_pcm_route_convert1_many(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)227 static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
228 					snd_pcm_uframes_t dst_offset,
229 					const snd_pcm_channel_area_t *src_areas,
230 					snd_pcm_uframes_t src_offset,
231 					unsigned int src_channels,
232 					snd_pcm_uframes_t frames,
233 					const snd_pcm_route_ttable_dst_t* ttable,
234 					const snd_pcm_route_params_t *params)
235 {
236 #define GET32_LABELS
237 #define PUT32_LABELS
238 #include "plugin_ops.h"
239 #undef GET32_LABELS
240 #undef PUT32_LABELS
241 	static void *const zero_labels[2] = {
242 		&&zero_int64,
243 #if SND_PCM_PLUGIN_ROUTE_FLOAT
244 		&&zero_float
245 #endif
246 	};
247 	/* sum_type att */
248 	static void *const add_labels[2 * 2] = {
249 		&&add_int64_noatt, &&add_int64_att,
250 #if SND_PCM_PLUGIN_ROUTE_FLOAT
251 		&&add_float_noatt, &&add_float_att
252 #endif
253 	};
254 	/* sum_type att */
255 	static void *const norm_labels[2 * 2] = {
256 		&&norm_int64_noatt,
257 		&&norm_int64_att,
258 #if SND_PCM_PLUGIN_ROUTE_FLOAT
259 		&&norm_float,
260 		&&norm_float,
261 #endif
262 	};
263 	void *zero, *get32, *add, *norm, *put32;
264 	int nsrcs = ttable->nsrcs;
265 	char *dst;
266 	int dst_step;
267 	const char *srcs[nsrcs];
268 	int src_steps[nsrcs];
269 	snd_pcm_route_ttable_src_t src_tt[nsrcs];
270 	int32_t sample = 0;
271 	int srcidx, srcidx1 = 0;
272 	for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
273 		const snd_pcm_channel_area_t *src_area;
274 		unsigned int channel = ttable->srcs[srcidx].channel;
275 		if (channel >= src_channels)
276 			continue;
277 		src_area = &src_areas[channel];
278 		srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
279 		src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
280 		src_tt[srcidx1] = ttable->srcs[srcidx];
281 		srcidx1++;
282 	}
283 	nsrcs = srcidx1;
284 	if (nsrcs == 0) {
285 		snd_pcm_route_convert1_zero(dst_area, dst_offset,
286 					    src_areas, src_offset,
287 					    src_channels,
288 					    frames, ttable, params);
289 		return;
290 	} else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
291 		if (params->use_getput)
292 			snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
293 							  src_areas, src_offset,
294 							  src_channels,
295 							  frames, ttable, params);
296 		else
297 			snd_pcm_route_convert1_one(dst_area, dst_offset,
298 						   src_areas, src_offset,
299 						   src_channels,
300 						   frames, ttable, params);
301 		return;
302 	}
303 
304 	zero = zero_labels[params->sum_idx];
305 	get32 = get32_labels[params->get_idx];
306 	add = add_labels[params->sum_idx * 2 + ttable->att];
307 	norm = norm_labels[params->sum_idx * 2 + ttable->att];
308 	put32 = put32_labels[params->put_idx];
309 	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
310 	dst_step = snd_pcm_channel_area_step(dst_area);
311 
312 	while (frames-- > 0) {
313 		snd_pcm_route_ttable_src_t *ttp = src_tt;
314 		sum_t sum;
315 
316 		/* Zero sum */
317 		goto *zero;
318 	zero_int64:
319 		sum.as_sint64 = 0;
320 		goto zero_end;
321 #if SND_PCM_PLUGIN_ROUTE_FLOAT
322 	zero_float:
323 		sum.as_float = 0.0;
324 		goto zero_end;
325 #endif
326 	zero_end:
327 		for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
328 			const char *src = srcs[srcidx];
329 
330 			/* Get sample */
331 			goto *get32;
332 #define GET32_END after_get
333 #include "plugin_ops.h"
334 #undef GET32_END
335 		after_get:
336 
337 			/* Sum */
338 			goto *add;
339 		add_int64_att:
340 			sum.as_sint64 += (int64_t) sample * ttp->as_int;
341 			goto after_sum;
342 		add_int64_noatt:
343 			if (ttp->as_int)
344 				sum.as_sint64 += sample;
345 			goto after_sum;
346 #if SND_PCM_PLUGIN_ROUTE_FLOAT
347 		add_float_att:
348 			sum.as_float += sample * ttp->as_float;
349 			goto after_sum;
350 		add_float_noatt:
351 			if (ttp->as_int)
352 				sum.as_float += sample;
353 			goto after_sum;
354 #endif
355 		after_sum:
356 			srcs[srcidx] += src_steps[srcidx];
357 			ttp++;
358 		}
359 
360 		/* Normalization */
361 		goto *norm;
362 	norm_int64_att:
363 		div(sum.as_sint64);
364 		/* fallthru */
365 	norm_int64_noatt:
366 		if (sum.as_sint64 > (int64_t)0x7fffffff)
367 			sample = 0x7fffffff;	/* maximum positive value */
368 		else if (sum.as_sint64 < -(int64_t)0x80000000)
369 			sample = 0x80000000;	/* maximum negative value */
370 		else
371 			sample = sum.as_sint64;
372 		goto after_norm;
373 
374 #if SND_PCM_PLUGIN_ROUTE_FLOAT
375 	norm_float:
376 		sum.as_float = rint(sum.as_float);
377 		if (sum.as_float > (int64_t)0x7fffffff)
378 			sample = 0x7fffffff;	/* maximum positive value */
379 		else if (sum.as_float < -(int64_t)0x80000000)
380 			sample = 0x80000000;	/* maximum negative value */
381 		else
382 			sample = sum.as_float;
383 		goto after_norm;
384 #endif
385 	after_norm:
386 
387 		/* Put sample */
388 		goto *put32;
389 #define PUT32_END after_put32
390 #include "plugin_ops.h"
391 #undef PUT32_END
392 	after_put32:
393 
394 		dst += dst_step;
395 	}
396 }
397 
398 #endif /* DOC_HIDDEN */
399 
snd_pcm_route_convert(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,unsigned int dst_channels,snd_pcm_uframes_t frames,snd_pcm_route_params_t * params)400 static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
401 				  snd_pcm_uframes_t dst_offset,
402 				  const snd_pcm_channel_area_t *src_areas,
403 				  snd_pcm_uframes_t src_offset,
404 				  unsigned int src_channels,
405 				  unsigned int dst_channels,
406 				  snd_pcm_uframes_t frames,
407 				  snd_pcm_route_params_t *params)
408 {
409 	unsigned int dst_channel;
410 	snd_pcm_route_ttable_dst_t *dstp;
411 	const snd_pcm_channel_area_t *dst_area;
412 
413 	dstp = params->dsts;
414 	dst_area = dst_areas;
415 	for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
416 		if (dst_channel >= params->ndsts)
417 			snd_pcm_route_convert1_zero(dst_area, dst_offset,
418 						    src_areas, src_offset,
419 						    src_channels,
420 						    frames, dstp, params);
421 		else
422 			dstp->func(dst_area, dst_offset,
423 				   src_areas, src_offset,
424 				   src_channels,
425 				   frames, dstp, params);
426 		dstp++;
427 		dst_area++;
428 	}
429 }
430 
snd_pcm_route_close(snd_pcm_t * pcm)431 static int snd_pcm_route_close(snd_pcm_t *pcm)
432 {
433 	snd_pcm_route_t *route = pcm->private_data;
434 	snd_pcm_route_params_t *params = &route->params;
435 	unsigned int dst_channel;
436 
437 	if (params->dsts) {
438 		for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
439 			free(params->dsts[dst_channel].srcs);
440 		}
441 		free(params->dsts);
442 	}
443 	free(route->chmap);
444 	return snd_pcm_generic_close(pcm);
445 }
446 
snd_pcm_route_hw_refine_cprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)447 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
448 {
449 	int err;
450 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
451 	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
452 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
453 					 &access_mask);
454 	if (err < 0)
455 		return err;
456 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
457 					 &format_mask);
458 	if (err < 0)
459 		return err;
460 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
461 	if (err < 0)
462 		return err;
463 	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
464 	if (err < 0)
465 		return err;
466 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
467 	return 0;
468 }
469 
snd_pcm_route_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)470 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
471 {
472 	snd_pcm_route_t *route = pcm->private_data;
473 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
474 	_snd_pcm_hw_params_any(sparams);
475 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
476 				   &saccess_mask);
477 	if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
478 		_snd_pcm_hw_params_set_format(sparams, route->sformat);
479 		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
480 	}
481 	if (route->schannels >= 0) {
482 		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
483 				      (unsigned int) route->schannels, 0);
484 	}
485 	return 0;
486 }
487 
snd_pcm_route_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)488 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
489 					    snd_pcm_hw_params_t *sparams)
490 {
491 	snd_pcm_route_t *route = pcm->private_data;
492 	int err;
493 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
494 			      SND_PCM_HW_PARBIT_PERIODS |
495 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
496 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
497 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
498 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
499 			      SND_PCM_HW_PARBIT_TICK_TIME);
500 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
501 		links |= (SND_PCM_HW_PARBIT_FORMAT |
502 			  SND_PCM_HW_PARBIT_SUBFORMAT |
503 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
504 	if (route->schannels < 0)
505 		links |= SND_PCM_HW_PARBIT_CHANNELS;
506 	err = _snd_pcm_hw_params_refine(sparams, links, params);
507 	if (err < 0)
508 		return err;
509 	return 0;
510 }
511 
snd_pcm_route_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)512 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
513 					    snd_pcm_hw_params_t *sparams)
514 {
515 	snd_pcm_route_t *route = pcm->private_data;
516 	int err;
517 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
518 			      SND_PCM_HW_PARBIT_PERIODS |
519 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
520 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
521 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
522 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
523 			      SND_PCM_HW_PARBIT_TICK_TIME);
524 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
525 		links |= (SND_PCM_HW_PARBIT_FORMAT |
526 			  SND_PCM_HW_PARBIT_SUBFORMAT |
527 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
528 	if (route->schannels < 0)
529 		links |= SND_PCM_HW_PARBIT_CHANNELS;
530 	err = _snd_pcm_hw_params_refine(params, links, sparams);
531 	if (err < 0)
532 		return err;
533 	return 0;
534 }
535 
snd_pcm_route_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)536 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
537 {
538 	return snd_pcm_hw_refine_slave(pcm, params,
539 				       snd_pcm_route_hw_refine_cprepare,
540 				       snd_pcm_route_hw_refine_cchange,
541 				       snd_pcm_route_hw_refine_sprepare,
542 				       snd_pcm_route_hw_refine_schange,
543 				       snd_pcm_generic_hw_refine);
544 }
545 
snd_pcm_route_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)546 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
547 {
548 	snd_pcm_route_t *route = pcm->private_data;
549 	snd_pcm_t *slave = route->plug.gen.slave;
550 	snd_pcm_format_t src_format, dst_format;
551 	int err = snd_pcm_hw_params_slave(pcm, params,
552 					  snd_pcm_route_hw_refine_cchange,
553 					  snd_pcm_route_hw_refine_sprepare,
554 					  snd_pcm_route_hw_refine_schange,
555 					  snd_pcm_generic_hw_params);
556 	if (err < 0)
557 		return err;
558 
559 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
560 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
561 		dst_format = slave->format;
562 	} else {
563 		src_format = slave->format;
564 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
565 	}
566 	if (err < 0)
567 		return err;
568 	/* 3 bytes or 20-bit formats? */
569 	route->params.use_getput =
570 		(snd_pcm_format_physical_width(src_format) + 7) / 8 == 3 ||
571 		(snd_pcm_format_physical_width(dst_format) + 7) / 8 == 3 ||
572 		snd_pcm_format_width(src_format) == 20 ||
573 		snd_pcm_format_width(dst_format) == 20;
574 	route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32);
575 	route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, dst_format);
576 	route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
577 	route->params.src_size = snd_pcm_format_width(src_format) / 8;
578 	route->params.dst_sfmt = dst_format;
579 #if SND_PCM_PLUGIN_ROUTE_FLOAT
580 	route->params.sum_idx = FLOAT;
581 #else
582 	route->params.sum_idx = UINT64;
583 #endif
584 	return 0;
585 }
586 
587 static snd_pcm_uframes_t
snd_pcm_route_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)588 snd_pcm_route_write_areas(snd_pcm_t *pcm,
589 			  const snd_pcm_channel_area_t *areas,
590 			  snd_pcm_uframes_t offset,
591 			  snd_pcm_uframes_t size,
592 			  const snd_pcm_channel_area_t *slave_areas,
593 			  snd_pcm_uframes_t slave_offset,
594 			  snd_pcm_uframes_t *slave_sizep)
595 {
596 	snd_pcm_route_t *route = pcm->private_data;
597 	snd_pcm_t *slave = route->plug.gen.slave;
598 	if (size > *slave_sizep)
599 		size = *slave_sizep;
600 	snd_pcm_route_convert(slave_areas, slave_offset,
601 			      areas, offset,
602 			      pcm->channels,
603 			      slave->channels,
604 			      size, &route->params);
605 	*slave_sizep = size;
606 	return size;
607 }
608 
609 static snd_pcm_uframes_t
snd_pcm_route_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)610 snd_pcm_route_read_areas(snd_pcm_t *pcm,
611 			 const snd_pcm_channel_area_t *areas,
612 			 snd_pcm_uframes_t offset,
613 			 snd_pcm_uframes_t size,
614 			 const snd_pcm_channel_area_t *slave_areas,
615 			 snd_pcm_uframes_t slave_offset,
616 			 snd_pcm_uframes_t *slave_sizep)
617 {
618 	snd_pcm_route_t *route = pcm->private_data;
619 	snd_pcm_t *slave = route->plug.gen.slave;
620 	if (size > *slave_sizep)
621 		size = *slave_sizep;
622 	snd_pcm_route_convert(areas, offset,
623 			      slave_areas, slave_offset,
624 			      slave->channels,
625 			      pcm->channels,
626 			      size, &route->params);
627 	*slave_sizep = size;
628 	return size;
629 }
630 
snd_pcm_route_get_chmap(snd_pcm_t * pcm)631 static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
632 {
633 	snd_pcm_route_t *route = pcm->private_data;
634 	snd_pcm_chmap_t *map, *slave_map;
635 	unsigned int src, dst, nsrcs;
636 
637 	slave_map = snd_pcm_generic_get_chmap(pcm);
638 	if (!slave_map)
639 		return NULL;
640 	nsrcs = route->params.nsrcs;
641 	map = calloc(4, nsrcs + 1);
642 	if (!map) {
643 		free(slave_map);
644 		return NULL;
645 	}
646 	map->channels = nsrcs;
647 	for (src = 0; src < nsrcs; src++)
648 		map->pos[src] = SND_CHMAP_NA;
649 	for (dst = 0; dst < route->params.ndsts; dst++) {
650 		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
651 		for (src = 0; src < d->nsrcs; src++) {
652 			unsigned int c = d->srcs[src].channel;
653 			if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
654 				map->pos[c] = slave_map->pos[dst];
655 		}
656 	}
657 	free(slave_map);
658 	return map;
659 }
660 
snd_pcm_route_query_chmaps(snd_pcm_t * pcm)661 static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
662 {
663 	snd_pcm_chmap_query_t **maps;
664 	snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm);
665 	if (!map)
666 		return NULL;
667 	maps = _snd_pcm_make_single_query_chmaps(map);
668 	free(map);
669 	return maps;
670 }
671 
snd_pcm_route_dump(snd_pcm_t * pcm,snd_output_t * out)672 static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
673 {
674 	snd_pcm_route_t *route = pcm->private_data;
675 	unsigned int dst;
676 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
677 		snd_output_printf(out, "Route conversion PCM\n");
678 	else
679 		snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
680 			snd_pcm_format_name(route->sformat));
681 	snd_output_puts(out, "  Transformation table:\n");
682 	for (dst = 0; dst < route->params.ndsts; dst++) {
683 		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
684 		unsigned int src;
685 		snd_output_printf(out, "    %d <- ", dst);
686 		if (d->nsrcs == 0) {
687 			snd_output_printf(out, "none\n");
688 			continue;
689 		}
690 		src = 0;
691 		while (1) {
692 			snd_pcm_route_ttable_src_t *s = &d->srcs[src];
693 			if (d->att)
694 #if SND_PCM_PLUGIN_ROUTE_FLOAT
695 				snd_output_printf(out, "%d*%g", s->channel, s->as_float);
696 #else
697 				snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
698 #endif
699 			else
700 				snd_output_printf(out, "%d", s->channel);
701 			src++;
702 			if (src == d->nsrcs)
703 				break;
704 			snd_output_puts(out, " + ");
705 		}
706 		snd_output_putc(out, '\n');
707 	}
708 	if (pcm->setup) {
709 		snd_output_printf(out, "Its setup is:\n");
710 		snd_pcm_dump_setup(pcm, out);
711 	}
712 	snd_output_printf(out, "Slave: ");
713 	snd_pcm_dump(route->plug.gen.slave, out);
714 }
715 
716 /*
717  * Converts a string to an array of channel indices:
718  * - Given a number, the result is an array with one element,
719  *   containing that number
720  * - Given a channel name (e g "FL") and a chmap,
721  *   it will look this up in the chmap and return all matches
722  * - Given a channel name and no chmap, the result is an array with one element,
723      containing alsa standard channel map. Note that this might be a negative
724      number in case of "UNKNOWN", "NA" or "MONO".
725  * Return value is number of matches written.
726  */
strtochannel(const char * id,snd_pcm_chmap_t * chmap,long * channel,int channel_size)727 static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
728 			 long *channel, int channel_size)
729 {
730 	int ch;
731 	if (safe_strtol(id, channel) >= 0)
732 		return 1;
733 
734 	ch = (int) snd_pcm_chmap_from_string(id);
735 	if (ch == -1)
736 		return -EINVAL;
737 
738 	if (chmap) {
739 		int i, r = 0;
740 		/* Start with highest channel to simplify implementation of
741 		   determine ttable size */
742 		for (i = chmap->channels - 1; i >= 0; i--) {
743 			if ((int) chmap->pos[i] != ch)
744 				continue;
745 			if (r >= channel_size)
746 				continue;
747 			channel[r++] = i;
748 		}
749 		return r;
750 	}
751 	else {
752 		/* Assume ALSA standard channel mapping */
753 		*channel = ch - SND_CHMAP_FL;
754 		return 1;
755 	}
756 }
757 
758 #define MAX_CHMAP_CHANNELS 256
759 
determine_chmap(snd_config_t * tt,snd_pcm_chmap_t ** tt_chmap)760 static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
761 {
762 	snd_config_iterator_t i, inext;
763 	snd_pcm_chmap_t *chmap;
764 
765 	assert(tt && tt_chmap);
766 	chmap = malloc(sizeof(snd_pcm_chmap_t) +
767 		       MAX_CHMAP_CHANNELS * sizeof(unsigned int));
768 
769 	chmap->channels = 0;
770 	snd_config_for_each(i, inext, tt) {
771 		const char *id;
772 		snd_config_iterator_t j, jnext;
773 		snd_config_t *in = snd_config_iterator_entry(i);
774 
775 		if (snd_config_get_id(in, &id) < 0)
776 			continue;
777 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
778 			goto err;
779 		snd_config_for_each(j, jnext, in) {
780 			int ch, k, found;
781 			long schannel;
782 			snd_config_t *jnode = snd_config_iterator_entry(j);
783 			if (snd_config_get_id(jnode, &id) < 0)
784 				continue;
785 			if (safe_strtol(id, &schannel) >= 0)
786 				continue;
787 			ch = (int) snd_pcm_chmap_from_string(id);
788 			if (ch == -1)
789 				goto err;
790 
791 			found = 0;
792 			for (k = 0; k < (int) chmap->channels; k++)
793 				if (ch == (int) chmap->pos[k]) {
794 					found = 1;
795 					break;
796 				}
797 			if (found)
798 				continue;
799 
800 			if (chmap->channels >= MAX_CHMAP_CHANNELS) {
801 				SNDERR("Too many channels in ttable chmap");
802 				goto err;
803 			}
804 			chmap->pos[chmap->channels++] = ch;
805 		}
806 	}
807 
808 	if (chmap->channels == 0) {
809 		free(chmap);
810 		chmap = NULL;
811 	}
812 	*tt_chmap = chmap;
813 	return 0;
814 
815 err:
816 	*tt_chmap = NULL;
817 	free(chmap);
818 	return -EINVAL;
819 }
820 
find_matching_chmap(snd_pcm_t * spcm,snd_pcm_chmap_t * tt_chmap,snd_pcm_chmap_t ** found_chmap,int * schannels)821 static int find_matching_chmap(snd_pcm_t *spcm, snd_pcm_chmap_t *tt_chmap,
822 			       snd_pcm_chmap_t **found_chmap, int *schannels)
823 {
824 	snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(spcm);
825 	int i;
826 
827 	*found_chmap = NULL;
828 
829 	if (chmaps == NULL)
830 		return 0; /* chmap API not supported for this slave */
831 
832 	for (i = 0; chmaps[i]; i++) {
833 		unsigned int j, k;
834 		int match = 1;
835 		snd_pcm_chmap_t *c = &chmaps[i]->map;
836 		if (*schannels >= 0 && (int) c->channels != *schannels)
837 			continue;
838 
839 		for (j = 0; j < tt_chmap->channels; j++) {
840 			int found = 0;
841 			unsigned int ch = tt_chmap->pos[j];
842 			for (k = 0; k < c->channels; k++)
843 				if (c->pos[k] == ch) {
844 					found = 1;
845 					break;
846 				}
847 			if (!found) {
848 				match = 0;
849 				break;
850 			}
851 		}
852 
853 		if (match) {
854 			int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
855 			*found_chmap = malloc(size);
856 			if (!*found_chmap) {
857 				snd_pcm_free_chmaps(chmaps);
858 				return -ENOMEM;
859 			}
860 			memcpy(*found_chmap, c, size);
861 			*schannels = c->channels;
862 			break;
863 		}
864 	}
865 
866 	snd_pcm_free_chmaps(chmaps);
867 
868 	if (*found_chmap == NULL) {
869 		SNDERR("Found no matching channel map");
870 		return -EINVAL;
871 	}
872 	return 0;
873 }
874 
route_chmap_init(snd_pcm_t * pcm)875 static int route_chmap_init(snd_pcm_t *pcm)
876 {
877 	int set_map = 0;
878 	snd_pcm_chmap_t *current;
879 	snd_pcm_route_t *route = pcm->private_data;
880 	if (!route->chmap)
881 		return 0;
882 	if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
883 		return 0;
884 
885 	/* Check if we really need to set the chmap or not.
886 	   This is important in case set_chmap is not implemented. */
887 	current = snd_pcm_get_chmap(route->plug.gen.slave);
888 	if (!current)
889 		return -ENOSYS;
890 	if (current->channels != route->chmap->channels)
891 		set_map = 1;
892 	else
893 		set_map = memcmp(current->pos, route->chmap->pos,
894 				 current->channels);
895 	free(current);
896 	if (!set_map)
897 		return 0;
898 
899 	return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
900 }
901 
902 
903 static const snd_pcm_ops_t snd_pcm_route_ops = {
904 	.close = snd_pcm_route_close,
905 	.info = snd_pcm_generic_info,
906 	.hw_refine = snd_pcm_route_hw_refine,
907 	.hw_params = snd_pcm_route_hw_params,
908 	.hw_free = snd_pcm_generic_hw_free,
909 	.sw_params = snd_pcm_generic_sw_params,
910 	.channel_info = snd_pcm_generic_channel_info,
911 	.dump = snd_pcm_route_dump,
912 	.nonblock = snd_pcm_generic_nonblock,
913 	.async = snd_pcm_generic_async,
914 	.mmap = snd_pcm_generic_mmap,
915 	.munmap = snd_pcm_generic_munmap,
916 	.query_chmaps = snd_pcm_route_query_chmaps,
917 	.get_chmap = snd_pcm_route_get_chmap,
918 	.set_chmap = NULL, /* NYI */
919 };
920 
route_load_ttable(snd_pcm_route_params_t * params,snd_pcm_stream_t stream,unsigned int tt_ssize,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_cused,unsigned int tt_sused)921 static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
922 			     unsigned int tt_ssize,
923 			     snd_pcm_route_ttable_entry_t *ttable,
924 			     unsigned int tt_cused, unsigned int tt_sused)
925 {
926 	unsigned int src_channel, dst_channel;
927 	snd_pcm_route_ttable_dst_t *dptr;
928 	unsigned int sused, dused, smul, dmul;
929 	if (stream == SND_PCM_STREAM_PLAYBACK) {
930 		sused = tt_cused;
931 		dused = tt_sused;
932 		smul = tt_ssize;
933 		dmul = 1;
934 	} else {
935 		sused = tt_sused;
936 		dused = tt_cused;
937 		smul = 1;
938 		dmul = tt_ssize;
939 	}
940 	params->ndsts = dused;
941 	params->nsrcs = sused;
942 	dptr = calloc(dused, sizeof(*params->dsts));
943 	if (!dptr)
944 		return -ENOMEM;
945 	params->dsts = dptr;
946 	for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
947 		snd_pcm_route_ttable_entry_t t = 0;
948 		int att = 0;
949 		int nsrcs = 0;
950 		snd_pcm_route_ttable_src_t srcs[sused];
951 		for (src_channel = 0; src_channel < sused; ++src_channel) {
952 			snd_pcm_route_ttable_entry_t v;
953 			v = ttable[src_channel * smul + dst_channel * dmul];
954 			if (v != 0) {
955 				srcs[nsrcs].channel = src_channel;
956 #if SND_PCM_PLUGIN_ROUTE_FLOAT
957 				/* Also in user space for non attenuated */
958 				srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
959 				srcs[nsrcs].as_float = v;
960 #else
961 				assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
962 				srcs[nsrcs].as_int = v;
963 #endif
964 				if (v != SND_PCM_PLUGIN_ROUTE_FULL)
965 					att = 1;
966 				t += v;
967 				nsrcs++;
968 			}
969 		}
970 #if 0
971 		assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
972 #endif
973 		dptr->att = att;
974 		dptr->nsrcs = nsrcs;
975 		if (nsrcs == 0)
976 			dptr->func = snd_pcm_route_convert1_zero;
977 		else
978 			dptr->func = snd_pcm_route_convert1_many;
979 		if (nsrcs > 0) {
980 			dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
981 			if (!dptr->srcs)
982 				return -ENOMEM;
983 			memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
984 		} else
985 			dptr->srcs = 0;
986 		dptr++;
987 	}
988 	return 0;
989 }
990 
991 /**
992  * \brief Creates a new Route & Volume PCM
993  * \param pcmp Returns created PCM handle
994  * \param name Name of PCM
995  * \param sformat Slave format
996  * \param schannels Slave channels
997  * \param ttable Attenuation table
998  * \param tt_ssize Attenuation table - slave size
999  * \param tt_cused Attenuation table - client used count
1000  * \param tt_sused Attenuation table - slave used count
1001  * \param slave Slave PCM handle
1002  * \param close_slave When set, the slave PCM handle is closed with copy PCM
1003  * \retval zero on success otherwise a negative error code
1004  * \warning Using of this function might be dangerous in the sense
1005  *          of compatibility reasons. The prototype might be freely
1006  *          changed in future.
1007  */
snd_pcm_route_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,int schannels,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_ssize,unsigned int tt_cused,unsigned int tt_sused,snd_pcm_t * slave,int close_slave)1008 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1009 		       snd_pcm_format_t sformat, int schannels,
1010 		       snd_pcm_route_ttable_entry_t *ttable,
1011 		       unsigned int tt_ssize,
1012 		       unsigned int tt_cused, unsigned int tt_sused,
1013 		       snd_pcm_t *slave, int close_slave)
1014 {
1015 	snd_pcm_t *pcm;
1016 	snd_pcm_route_t *route;
1017 	int err;
1018 	assert(pcmp && slave && ttable);
1019 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1020 	    snd_pcm_format_linear(sformat) != 1)
1021 		return -EINVAL;
1022 	route = calloc(1, sizeof(snd_pcm_route_t));
1023 	if (!route) {
1024 		return -ENOMEM;
1025 	}
1026 	snd_pcm_plugin_init(&route->plug);
1027 	route->sformat = sformat;
1028 	route->schannels = schannels;
1029 	route->plug.read = snd_pcm_route_read_areas;
1030 	route->plug.write = snd_pcm_route_write_areas;
1031 	route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
1032 	route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
1033 	route->plug.gen.slave = slave;
1034 	route->plug.gen.close_slave = close_slave;
1035 	route->plug.init = route_chmap_init;
1036 
1037 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
1038 	if (err < 0) {
1039 		free(route);
1040 		return err;
1041 	}
1042 	pcm->ops = &snd_pcm_route_ops;
1043 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
1044 	pcm->private_data = route;
1045 	pcm->poll_fd = slave->poll_fd;
1046 	pcm->poll_events = slave->poll_events;
1047 	pcm->tstamp_type = slave->tstamp_type;
1048 	snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
1049 	snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
1050 	err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
1051 	if (err < 0) {
1052 		snd_pcm_close(pcm);
1053 		return err;
1054 	}
1055 	*pcmp = pcm;
1056 
1057 	return 0;
1058 }
1059 
_snd_pcm_route_determine_ttable(snd_config_t * tt,unsigned int * tt_csize,unsigned int * tt_ssize,snd_pcm_chmap_t * chmap)1060 static int _snd_pcm_route_determine_ttable(snd_config_t *tt,
1061 					   unsigned int *tt_csize,
1062 					   unsigned int *tt_ssize,
1063 					   snd_pcm_chmap_t *chmap)
1064 {
1065 	snd_config_iterator_t i, inext;
1066 	long csize = 0, ssize = 0;
1067 	int err;
1068 
1069 	assert(tt && tt_csize && tt_ssize);
1070 	snd_config_for_each(i, inext, tt) {
1071 		snd_config_t *in = snd_config_iterator_entry(i);
1072 		snd_config_iterator_t j, jnext;
1073 		long cchannel;
1074 		const char *id;
1075 		if (snd_config_get_id(in, &id) < 0)
1076 			continue;
1077 		err = safe_strtol(id, &cchannel);
1078 		if (err < 0) {
1079 			SNDERR("Invalid client channel: %s", id);
1080 			return -EINVAL;
1081 		}
1082 		if (cchannel + 1 > csize)
1083 			csize = cchannel + 1;
1084 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1085 			return -EINVAL;
1086 		snd_config_for_each(j, jnext, in) {
1087 			snd_config_t *jnode = snd_config_iterator_entry(j);
1088 			long schannel;
1089 			const char *id;
1090 			if (snd_config_get_id(jnode, &id) < 0)
1091 				continue;
1092 			err = strtochannel(id, chmap, &schannel, 1);
1093 			if (err < 0) {
1094 				SNDERR("Invalid slave channel: %s", id);
1095 				return -EINVAL;
1096 			}
1097 			if (schannel + 1 > ssize)
1098 				ssize = schannel + 1;
1099 		}
1100 	}
1101 	if (csize == 0 || ssize == 0) {
1102 		SNDERR("Invalid null ttable configuration");
1103 		return -EINVAL;
1104 	}
1105 	*tt_csize = csize;
1106 	*tt_ssize = ssize;
1107 	return 0;
1108 }
1109 
1110 /**
1111  * \brief Determine route matrix sizes
1112  * \param tt Configuration root describing route matrix
1113  * \param tt_csize Returned client size in elements
1114  * \param tt_ssize Returned slave size in elements
1115  * \retval zero on success otherwise a negative error code
1116  */
snd_pcm_route_determine_ttable(snd_config_t * tt,unsigned int * tt_csize,unsigned int * tt_ssize)1117 int snd_pcm_route_determine_ttable(snd_config_t *tt,
1118 				   unsigned int *tt_csize,
1119 				   unsigned int *tt_ssize)
1120 {
1121 	return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
1122 }
1123 
1124 /**
1125  * \brief Load route matrix
1126  * \param tt Configuration root describing route matrix
1127  * \param ttable Returned route matrix
1128  * \param tt_csize Client size in elements
1129  * \param tt_ssize Slave size in elements
1130  * \param tt_cused Used client elements
1131  * \param tt_sused Used slave elements
1132  * \param schannels Slave channels
1133  * \retval zero on success otherwise a negative error code
1134  */
_snd_pcm_route_load_ttable(snd_config_t * tt,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_csize,unsigned int tt_ssize,unsigned int * tt_cused,unsigned int * tt_sused,int schannels,snd_pcm_chmap_t * chmap)1135 static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1136 				      unsigned int tt_csize, unsigned int tt_ssize,
1137 				      unsigned int *tt_cused, unsigned int *tt_sused,
1138 				      int schannels, snd_pcm_chmap_t *chmap)
1139 {
1140 	int cused = -1;
1141 	int sused = -1;
1142 	snd_config_iterator_t i, inext;
1143 	unsigned int k;
1144 	int err;
1145 	for (k = 0; k < tt_csize * tt_ssize; ++k)
1146 		ttable[k] = 0.0;
1147 	snd_config_for_each(i, inext, tt) {
1148 		snd_config_t *in = snd_config_iterator_entry(i);
1149 		snd_config_iterator_t j, jnext;
1150 		long cchannel;
1151 		const char *id;
1152 		if (snd_config_get_id(in, &id) < 0)
1153 			continue;
1154 		err = safe_strtol(id, &cchannel);
1155 		if (err < 0 ||
1156 		    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
1157 			SNDERR("Invalid client channel: %s", id);
1158 			return -EINVAL;
1159 		}
1160 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1161 			return -EINVAL;
1162 		snd_config_for_each(j, jnext, in) {
1163 			snd_config_t *jnode = snd_config_iterator_entry(j);
1164 			double value;
1165 			int ss;
1166 			long *scha = alloca(tt_ssize * sizeof(long));
1167 			const char *id;
1168 			if (snd_config_get_id(jnode, &id) < 0)
1169 				continue;
1170 
1171 			ss = strtochannel(id, chmap, scha, tt_ssize);
1172 			if (ss < 0) {
1173 				SNDERR("Invalid slave channel: %s", id);
1174 				return -EINVAL;
1175 			}
1176 
1177 			err = snd_config_get_real(jnode, &value);
1178 			if (err < 0) {
1179 				long v;
1180 				err = snd_config_get_integer(jnode, &v);
1181 				if (err < 0) {
1182 					SNDERR("Invalid type for %s", id);
1183 					return -EINVAL;
1184 				}
1185 				value = v;
1186 			}
1187 
1188 			for (k = 0; (int) k < ss; k++) {
1189 				long schannel = scha[k];
1190 				if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
1191 				    (schannels > 0 && schannel >= schannels)) {
1192 					SNDERR("Invalid slave channel: %s", id);
1193 					return -EINVAL;
1194 				}
1195 				ttable[cchannel * tt_ssize + schannel] = value;
1196 				if (schannel > sused)
1197 					sused = schannel;
1198 			}
1199 		}
1200 		if (cchannel > cused)
1201 			cused = cchannel;
1202 	}
1203 	*tt_sused = sused + 1;
1204 	*tt_cused = cused + 1;
1205 	return 0;
1206 }
1207 
1208 /**
1209  * \brief Load route matrix
1210  * \param tt Configuration root describing route matrix
1211  * \param ttable Returned route matrix
1212  * \param tt_csize Client size in elements
1213  * \param tt_ssize Slave size in elements
1214  * \param tt_cused Used client elements
1215  * \param tt_sused Used slave elements
1216  * \param schannels Slave channels
1217  * \retval zero on success otherwise a negative error code
1218  */
snd_pcm_route_load_ttable(snd_config_t * tt,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_csize,unsigned int tt_ssize,unsigned int * tt_cused,unsigned int * tt_sused,int schannels)1219 int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1220 			      unsigned int tt_csize, unsigned int tt_ssize,
1221 			      unsigned int *tt_cused, unsigned int *tt_sused,
1222 			      int schannels)
1223 {
1224 	return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
1225 					  tt_cused, tt_sused, schannels, NULL);
1226 }
1227 
1228 /*! \page pcm_plugins
1229 
1230 \section pcm_plugins_route Plugin: Route & Volume
1231 
1232 This plugin converts channels and applies volume during the conversion.
1233 The format and rate must match for both of them.
1234 
1235 SCHANNEL can be a channel name instead of a number (e g FL, LFE).
1236 If so, a matching channel map will be selected for the slave.
1237 
1238 \code
1239 pcm.name {
1240         type route              # Route & Volume conversion PCM
1241         slave STR               # Slave name
1242         # or
1243         slave {                 # Slave definition
1244                 pcm STR         # Slave PCM name
1245                 # or
1246                 pcm { }         # Slave PCM definition
1247                 [format STR]    # Slave format
1248                 [channels INT]  # Slave channels
1249         }
1250         ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1251                 CCHANNEL {
1252                         SCHANNEL REAL   # route value (0.0 - 1.0)
1253                 }
1254         }
1255 }
1256 \endcode
1257 
1258 \subsection pcm_plugins_route_funcref Function reference
1259 
1260 <UL>
1261   <LI>snd_pcm_route_open()
1262   <LI>_snd_pcm_route_open()
1263 </UL>
1264 
1265 */
1266 
1267 /**
1268  * \brief Creates a new Route & Volume PCM
1269  * \param pcmp Returns created PCM handle
1270  * \param name Name of PCM
1271  * \param root Root configuration node
1272  * \param conf Configuration node with Route & Volume PCM description
1273  * \param stream Stream type
1274  * \param mode Stream mode
1275  * \retval zero on success otherwise a negative error code
1276  * \warning Using of this function might be dangerous in the sense
1277  *          of compatibility reasons. The prototype might be freely
1278  *          changed in future.
1279  */
_snd_pcm_route_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)1280 int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1281 			snd_config_t *root, snd_config_t *conf,
1282 			snd_pcm_stream_t stream, int mode)
1283 {
1284 	snd_config_iterator_t i, next;
1285 	int err;
1286 	snd_pcm_t *spcm;
1287 	snd_config_t *slave = NULL, *sconf;
1288 	snd_pcm_chmap_t *tt_chmap = NULL, *chmap = NULL;
1289 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1290 	int schannels = -1;
1291 	snd_config_t *tt = NULL;
1292 	snd_pcm_route_ttable_entry_t *ttable = NULL;
1293 	unsigned int csize, ssize;
1294 	unsigned int cused, sused;
1295 	snd_config_for_each(i, next, conf) {
1296 		snd_config_t *n = snd_config_iterator_entry(i);
1297 		const char *id;
1298 		if (snd_config_get_id(n, &id) < 0)
1299 			continue;
1300 		if (snd_pcm_conf_generic_id(id))
1301 			continue;
1302 		if (strcmp(id, "slave") == 0) {
1303 			slave = n;
1304 			continue;
1305 		}
1306 		if (strcmp(id, "ttable") == 0) {
1307 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1308 				SNDERR("Invalid type for %s", id);
1309 				return -EINVAL;
1310 			}
1311 			tt = n;
1312 			continue;
1313 		}
1314 		SNDERR("Unknown field %s", id);
1315 		return -EINVAL;
1316 	}
1317 	if (!slave) {
1318 		SNDERR("slave is not defined");
1319 		return -EINVAL;
1320 	}
1321 	if (!tt) {
1322 		SNDERR("ttable is not defined");
1323 		return -EINVAL;
1324 	}
1325 	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1326 				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1327 				 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
1328 	if (err < 0)
1329 		return err;
1330 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1331 	    snd_pcm_format_linear(sformat) != 1) {
1332 	    	snd_config_delete(sconf);
1333 		SNDERR("slave format is not linear");
1334 		return -EINVAL;
1335 	}
1336 
1337 	err = determine_chmap(tt, &tt_chmap);
1338 	if (err < 0) {
1339 		free(ttable);
1340 		return err;
1341 	}
1342 
1343 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1344 	snd_config_delete(sconf);
1345 	if (err < 0) {
1346 		free(tt_chmap);
1347 		free(ttable);
1348 		return err;
1349 	}
1350 
1351 	if (tt_chmap) {
1352 		err = find_matching_chmap(spcm, tt_chmap, &chmap, &schannels);
1353 		free(tt_chmap);
1354 		if (err < 0) {
1355 			snd_pcm_close(spcm);
1356 			return err;
1357 		}
1358 	}
1359 
1360 	err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
1361 	if (err < 0) {
1362 		free(chmap);
1363 		snd_pcm_close(spcm);
1364 		return err;
1365 	}
1366 	ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
1367 	if (ttable == NULL) {
1368 		free(chmap);
1369 		snd_pcm_close(spcm);
1370 		return -ENOMEM;
1371 	}
1372 	err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
1373 					&cused, &sused, schannels, chmap);
1374 	if (err < 0) {
1375 		free(chmap);
1376 		free(ttable);
1377 		snd_pcm_close(spcm);
1378 		return err;
1379 	}
1380 
1381 	err = snd_pcm_route_open(pcmp, name, sformat, schannels,
1382 				 ttable, ssize,
1383 				 cused, sused,
1384 				 spcm, 1);
1385 	free(ttable);
1386 	if (err < 0) {
1387 		free(chmap);
1388 		snd_pcm_close(spcm);
1389 	} else {
1390 		((snd_pcm_route_t*) (*pcmp)->private_data)->chmap = chmap;
1391 	}
1392 
1393 	return err;
1394 }
1395 #ifndef DOC_HIDDEN
1396 SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
1397 #endif
1398