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