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