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