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 "pcm_local.h"
30 #include "pcm_plugin.h"
31 #include "plugin_ops.h"
32 #include "bswap.h"
33 #include <math.h>
34 
35 #ifndef PIC
36 /* entry for static linking */
37 const char *_snd_module_pcm_route = "";
38 #endif
39 
40 #ifndef DOC_HIDDEN
41 
42 /* The best possible hack to support missing optimization in gcc 2.7.2.3 */
43 #if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
44 #define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
45 #elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
46 #define div(a) a >>= 4
47 #else
48 #error "Add some code here"
49 #endif
50 
51 typedef struct {
52 	int channel;
53 	int as_int;
54 #if SND_PCM_PLUGIN_ROUTE_FLOAT
55 	float as_float;
56 #endif
57 } snd_pcm_route_ttable_src_t;
58 
59 typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
60 
61 typedef struct {
62 	enum {UINT64, FLOAT} sum_idx;
63 	unsigned int get_idx;
64 	unsigned int put_idx;
65 	unsigned int conv_idx;
66 	int use_getput;
67 	unsigned int src_size;
68 	snd_pcm_format_t dst_sfmt;
69 	unsigned int nsrcs;
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_chmap_t *chmap;
106 	snd_pcm_chmap_query_t **chmap_override;
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 	snd_pcm_free_chmaps(route->chmap_override);
445 	return snd_pcm_generic_close(pcm);
446 }
447 
snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)448 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
449 {
450 	int err;
451 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
452 	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
453 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
454 					 &access_mask);
455 	if (err < 0)
456 		return err;
457 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
458 					 &format_mask);
459 	if (err < 0)
460 		return err;
461 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
462 	if (err < 0)
463 		return err;
464 	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
465 	if (err < 0)
466 		return err;
467 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
468 	return 0;
469 }
470 
snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)471 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
472 {
473 	snd_pcm_route_t *route = pcm->private_data;
474 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
475 	_snd_pcm_hw_params_any(sparams);
476 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
477 				   &saccess_mask);
478 	if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
479 		_snd_pcm_hw_params_set_format(sparams, route->sformat);
480 		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
481 	}
482 	if (route->schannels >= 0) {
483 		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
484 				      (unsigned int) route->schannels, 0);
485 	}
486 	return 0;
487 }
488 
snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)489 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
490 					    snd_pcm_hw_params_t *sparams)
491 {
492 	snd_pcm_route_t *route = pcm->private_data;
493 	int err;
494 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
495 			      SND_PCM_HW_PARBIT_PERIODS |
496 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
497 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
498 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
499 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
500 			      SND_PCM_HW_PARBIT_TICK_TIME);
501 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
502 		links |= (SND_PCM_HW_PARBIT_FORMAT |
503 			  SND_PCM_HW_PARBIT_SUBFORMAT |
504 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
505 	if (route->schannels < 0)
506 		links |= SND_PCM_HW_PARBIT_CHANNELS;
507 	err = _snd_pcm_hw_params_refine(sparams, links, params);
508 	if (err < 0)
509 		return err;
510 	return 0;
511 }
512 
snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)513 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
514 					    snd_pcm_hw_params_t *sparams)
515 {
516 	snd_pcm_route_t *route = pcm->private_data;
517 	int err;
518 	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
519 			      SND_PCM_HW_PARBIT_PERIODS |
520 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
521 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
522 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
523 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
524 			      SND_PCM_HW_PARBIT_TICK_TIME);
525 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
526 		links |= (SND_PCM_HW_PARBIT_FORMAT |
527 			  SND_PCM_HW_PARBIT_SUBFORMAT |
528 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
529 	if (route->schannels < 0)
530 		links |= SND_PCM_HW_PARBIT_CHANNELS;
531 	err = _snd_pcm_hw_params_refine(params, links, sparams);
532 	if (err < 0)
533 		return err;
534 	return 0;
535 }
536 
snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)537 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
538 {
539 	return snd_pcm_hw_refine_slave(pcm, params,
540 				       snd_pcm_route_hw_refine_cprepare,
541 				       snd_pcm_route_hw_refine_cchange,
542 				       snd_pcm_route_hw_refine_sprepare,
543 				       snd_pcm_route_hw_refine_schange,
544 				       snd_pcm_generic_hw_refine);
545 }
546 
snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)547 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
548 {
549 	snd_pcm_route_t *route = pcm->private_data;
550 	snd_pcm_t *slave = route->plug.gen.slave;
551 	snd_pcm_format_t src_format, dst_format;
552 	int err = snd_pcm_hw_params_slave(pcm, params,
553 					  snd_pcm_route_hw_refine_cchange,
554 					  snd_pcm_route_hw_refine_sprepare,
555 					  snd_pcm_route_hw_refine_schange,
556 					  snd_pcm_generic_hw_params);
557 	if (err < 0)
558 		return err;
559 
560 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
561 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
562 		dst_format = slave->format;
563 	} else {
564 		src_format = slave->format;
565 		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
566 	}
567 	if (err < 0)
568 		return err;
569 	/* 3 bytes or 20-bit formats? */
570 	route->params.use_getput =
571 		(snd_pcm_format_physical_width(src_format) + 7) / 8 == 3 ||
572 		(snd_pcm_format_physical_width(dst_format) + 7) / 8 == 3 ||
573 		snd_pcm_format_width(src_format) == 20 ||
574 		snd_pcm_format_width(dst_format) == 20;
575 	route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32);
576 	route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, dst_format);
577 	route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
578 	route->params.src_size = snd_pcm_format_width(src_format) / 8;
579 	route->params.dst_sfmt = dst_format;
580 #if SND_PCM_PLUGIN_ROUTE_FLOAT
581 	route->params.sum_idx = FLOAT;
582 #else
583 	route->params.sum_idx = UINT64;
584 #endif
585 	return 0;
586 }
587 
588 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)589 snd_pcm_route_write_areas(snd_pcm_t *pcm,
590 			  const snd_pcm_channel_area_t *areas,
591 			  snd_pcm_uframes_t offset,
592 			  snd_pcm_uframes_t size,
593 			  const snd_pcm_channel_area_t *slave_areas,
594 			  snd_pcm_uframes_t slave_offset,
595 			  snd_pcm_uframes_t *slave_sizep)
596 {
597 	snd_pcm_route_t *route = pcm->private_data;
598 	snd_pcm_t *slave = route->plug.gen.slave;
599 	if (size > *slave_sizep)
600 		size = *slave_sizep;
601 	snd_pcm_route_convert(slave_areas, slave_offset,
602 			      areas, offset,
603 			      pcm->channels,
604 			      slave->channels,
605 			      size, &route->params);
606 	*slave_sizep = size;
607 	return size;
608 }
609 
610 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)611 snd_pcm_route_read_areas(snd_pcm_t *pcm,
612 			 const snd_pcm_channel_area_t *areas,
613 			 snd_pcm_uframes_t offset,
614 			 snd_pcm_uframes_t size,
615 			 const snd_pcm_channel_area_t *slave_areas,
616 			 snd_pcm_uframes_t slave_offset,
617 			 snd_pcm_uframes_t *slave_sizep)
618 {
619 	snd_pcm_route_t *route = pcm->private_data;
620 	snd_pcm_t *slave = route->plug.gen.slave;
621 	if (size > *slave_sizep)
622 		size = *slave_sizep;
623 	snd_pcm_route_convert(areas, offset,
624 			      slave_areas, slave_offset,
625 			      slave->channels,
626 			      pcm->channels,
627 			      size, &route->params);
628 	*slave_sizep = size;
629 	return size;
630 }
631 
snd_pcm_route_get_chmap(snd_pcm_t *pcm)632 static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
633 {
634 	snd_pcm_route_t *route = pcm->private_data;
635 	snd_pcm_chmap_t *map, *slave_map;
636 	unsigned int src, dst, nsrcs;
637 
638 	if (route->chmap_override)
639 		return _snd_pcm_choose_fixed_chmap(pcm, route->chmap_override);
640 
641 	slave_map = snd_pcm_generic_get_chmap(pcm);
642 	if (!slave_map)
643 		return NULL;
644 	nsrcs = route->params.nsrcs;
645 	map = calloc(4, nsrcs + 1);
646 	if (!map) {
647 		free(slave_map);
648 		return NULL;
649 	}
650 	map->channels = nsrcs;
651 	for (src = 0; src < nsrcs; src++)
652 		map->pos[src] = SND_CHMAP_NA;
653 	for (dst = 0; dst < route->params.ndsts; dst++) {
654 		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
655 		for (src = 0; src < d->nsrcs; src++) {
656 			unsigned int c = d->srcs[src].channel;
657 			if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
658 				map->pos[c] = slave_map->pos[dst];
659 		}
660 	}
661 	free(slave_map);
662 	return map;
663 }
664 
snd_pcm_route_query_chmaps(snd_pcm_t *pcm)665 static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
666 {
667 	snd_pcm_route_t *route = pcm->private_data;
668 	snd_pcm_chmap_query_t **maps;
669 	snd_pcm_chmap_t *map;
670 
671 	if (route->chmap_override)
672 		return _snd_pcm_copy_chmap_query(route->chmap_override);
673 
674 	map = snd_pcm_route_get_chmap(pcm);
675 	if (!map)
676 		return NULL;
677 	maps = _snd_pcm_make_single_query_chmaps(map);
678 	free(map);
679 	return maps;
680 }
681 
snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)682 static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
683 {
684 	snd_pcm_route_t *route = pcm->private_data;
685 	unsigned int dst;
686 	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
687 		snd_output_printf(out, "Route conversion PCM\n");
688 	else
689 		snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
690 			snd_pcm_format_name(route->sformat));
691 	snd_output_puts(out, "  Transformation table:\n");
692 	for (dst = 0; dst < route->params.ndsts; dst++) {
693 		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
694 		unsigned int src;
695 		snd_output_printf(out, "    %d <- ", dst);
696 		if (d->nsrcs == 0) {
697 			snd_output_printf(out, "none\n");
698 			continue;
699 		}
700 		src = 0;
701 		while (1) {
702 			snd_pcm_route_ttable_src_t *s = &d->srcs[src];
703 			if (d->att)
704 #if SND_PCM_PLUGIN_ROUTE_FLOAT
705 				snd_output_printf(out, "%d*%g", s->channel, s->as_float);
706 #else
707 				snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
708 #endif
709 			else
710 				snd_output_printf(out, "%d", s->channel);
711 			src++;
712 			if (src == d->nsrcs)
713 				break;
714 			snd_output_puts(out, " + ");
715 		}
716 		snd_output_putc(out, '\n');
717 	}
718 	if (pcm->setup) {
719 		snd_output_printf(out, "Its setup is:\n");
720 		snd_pcm_dump_setup(pcm, out);
721 	}
722 	snd_output_printf(out, "Slave: ");
723 	snd_pcm_dump(route->plug.gen.slave, out);
724 }
725 
726 /*
727  * Converts a string to an array of channel indices:
728  * - Given a number, the result is an array with one element,
729  *   containing that number
730  * - Given a channel name (e g "FL") and a chmap,
731  *   it will look this up in the chmap and return all matches
732  * - Given a channel name and no chmap, the result is an array with one element,
733      containing alsa standard channel map. Note that this might be a negative
734      number in case of "UNKNOWN", "NA" or "MONO".
735  * Return value is number of matches written.
736  */
strtochannel(const char *id, snd_pcm_chmap_t *chmap, long *channel, int channel_size)737 static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
738 			 long *channel, int channel_size)
739 {
740 	int ch;
741 	if (safe_strtol(id, channel) >= 0)
742 		return 1;
743 
744 	ch = (int) snd_pcm_chmap_from_string(id);
745 	if (ch == -1)
746 		return -EINVAL;
747 
748 	if (chmap) {
749 		int i, r = 0;
750 		/* Start with highest channel to simplify implementation of
751 		   determine ttable size */
752 		for (i = chmap->channels - 1; i >= 0; i--) {
753 			if ((int) chmap->pos[i] != ch)
754 				continue;
755 			if (r >= channel_size)
756 				continue;
757 			channel[r++] = i;
758 		}
759 		return r;
760 	}
761 	else {
762 		/* Assume ALSA standard channel mapping */
763 		*channel = ch - SND_CHMAP_FL;
764 		return 1;
765 	}
766 }
767 
768 #ifndef DOC_HIDDEN
769 #define MAX_CHMAP_CHANNELS 256
770 #endif
771 
determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)772 static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
773 {
774 	snd_config_iterator_t i, inext;
775 	snd_pcm_chmap_t *chmap;
776 
777 	assert(tt && tt_chmap);
778 	chmap = malloc(sizeof(snd_pcm_chmap_t) +
779 		       MAX_CHMAP_CHANNELS * sizeof(unsigned int));
780 
781 	chmap->channels = 0;
782 	snd_config_for_each(i, inext, tt) {
783 		const char *id;
784 		snd_config_iterator_t j, jnext;
785 		snd_config_t *in = snd_config_iterator_entry(i);
786 
787 		if (snd_config_get_id(in, &id) < 0)
788 			continue;
789 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
790 			goto err;
791 		snd_config_for_each(j, jnext, in) {
792 			int ch, k, found;
793 			long schannel;
794 			snd_config_t *jnode = snd_config_iterator_entry(j);
795 			if (snd_config_get_id(jnode, &id) < 0)
796 				continue;
797 			if (safe_strtol(id, &schannel) >= 0)
798 				continue;
799 			ch = (int) snd_pcm_chmap_from_string(id);
800 			if (ch == -1)
801 				goto err;
802 
803 			found = 0;
804 			for (k = 0; k < (int) chmap->channels; k++)
805 				if (ch == (int) chmap->pos[k]) {
806 					found = 1;
807 					break;
808 				}
809 			if (found)
810 				continue;
811 
812 			if (chmap->channels >= MAX_CHMAP_CHANNELS) {
813 				SNDERR("Too many channels in ttable chmap");
814 				goto err;
815 			}
816 			chmap->pos[chmap->channels++] = ch;
817 		}
818 	}
819 
820 	if (chmap->channels == 0) {
821 		free(chmap);
822 		chmap = NULL;
823 	}
824 	*tt_chmap = chmap;
825 	return 0;
826 
827 err:
828 	*tt_chmap = NULL;
829 	free(chmap);
830 	return -EINVAL;
831 }
832 
find_matching_chmap(snd_pcm_chmap_query_t **chmaps, snd_pcm_chmap_t *tt_chmap, snd_pcm_chmap_t **found_chmap, int *schannels)833 static int find_matching_chmap(snd_pcm_chmap_query_t **chmaps,
834 			       snd_pcm_chmap_t *tt_chmap,
835 			       snd_pcm_chmap_t **found_chmap, int *schannels)
836 {
837 	int i;
838 
839 	*found_chmap = NULL;
840 
841 	if (chmaps == NULL)
842 		return 0; /* chmap API not supported for this slave */
843 
844 	for (i = 0; chmaps[i]; i++) {
845 		unsigned int j, k;
846 		int match = 1;
847 		snd_pcm_chmap_t *c = &chmaps[i]->map;
848 		if (*schannels >= 0 && (int) c->channels != *schannels)
849 			continue;
850 
851 		for (j = 0; j < tt_chmap->channels; j++) {
852 			int found = 0;
853 			unsigned int ch = tt_chmap->pos[j];
854 			for (k = 0; k < c->channels; k++)
855 				if (c->pos[k] == ch) {
856 					found = 1;
857 					break;
858 				}
859 			if (!found) {
860 				match = 0;
861 				break;
862 			}
863 		}
864 
865 		if (match) {
866 			int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
867 			*found_chmap = malloc(size);
868 			if (!*found_chmap) {
869 				return -ENOMEM;
870 			}
871 			memcpy(*found_chmap, c, size);
872 			*schannels = c->channels;
873 			break;
874 		}
875 	}
876 
877 	if (*found_chmap == NULL) {
878 		SNDERR("Found no matching channel map");
879 		return -EINVAL;
880 	}
881 	return 0;
882 }
883 
route_chmap_init(snd_pcm_t *pcm)884 static int route_chmap_init(snd_pcm_t *pcm)
885 {
886 	int set_map = 0;
887 	snd_pcm_chmap_t *current;
888 	snd_pcm_route_t *route = pcm->private_data;
889 	if (!route->chmap)
890 		return 0;
891 	if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
892 		return 0;
893 
894 	/* Check if we really need to set the chmap or not.
895 	   This is important in case set_chmap is not implemented. */
896 	current = snd_pcm_get_chmap(route->plug.gen.slave);
897 	if (!current)
898 		return -ENOSYS;
899 	if (current->channels != route->chmap->channels)
900 		set_map = 1;
901 	else
902 		set_map = memcmp(current->pos, route->chmap->pos,
903 				 current->channels);
904 	free(current);
905 	if (!set_map)
906 		return 0;
907 
908 	return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
909 }
910 
911 
912 static const snd_pcm_ops_t snd_pcm_route_ops = {
913 	.close = snd_pcm_route_close,
914 	.info = snd_pcm_generic_info,
915 	.hw_refine = snd_pcm_route_hw_refine,
916 	.hw_params = snd_pcm_route_hw_params,
917 	.hw_free = snd_pcm_generic_hw_free,
918 	.sw_params = snd_pcm_generic_sw_params,
919 	.channel_info = snd_pcm_generic_channel_info,
920 	.dump = snd_pcm_route_dump,
921 	.nonblock = snd_pcm_generic_nonblock,
922 	.async = snd_pcm_generic_async,
923 	.mmap = snd_pcm_generic_mmap,
924 	.munmap = snd_pcm_generic_munmap,
925 	.query_chmaps = snd_pcm_route_query_chmaps,
926 	.get_chmap = snd_pcm_route_get_chmap,
927 	.set_chmap = NULL, /* NYI */
928 };
929 
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)930 static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
931 			     unsigned int tt_ssize,
932 			     snd_pcm_route_ttable_entry_t *ttable,
933 			     unsigned int tt_cused, unsigned int tt_sused)
934 {
935 	unsigned int src_channel, dst_channel;
936 	snd_pcm_route_ttable_dst_t *dptr;
937 	unsigned int sused, dused, smul, dmul;
938 	if (stream == SND_PCM_STREAM_PLAYBACK) {
939 		sused = tt_cused;
940 		dused = tt_sused;
941 		smul = tt_ssize;
942 		dmul = 1;
943 	} else {
944 		sused = tt_sused;
945 		dused = tt_cused;
946 		smul = 1;
947 		dmul = tt_ssize;
948 	}
949 	params->ndsts = dused;
950 	params->nsrcs = sused;
951 	dptr = calloc(dused, sizeof(*params->dsts));
952 	if (!dptr)
953 		return -ENOMEM;
954 	params->dsts = dptr;
955 	for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
956 		snd_pcm_route_ttable_entry_t t = 0;
957 		int att = 0;
958 		int nsrcs = 0;
959 		snd_pcm_route_ttable_src_t srcs[sused];
960 		for (src_channel = 0; src_channel < sused; ++src_channel) {
961 			snd_pcm_route_ttable_entry_t v;
962 			v = ttable[src_channel * smul + dst_channel * dmul];
963 			if (v != 0) {
964 				srcs[nsrcs].channel = src_channel;
965 #if SND_PCM_PLUGIN_ROUTE_FLOAT
966 				/* Also in user space for non attenuated */
967 				srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
968 				srcs[nsrcs].as_float = v;
969 #else
970 				assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
971 				srcs[nsrcs].as_int = v;
972 #endif
973 				if (v != SND_PCM_PLUGIN_ROUTE_FULL)
974 					att = 1;
975 				t += v;
976 				nsrcs++;
977 			}
978 		}
979 #if 0
980 		assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
981 #endif
982 		dptr->att = att;
983 		dptr->nsrcs = nsrcs;
984 		if (nsrcs == 0)
985 			dptr->func = snd_pcm_route_convert1_zero;
986 		else
987 			dptr->func = snd_pcm_route_convert1_many;
988 		if (nsrcs > 0) {
989 			dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
990 			if (!dptr->srcs)
991 				return -ENOMEM;
992 			memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
993 		} else
994 			dptr->srcs = 0;
995 		dptr++;
996 	}
997 	return 0;
998 }
999 
1000 /**
1001  * \brief Creates a new Route & Volume PCM
1002  * \param pcmp Returns created PCM handle
1003  * \param name Name of PCM
1004  * \param sformat Slave format
1005  * \param schannels Slave channels
1006  * \param ttable Attenuation table
1007  * \param tt_ssize Attenuation table - slave size
1008  * \param tt_cused Attenuation table - client used count
1009  * \param tt_sused Attenuation table - slave used count
1010  * \param slave Slave PCM handle
1011  * \param close_slave When set, the slave PCM handle is closed with copy PCM
1012  * \retval zero on success otherwise a negative error code
1013  * \warning Using of this function might be dangerous in the sense
1014  *          of compatibility reasons. The prototype might be freely
1015  *          changed in future.
1016  */
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)1017 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1018 		       snd_pcm_format_t sformat, int schannels,
1019 		       snd_pcm_route_ttable_entry_t *ttable,
1020 		       unsigned int tt_ssize,
1021 		       unsigned int tt_cused, unsigned int tt_sused,
1022 		       snd_pcm_t *slave, int close_slave)
1023 {
1024 	snd_pcm_t *pcm;
1025 	snd_pcm_route_t *route;
1026 	int err;
1027 	assert(pcmp && slave && ttable);
1028 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1029 	    snd_pcm_format_linear(sformat) != 1)
1030 		return -EINVAL;
1031 	route = calloc(1, sizeof(snd_pcm_route_t));
1032 	if (!route) {
1033 		return -ENOMEM;
1034 	}
1035 	snd_pcm_plugin_init(&route->plug);
1036 	route->sformat = sformat;
1037 	route->schannels = schannels;
1038 	route->plug.read = snd_pcm_route_read_areas;
1039 	route->plug.write = snd_pcm_route_write_areas;
1040 	route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
1041 	route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
1042 	route->plug.gen.slave = slave;
1043 	route->plug.gen.close_slave = close_slave;
1044 	route->plug.init = route_chmap_init;
1045 
1046 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
1047 	if (err < 0) {
1048 		free(route);
1049 		return err;
1050 	}
1051 	pcm->ops = &snd_pcm_route_ops;
1052 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
1053 	pcm->private_data = route;
1054 	pcm->poll_fd = slave->poll_fd;
1055 	pcm->poll_events = slave->poll_events;
1056 	pcm->tstamp_type = slave->tstamp_type;
1057 	snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
1058 	snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
1059 	err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
1060 	if (err < 0) {
1061 		snd_pcm_close(pcm);
1062 		return err;
1063 	}
1064 	*pcmp = pcm;
1065 
1066 	return 0;
1067 }
1068 
_snd_pcm_route_determine_ttable(snd_config_t *tt, unsigned int *tt_csize, unsigned int *tt_ssize, snd_pcm_chmap_t *chmap)1069 static int _snd_pcm_route_determine_ttable(snd_config_t *tt,
1070 					   unsigned int *tt_csize,
1071 					   unsigned int *tt_ssize,
1072 					   snd_pcm_chmap_t *chmap)
1073 {
1074 	snd_config_iterator_t i, inext;
1075 	long csize = 0, ssize = 0;
1076 	int err;
1077 
1078 	assert(tt && tt_csize && tt_ssize);
1079 	snd_config_for_each(i, inext, tt) {
1080 		snd_config_t *in = snd_config_iterator_entry(i);
1081 		snd_config_iterator_t j, jnext;
1082 		long cchannel;
1083 		const char *id;
1084 		if (snd_config_get_id(in, &id) < 0)
1085 			continue;
1086 		err = safe_strtol(id, &cchannel);
1087 		if (err < 0) {
1088 			SNDERR("Invalid client channel: %s", id);
1089 			return -EINVAL;
1090 		}
1091 		if (cchannel + 1 > csize)
1092 			csize = cchannel + 1;
1093 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1094 			return -EINVAL;
1095 		snd_config_for_each(j, jnext, in) {
1096 			snd_config_t *jnode = snd_config_iterator_entry(j);
1097 			long schannel;
1098 			const char *id;
1099 			if (snd_config_get_id(jnode, &id) < 0)
1100 				continue;
1101 			err = strtochannel(id, chmap, &schannel, 1);
1102 			if (err < 0) {
1103 				SNDERR("Invalid slave channel: %s", id);
1104 				return -EINVAL;
1105 			}
1106 			if (schannel + 1 > ssize)
1107 				ssize = schannel + 1;
1108 		}
1109 	}
1110 	if (csize == 0 || ssize == 0) {
1111 		SNDERR("Invalid null ttable configuration");
1112 		return -EINVAL;
1113 	}
1114 	*tt_csize = csize;
1115 	*tt_ssize = ssize;
1116 	return 0;
1117 }
1118 
1119 /**
1120  * \brief Determine route matrix sizes
1121  * \param tt Configuration root describing route matrix
1122  * \param tt_csize Returned client size in elements
1123  * \param tt_ssize Returned slave size in elements
1124  * \retval zero on success otherwise a negative error code
1125  */
snd_pcm_route_determine_ttable(snd_config_t *tt, unsigned int *tt_csize, unsigned int *tt_ssize)1126 int snd_pcm_route_determine_ttable(snd_config_t *tt,
1127 				   unsigned int *tt_csize,
1128 				   unsigned int *tt_ssize)
1129 {
1130 	return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
1131 }
1132 
1133 /**
1134  * \brief Load route matrix
1135  * \param tt Configuration root describing route matrix
1136  * \param ttable Returned route matrix
1137  * \param tt_csize Client size in elements
1138  * \param tt_ssize Slave size in elements
1139  * \param tt_cused Used client elements
1140  * \param tt_sused Used slave elements
1141  * \param schannels Slave channels
1142  * \retval zero on success otherwise a negative error code
1143  */
_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)1144 static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1145 				      unsigned int tt_csize, unsigned int tt_ssize,
1146 				      unsigned int *tt_cused, unsigned int *tt_sused,
1147 				      int schannels, snd_pcm_chmap_t *chmap)
1148 {
1149 	int cused = -1;
1150 	int sused = -1;
1151 	snd_config_iterator_t i, inext;
1152 	unsigned int k;
1153 	int err;
1154 
1155 	long *scha = alloca(tt_ssize * sizeof(long));
1156 	if (scha == NULL)
1157 		return -ENOMEM;
1158 
1159 	for (k = 0; k < tt_csize * tt_ssize; ++k)
1160 		ttable[k] = 0.0;
1161 	snd_config_for_each(i, inext, tt) {
1162 		snd_config_t *in = snd_config_iterator_entry(i);
1163 		snd_config_iterator_t j, jnext;
1164 		long cchannel;
1165 		const char *id;
1166 		if (snd_config_get_id(in, &id) < 0)
1167 			continue;
1168 		err = safe_strtol(id, &cchannel);
1169 		if (err < 0 ||
1170 		    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
1171 			SNDERR("Invalid client channel: %s", id);
1172 			return -EINVAL;
1173 		}
1174 		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1175 			return -EINVAL;
1176 		snd_config_for_each(j, jnext, in) {
1177 			snd_config_t *jnode = snd_config_iterator_entry(j);
1178 			double value;
1179 			int ss;
1180 			const char *id;
1181 			if (snd_config_get_id(jnode, &id) < 0)
1182 				continue;
1183 
1184 			ss = strtochannel(id, chmap, scha, tt_ssize);
1185 			if (ss < 0) {
1186 				SNDERR("Invalid slave channel: %s", id);
1187 				return -EINVAL;
1188 			}
1189 
1190 			err = snd_config_get_ireal(jnode, &value);
1191 			if (err < 0) {
1192 				SNDERR("Invalid type for %s", id);
1193 				return -EINVAL;
1194 			}
1195 
1196 			for (k = 0; (int) k < ss; k++) {
1197 				long schannel = scha[k];
1198 				if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
1199 				    (schannels > 0 && schannel >= schannels)) {
1200 					SNDERR("Invalid slave channel: %s", id);
1201 					return -EINVAL;
1202 				}
1203 				ttable[cchannel * tt_ssize + schannel] = value;
1204 				if (schannel > sused)
1205 					sused = schannel;
1206 			}
1207 		}
1208 		if (cchannel > cused)
1209 			cused = cchannel;
1210 	}
1211 	*tt_sused = sused + 1;
1212 	*tt_cused = cused + 1;
1213 	return 0;
1214 }
1215 
1216 /**
1217  * \brief Load route matrix
1218  * \param tt Configuration root describing route matrix
1219  * \param ttable Returned route matrix
1220  * \param tt_csize Client size in elements
1221  * \param tt_ssize Slave size in elements
1222  * \param tt_cused Used client elements
1223  * \param tt_sused Used slave elements
1224  * \param schannels Slave channels
1225  * \retval zero on success otherwise a negative error code
1226  */
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)1227 int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1228 			      unsigned int tt_csize, unsigned int tt_ssize,
1229 			      unsigned int *tt_cused, unsigned int *tt_sused,
1230 			      int schannels)
1231 {
1232 	return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
1233 					  tt_cused, tt_sused, schannels, NULL);
1234 }
1235 
1236 /*! \page pcm_plugins
1237 
1238 \section pcm_plugins_route Plugin: Route & Volume
1239 
1240 This plugin converts channels and applies volume during the conversion.
1241 The format and rate must match for both of them.
1242 
1243 SCHANNEL can be a channel name instead of a number (e g FL, LFE).
1244 If so, a matching channel map will be selected for the slave.
1245 
1246 \code
1247 pcm.name {
1248         type route              # Route & Volume conversion PCM
1249         slave STR               # Slave name
1250         # or
1251         slave {                 # Slave definition
1252                 pcm STR         # Slave PCM name
1253                 # or
1254                 pcm { }         # Slave PCM definition
1255                 [format STR]    # Slave format
1256                 [channels INT]  # Slave channels
1257         }
1258         ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1259                 CCHANNEL {
1260                         SCHANNEL REAL   # route value (0.0 - 1.0)
1261                 }
1262         }
1263         [chmap MAP]             # Override channel maps; MAP is a string array
1264 }
1265 \endcode
1266 
1267 \subsection pcm_plugins_route_funcref Function reference
1268 
1269 <UL>
1270   <LI>snd_pcm_route_open()
1271   <LI>_snd_pcm_route_open()
1272 </UL>
1273 
1274 */
1275 
1276 /**
1277  * \brief Creates a new Route & Volume PCM
1278  * \param pcmp Returns created PCM handle
1279  * \param name Name of PCM
1280  * \param root Root configuration node
1281  * \param conf Configuration node with Route & Volume PCM description
1282  * \param stream Stream type
1283  * \param mode Stream mode
1284  * \retval zero on success otherwise a negative error code
1285  * \warning Using of this function might be dangerous in the sense
1286  *          of compatibility reasons. The prototype might be freely
1287  *          changed in future.
1288  */
_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)1289 int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1290 			snd_config_t *root, snd_config_t *conf,
1291 			snd_pcm_stream_t stream, int mode)
1292 {
1293 	snd_config_iterator_t i, next;
1294 	int err;
1295 	snd_pcm_t *spcm;
1296 	snd_config_t *slave = NULL, *sconf;
1297 	snd_pcm_chmap_t *tt_chmap = NULL, *chmap = NULL;
1298 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1299 	int schannels = -1;
1300 	snd_config_t *tt = NULL;
1301 	snd_pcm_route_ttable_entry_t *ttable = NULL;
1302 	unsigned int csize, ssize;
1303 	unsigned int cused, sused;
1304 	snd_pcm_chmap_query_t **chmaps = NULL;
1305 	snd_config_for_each(i, next, conf) {
1306 		snd_config_t *n = snd_config_iterator_entry(i);
1307 		const char *id;
1308 		if (snd_config_get_id(n, &id) < 0)
1309 			continue;
1310 		if (snd_pcm_conf_generic_id(id))
1311 			continue;
1312 		if (strcmp(id, "slave") == 0) {
1313 			slave = n;
1314 			continue;
1315 		}
1316 		if (strcmp(id, "ttable") == 0) {
1317 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1318 				SNDERR("Invalid type for %s", id);
1319 				snd_pcm_free_chmaps(chmaps);
1320 				return -EINVAL;
1321 			}
1322 			tt = n;
1323 			continue;
1324 		}
1325 		if (strcmp(id, "chmap") == 0) {
1326 			chmaps = _snd_pcm_parse_config_chmaps(n);
1327 			if (!chmaps) {
1328 				SNDERR("Invalid channel map for %s", id);
1329 				return -EINVAL;
1330 			}
1331 			continue;
1332 		}
1333 		SNDERR("Unknown field %s", id);
1334 		return -EINVAL;
1335 	}
1336 	if (!slave) {
1337 		SNDERR("slave is not defined");
1338 		snd_pcm_free_chmaps(chmaps);
1339 		return -EINVAL;
1340 	}
1341 	if (!tt) {
1342 		SNDERR("ttable is not defined");
1343 		snd_pcm_free_chmaps(chmaps);
1344 		return -EINVAL;
1345 	}
1346 	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1347 				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1348 				 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
1349 	if (err < 0) {
1350 		snd_pcm_free_chmaps(chmaps);
1351 		return err;
1352 	}
1353 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1354 	    snd_pcm_format_linear(sformat) != 1) {
1355 	    	snd_config_delete(sconf);
1356 		SNDERR("slave format is not linear");
1357 		snd_pcm_free_chmaps(chmaps);
1358 		return -EINVAL;
1359 	}
1360 
1361 	err = determine_chmap(tt, &tt_chmap);
1362 	if (err < 0) {
1363 		free(ttable);
1364 		return err;
1365 	}
1366 
1367 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1368 	snd_config_delete(sconf);
1369 	if (err < 0) {
1370 		free(tt_chmap);
1371 		free(ttable);
1372 		snd_pcm_free_chmaps(chmaps);
1373 		return err;
1374 	}
1375 
1376 	if (tt_chmap) {
1377 		if (!chmaps)
1378 			chmaps = snd_pcm_query_chmaps(spcm);
1379 		if (chmaps)
1380 			err = find_matching_chmap(chmaps, tt_chmap, &chmap,
1381 						  &schannels);
1382 		free(tt_chmap);
1383 		if (chmaps && err < 0) {
1384 			snd_pcm_free_chmaps(chmaps);
1385 			snd_pcm_close(spcm);
1386 			return err;
1387 		}
1388 	}
1389 
1390 	err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
1391 	if (err < 0) {
1392 		free(chmap);
1393 		snd_pcm_free_chmaps(chmaps);
1394 		snd_pcm_close(spcm);
1395 		return err;
1396 	}
1397 	ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
1398 	if (ttable == NULL) {
1399 		free(chmap);
1400 		snd_pcm_free_chmaps(chmaps);
1401 		snd_pcm_close(spcm);
1402 		return -ENOMEM;
1403 	}
1404 	err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
1405 					&cused, &sused, schannels, chmap);
1406 	if (err < 0) {
1407 		free(chmap);
1408 		free(ttable);
1409 		snd_pcm_free_chmaps(chmaps);
1410 		snd_pcm_close(spcm);
1411 		return err;
1412 	}
1413 
1414 	err = snd_pcm_route_open(pcmp, name, sformat, schannels,
1415 				 ttable, ssize,
1416 				 cused, sused,
1417 				 spcm, 1);
1418 	free(ttable);
1419 	if (err < 0) {
1420 		free(chmap);
1421 		snd_pcm_free_chmaps(chmaps);
1422 		snd_pcm_close(spcm);
1423 	} else {
1424 		snd_pcm_route_t *route = (*pcmp)->private_data;
1425 
1426 		route->chmap = chmap;
1427 		route->chmap_override = chmaps;
1428 	}
1429 
1430 	return err;
1431 }
1432 #ifndef DOC_HIDDEN
1433 SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
1434 #endif
1435