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