• Home
  • History
  • Annotate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/iserver/alsa-lib-1.0.26/src/pcm/

Lines Matching defs:*

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
9 * PCM - Route & Volume Plugin
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <byteswap.h>
30 #include <math.h>
31 #include "pcm_local.h"
32 #include "pcm_plugin.h"
34 #include "plugin_ops.h"
36 #ifndef PIC
37 /* entry for static linking */
38 const char *_snd_module_pcm_route = "";
39 #endif
41 #ifndef DOC_HIDDEN
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
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;
60 typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
62 typedef struct {
63 enum {UINT32=0, UINT64=1, FLOAT=2} 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 ndsts;
71 snd_pcm_route_ttable_dst_t *dsts;
72 } snd_pcm_route_params_t;
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);
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;
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;
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_route_t;
107 #endif /* DOC_HIDDEN */
109 static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
110 snd_pcm_uframes_t dst_offset,
111 const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
112 snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
113 unsigned int src_channels ATTRIBUTE_UNUSED,
114 snd_pcm_uframes_t frames,
115 const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
116 const snd_pcm_route_params_t *params)
118 snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
121 #ifndef DOC_HIDDEN
123 static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
124 snd_pcm_uframes_t dst_offset,
125 const snd_pcm_channel_area_t *src_areas,
126 snd_pcm_uframes_t src_offset,
127 unsigned int src_channels,
128 snd_pcm_uframes_t frames,
129 const snd_pcm_route_ttable_dst_t* ttable,
130 const snd_pcm_route_params_t *params)
132 #define CONV_LABELS
133 #include "plugin_ops.h"
134 #undef CONV_LABELS
135 void *conv;
136 const snd_pcm_channel_area_t *src_area = 0;
137 unsigned int srcidx;
138 const char *src;
139 char *dst;
140 int src_step, dst_step;
141 for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
142 unsigned int channel = ttable->srcs[srcidx].channel;
143 if (channel >= src_channels)
144 continue;
145 src_area = &src_areas[channel];
146 if (src_area->addr != NULL)
147 break;
149 if (srcidx == ttable->nsrcs || srcidx == src_channels) {
150 snd_pcm_route_convert1_zero(dst_area, dst_offset,
151 src_areas, src_offset,
152 src_channels,
153 frames, ttable, params);
154 return;
157 conv = conv_labels[params->conv_idx];
158 src = snd_pcm_channel_area_addr(src_area, src_offset);
159 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
160 src_step = snd_pcm_channel_area_step(src_area);
161 dst_step = snd_pcm_channel_area_step(dst_area);
162 while (frames-- > 0) {
163 goto *conv;
164 #define CONV_END after
165 #include "plugin_ops.h"
166 #undef CONV_END
167 after:
168 src += src_step;
169 dst += dst_step;
173 static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
174 snd_pcm_uframes_t dst_offset,
175 const snd_pcm_channel_area_t *src_areas,
176 snd_pcm_uframes_t src_offset,
177 unsigned int src_channels,
178 snd_pcm_uframes_t frames,
179 const snd_pcm_route_ttable_dst_t* ttable,
180 const snd_pcm_route_params_t *params)
182 #define CONV24_LABELS
183 #include "plugin_ops.h"
184 #undef CONV24_LABELS
185 void *get, *put;
186 const snd_pcm_channel_area_t *src_area = 0;
187 unsigned int srcidx;
188 const char *src;
189 char *dst;
190 int src_step, dst_step;
191 u_int32_t sample = 0;
192 for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
193 unsigned int channel = ttable->srcs[srcidx].channel;
194 if (channel >= src_channels)
195 continue;
196 src_area = &src_areas[channel];
197 if (src_area->addr != NULL)
198 break;
200 if (srcidx == ttable->nsrcs || srcidx == src_channels) {
201 snd_pcm_route_convert1_zero(dst_area, dst_offset,
202 src_areas, src_offset,
203 src_channels,
204 frames, ttable, params);
205 return;
208 get = get32_labels[params->get_idx];
209 put = put32_labels[params->put_idx];
210 src = snd_pcm_channel_area_addr(src_area, src_offset);
211 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
212 src_step = snd_pcm_channel_area_step(src_area);
213 dst_step = snd_pcm_channel_area_step(dst_area);
214 while (frames-- > 0) {
215 goto *get;
216 #define CONV24_END after
217 #include "plugin_ops.h"
218 #undef CONV24_END
219 after:
220 src += src_step;
221 dst += dst_step;
225 static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
226 snd_pcm_uframes_t dst_offset,
227 const snd_pcm_channel_area_t *src_areas,
228 snd_pcm_uframes_t src_offset,
229 unsigned int src_channels,
230 snd_pcm_uframes_t frames,
231 const snd_pcm_route_ttable_dst_t* ttable,
232 const snd_pcm_route_params_t *params)
234 #define GETS_LABELS
235 #define PUT32_LABELS
236 #include "plugin_ops.h"
237 #undef GETS_LABELS
238 #undef PUT32_LABELS
239 static void *const zero_labels[3] = {
240 &&zero_int32, &&zero_int64,
241 #if SND_PCM_PLUGIN_ROUTE_FLOAT
242 &&zero_float
243 #endif
245 /* sum_type att */
246 static void *const add_labels[3 * 2] = {
247 &&add_int32_noatt, &&add_int32_att,
248 &&add_int64_noatt, &&add_int64_att,
249 #if SND_PCM_PLUGIN_ROUTE_FLOAT
250 &&add_float_noatt, &&add_float_att
251 #endif
253 /* sum_type att shift */
254 static void *const norm_labels[3 * 2 * 4] = {
255 0,
256 &&norm_int32_8_noatt,
257 &&norm_int32_16_noatt,
258 &&norm_int32_24_noatt,
259 0,
260 &&norm_int32_8_att,
261 &&norm_int32_16_att,
262 &&norm_int32_24_att,
263 &&norm_int64_0_noatt,
264 &&norm_int64_8_noatt,
265 &&norm_int64_16_noatt,
266 &&norm_int64_24_noatt,
267 &&norm_int64_0_att,
268 &&norm_int64_8_att,
269 &&norm_int64_16_att,
270 &&norm_int64_24_att,
271 #if SND_PCM_PLUGIN_ROUTE_FLOAT
272 &&norm_float_0,
273 &&norm_float_8,
274 &&norm_float_16,
275 &&norm_float_24,
276 &&norm_float_0,
277 &&norm_float_8,
278 &&norm_float_16,
279 &&norm_float_24,
280 #endif
282 void *zero, *get, *add, *norm, *put32;
283 int nsrcs = ttable->nsrcs;
284 char *dst;
285 int dst_step;
286 const char *srcs[nsrcs];
287 int src_steps[nsrcs];
288 snd_pcm_route_ttable_src_t src_tt[nsrcs];
289 int32_t sample = 0;
290 int srcidx, srcidx1 = 0;
291 for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
292 const snd_pcm_channel_area_t *src_area;
293 unsigned int channel = ttable->srcs[srcidx].channel;
294 if (channel >= src_channels)
295 continue;
296 src_area = &src_areas[channel];
297 srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
298 src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
299 src_tt[srcidx1] = ttable->srcs[srcidx];
300 srcidx1++;
302 nsrcs = srcidx1;
303 if (nsrcs == 0) {
304 snd_pcm_route_convert1_zero(dst_area, dst_offset,
305 src_areas, src_offset,
306 src_channels,
307 frames, ttable, params);
308 return;
309 } else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
310 if (params->use_getput)
311 snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
312 src_areas, src_offset,
313 src_channels,
314 frames, ttable, params);
315 else
316 snd_pcm_route_convert1_one(dst_area, dst_offset,
317 src_areas, src_offset,
318 src_channels,
319 frames, ttable, params);
320 return;
323 zero = zero_labels[params->sum_idx];
324 get = gets_labels[params->get_idx];
325 add = add_labels[params->sum_idx * 2 + ttable->att];
326 norm = norm_labels[params->sum_idx * 8 + ttable->att * 4 + 4 - params->src_size];
327 put32 = put32_labels[params->put_idx];
328 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
329 dst_step = snd_pcm_channel_area_step(dst_area);
331 while (frames-- > 0) {
332 snd_pcm_route_ttable_src_t *ttp = src_tt;
333 sum_t sum;
335 /* Zero sum */
336 goto *zero;
337 zero_int32:
338 sum.as_sint32 = 0;
339 goto zero_end;
340 zero_int64:
341 sum.as_sint64 = 0;
342 goto zero_end;
343 #if SND_PCM_PLUGIN_ROUTE_FLOAT
344 zero_float:
345 sum.as_float = 0.0;
346 goto zero_end;
347 #endif
348 zero_end:
349 for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
350 const char *src = srcs[srcidx];
352 /* Get sample */
353 goto *get;
354 #define GETS_END after_get
355 #include "plugin_ops.h"
356 #undef GETS_END
357 after_get:
359 /* Sum */
360 goto *add;
361 add_int32_att:
362 sum.as_sint32 += sample * ttp->as_int;
363 goto after_sum;
364 add_int32_noatt:
365 if (ttp->as_int)
366 sum.as_sint32 += sample;
367 goto after_sum;
368 add_int64_att:
369 sum.as_sint64 += (int64_t) sample * ttp->as_int;
370 goto after_sum;
371 add_int64_noatt:
372 if (ttp->as_int)
373 sum.as_sint64 += sample;
374 goto after_sum;
375 #if SND_PCM_PLUGIN_ROUTE_FLOAT
376 add_float_att:
377 sum.as_float += sample * ttp->as_float;
378 goto after_sum;
379 add_float_noatt:
380 if (ttp->as_int)
381 sum.as_float += sample;
382 goto after_sum;
383 #endif
384 after_sum:
385 srcs[srcidx] += src_steps[srcidx];
386 ttp++;
389 /* Normalization */
390 goto *norm;
391 norm_int32_8_att:
392 sum.as_sint64 = sum.as_sint32;
393 norm_int64_8_att:
394 sum.as_sint64 <<= 8;
395 norm_int64_0_att:
396 div(sum.as_sint64);
397 goto norm_int;
399 norm_int32_16_att:
400 sum.as_sint64 = sum.as_sint32;
401 norm_int64_16_att:
402 sum.as_sint64 <<= 16;
403 div(sum.as_sint64);
404 goto norm_int;
406 norm_int32_24_att:
407 sum.as_sint64 = sum.as_sint32;
408 norm_int64_24_att:
409 sum.as_sint64 <<= 24;
410 div(sum.as_sint64);
411 goto norm_int;
413 norm_int32_8_noatt:
414 sum.as_sint64 = sum.as_sint32;
415 norm_int64_8_noatt:
416 sum.as_sint64 <<= 8;
417 goto norm_int;
419 norm_int32_16_noatt:
420 sum.as_sint64 = sum.as_sint32;
421 norm_int64_16_noatt:
422 sum.as_sint64 <<= 16;
423 goto norm_int;
425 norm_int32_24_noatt:
426 sum.as_sint64 = sum.as_sint32;
427 norm_int64_24_noatt:
428 sum.as_sint64 <<= 24;
429 goto norm_int;
431 norm_int64_0_noatt:
432 norm_int:
433 if (sum.as_sint64 > (int64_t)0x7fffffff)
434 sample = 0x7fffffff; /* maximum positive value */
435 else if (sum.as_sint64 < -(int64_t)0x80000000)
436 sample = 0x80000000; /* maximum negative value */
437 else
438 sample = sum.as_sint64;
439 goto after_norm;
441 #if SND_PCM_PLUGIN_ROUTE_FLOAT
442 norm_float_8:
443 sum.as_float *= 1 << 8;
444 goto norm_float;
445 norm_float_16:
446 sum.as_float *= 1 << 16;
447 goto norm_float;
448 norm_float_24:
449 sum.as_float *= 1 << 24;
450 goto norm_float;
451 norm_float_0:
452 norm_float:
453 sum.as_float = rint(sum.as_float);
454 if (sum.as_float > (int64_t)0x7fffffff)
455 sample = 0x7fffffff; /* maximum positive value */
456 else if (sum.as_float < -(int64_t)0x80000000)
457 sample = 0x80000000; /* maximum negative value */
458 else
459 sample = sum.as_float;
460 goto after_norm;
461 #endif
462 after_norm:
464 /* Put sample */
465 goto *put32;
466 #define PUT32_END after_put32
467 #include "plugin_ops.h"
468 #undef PUT32_END
469 after_put32:
471 dst += dst_step;
475 #endif /* DOC_HIDDEN */
477 static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
478 snd_pcm_uframes_t dst_offset,
479 const snd_pcm_channel_area_t *src_areas,
480 snd_pcm_uframes_t src_offset,
481 unsigned int src_channels,
482 unsigned int dst_channels,
483 snd_pcm_uframes_t frames,
484 snd_pcm_route_params_t *params)
486 unsigned int dst_channel;
487 snd_pcm_route_ttable_dst_t *dstp;
488 const snd_pcm_channel_area_t *dst_area;
490 dstp = params->dsts;
491 dst_area = dst_areas;
492 for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
493 if (dst_channel >= params->ndsts)
494 snd_pcm_route_convert1_zero(dst_area, dst_offset,
495 src_areas, src_offset,
496 src_channels,
497 frames, dstp, params);
498 else
499 dstp->func(dst_area, dst_offset,
500 src_areas, src_offset,
501 src_channels,
502 frames, dstp, params);
503 dstp++;
504 dst_area++;
508 static int snd_pcm_route_close(snd_pcm_t *pcm)
510 snd_pcm_route_t *route = pcm->private_data;
511 snd_pcm_route_params_t *params = &route->params;
512 unsigned int dst_channel;
514 if (params->dsts) {
515 for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
516 free(params->dsts[dst_channel].srcs);
518 free(params->dsts);
520 return snd_pcm_generic_close(pcm);
523 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
525 int err;
526 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
527 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
528 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
529 &access_mask);
530 if (err < 0)
531 return err;
532 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
533 &format_mask);
534 if (err < 0)
535 return err;
536 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
537 if (err < 0)
538 return err;
539 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
540 if (err < 0)
541 return err;
542 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
543 return 0;
546 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
548 snd_pcm_route_t *route = pcm->private_data;
549 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
550 _snd_pcm_hw_params_any(sparams);
551 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
552 &saccess_mask);
553 if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
554 _snd_pcm_hw_params_set_format(sparams, route->sformat);
555 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
557 if (route->schannels >= 0) {
558 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
559 (unsigned int) route->schannels, 0);
561 return 0;
564 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
565 snd_pcm_hw_params_t *sparams)
567 snd_pcm_route_t *route = pcm->private_data;
568 int err;
569 unsigned int links = (SND_PCM_HW_PARBIT_RATE |
570 SND_PCM_HW_PARBIT_PERIODS |
571 SND_PCM_HW_PARBIT_PERIOD_SIZE |
572 SND_PCM_HW_PARBIT_PERIOD_TIME |
573 SND_PCM_HW_PARBIT_BUFFER_SIZE |
574 SND_PCM_HW_PARBIT_BUFFER_TIME |
575 SND_PCM_HW_PARBIT_TICK_TIME);
576 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
577 links |= (SND_PCM_HW_PARBIT_FORMAT |
578 SND_PCM_HW_PARBIT_SUBFORMAT |
579 SND_PCM_HW_PARBIT_SAMPLE_BITS);
580 if (route->schannels < 0)
581 links |= SND_PCM_HW_PARBIT_CHANNELS;
582 err = _snd_pcm_hw_params_refine(sparams, links, params);
583 if (err < 0)
584 return err;
585 return 0;
588 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
589 snd_pcm_hw_params_t *sparams)
591 snd_pcm_route_t *route = pcm->private_data;
592 int err;
593 unsigned int links = (SND_PCM_HW_PARBIT_RATE |
594 SND_PCM_HW_PARBIT_PERIODS |
595 SND_PCM_HW_PARBIT_PERIOD_SIZE |
596 SND_PCM_HW_PARBIT_PERIOD_TIME |
597 SND_PCM_HW_PARBIT_BUFFER_SIZE |
598 SND_PCM_HW_PARBIT_BUFFER_TIME |
599 SND_PCM_HW_PARBIT_TICK_TIME);
600 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
601 links |= (SND_PCM_HW_PARBIT_FORMAT |
602 SND_PCM_HW_PARBIT_SUBFORMAT |
603 SND_PCM_HW_PARBIT_SAMPLE_BITS);
604 if (route->schannels < 0)
605 links |= SND_PCM_HW_PARBIT_CHANNELS;
606 err = _snd_pcm_hw_params_refine(params, links, sparams);
607 if (err < 0)
608 return err;
609 return 0;
612 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
614 return snd_pcm_hw_refine_slave(pcm, params,
615 snd_pcm_route_hw_refine_cprepare,
616 snd_pcm_route_hw_refine_cchange,
617 snd_pcm_route_hw_refine_sprepare,
618 snd_pcm_route_hw_refine_schange,
619 snd_pcm_generic_hw_refine);
622 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
624 snd_pcm_route_t *route = pcm->private_data;
625 snd_pcm_t *slave = route->plug.gen.slave;
626 snd_pcm_format_t src_format, dst_format;
627 int err = snd_pcm_hw_params_slave(pcm, params,
628 snd_pcm_route_hw_refine_cchange,
629 snd_pcm_route_hw_refine_sprepare,
630 snd_pcm_route_hw_refine_schange,
631 snd_pcm_generic_hw_params);
632 if (err < 0)
633 return err;
635 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
636 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
637 dst_format = slave->format;
638 } else {
639 src_format = slave->format;
640 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
642 if (err < 0)
643 return err;
644 route->params.use_getput = snd_pcm_format_physical_width(src_format) == 24 ||
645 snd_pcm_format_physical_width(dst_format) == 24;
646 route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S16);
647 route->params.put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, dst_format);
648 route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
649 route->params.src_size = snd_pcm_format_width(src_format) / 8;
650 route->params.dst_sfmt = dst_format;
651 #if SND_PCM_PLUGIN_ROUTE_FLOAT
652 route->params.sum_idx = FLOAT;
653 #else
654 if (snd_pcm_format_width(src_format) == 32)
655 route->params.sum_idx = UINT64;
656 else
657 route->params.sum_idx = UINT32;
658 #endif
659 return 0;
662 static snd_pcm_uframes_t
663 snd_pcm_route_write_areas(snd_pcm_t *pcm,
664 const snd_pcm_channel_area_t *areas,
665 snd_pcm_uframes_t offset,
666 snd_pcm_uframes_t size,
667 const snd_pcm_channel_area_t *slave_areas,
668 snd_pcm_uframes_t slave_offset,
669 snd_pcm_uframes_t *slave_sizep)
671 snd_pcm_route_t *route = pcm->private_data;
672 snd_pcm_t *slave = route->plug.gen.slave;
673 if (size > *slave_sizep)
674 size = *slave_sizep;
675 snd_pcm_route_convert(slave_areas, slave_offset,
676 areas, offset,
677 pcm->channels,
678 slave->channels,
679 size, &route->params);
680 *slave_sizep = size;
681 return size;
684 static snd_pcm_uframes_t
685 snd_pcm_route_read_areas(snd_pcm_t *pcm,
686 const snd_pcm_channel_area_t *areas,
687 snd_pcm_uframes_t offset,
688 snd_pcm_uframes_t size,
689 const snd_pcm_channel_area_t *slave_areas,
690 snd_pcm_uframes_t slave_offset,
691 snd_pcm_uframes_t *slave_sizep)
693 snd_pcm_route_t *route = pcm->private_data;
694 snd_pcm_t *slave = route->plug.gen.slave;
695 if (size > *slave_sizep)
696 size = *slave_sizep;
697 snd_pcm_route_convert(areas, offset,
698 slave_areas, slave_offset,
699 slave->channels,
700 pcm->channels,
701 size, &route->params);
702 *slave_sizep = size;
703 return size;
706 static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
708 snd_pcm_route_t *route = pcm->private_data;
709 unsigned int dst;
710 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
711 snd_output_printf(out, "Route conversion PCM\n");
712 else
713 snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
714 snd_pcm_format_name(route->sformat));
715 snd_output_puts(out, " Transformation table:\n");
716 for (dst = 0; dst < route->params.ndsts; dst++) {
717 snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
718 unsigned int src;
719 snd_output_printf(out, " %d <- ", dst);
720 if (d->nsrcs == 0) {
721 snd_output_printf(out, "none\n");
722 continue;
724 src = 0;
725 while (1) {
726 snd_pcm_route_ttable_src_t *s = &d->srcs[src];
727 if (d->att)
728 #if SND_PCM_PLUGIN_ROUTE_FLOAT
729 snd_output_printf(out, "%d*%g", s->channel, s->as_float);
730 #else
731 snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
732 #endif
733 else
734 snd_output_printf(out, "%d", s->channel);
735 src++;
736 if (src == d->nsrcs)
737 break;
738 snd_output_puts(out, " + ");
740 snd_output_putc(out, '\n');
742 if (pcm->setup) {
743 snd_output_printf(out, "Its setup is:\n");
744 snd_pcm_dump_setup(pcm, out);
746 snd_output_printf(out, "Slave: ");
747 snd_pcm_dump(route->plug.gen.slave, out);
750 static const snd_pcm_ops_t snd_pcm_route_ops = {
751 .close = snd_pcm_route_close,
752 .info = snd_pcm_generic_info,
753 .hw_refine = snd_pcm_route_hw_refine,
754 .hw_params = snd_pcm_route_hw_params,
755 .hw_free = snd_pcm_generic_hw_free,
756 .sw_params = snd_pcm_generic_sw_params,
757 .channel_info = snd_pcm_generic_channel_info,
758 .dump = snd_pcm_route_dump,
759 .nonblock = snd_pcm_generic_nonblock,
760 .async = snd_pcm_generic_async,
761 .mmap = snd_pcm_generic_mmap,
762 .munmap = snd_pcm_generic_munmap,
765 static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
766 unsigned int tt_ssize,
767 snd_pcm_route_ttable_entry_t *ttable,
768 unsigned int tt_cused, unsigned int tt_sused)
770 unsigned int src_channel, dst_channel;
771 snd_pcm_route_ttable_dst_t *dptr;
772 unsigned int sused, dused, smul, dmul;
773 if (stream == SND_PCM_STREAM_PLAYBACK) {
774 sused = tt_cused;
775 dused = tt_sused;
776 smul = tt_ssize;
777 dmul = 1;
778 } else {
779 sused = tt_sused;
780 dused = tt_cused;
781 smul = 1;
782 dmul = tt_ssize;
784 params->ndsts = dused;
785 dptr = calloc(dused, sizeof(*params->dsts));
786 if (!dptr)
787 return -ENOMEM;
788 params->dsts = dptr;
789 for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
790 snd_pcm_route_ttable_entry_t t = 0;
791 int att = 0;
792 int nsrcs = 0;
793 snd_pcm_route_ttable_src_t srcs[sused];
794 for (src_channel = 0; src_channel < sused; ++src_channel) {
795 snd_pcm_route_ttable_entry_t v;
796 v = ttable[src_channel * smul + dst_channel * dmul];
797 if (v != 0) {
798 srcs[nsrcs].channel = src_channel;
799 #if SND_PCM_PLUGIN_ROUTE_FLOAT
800 /* Also in user space for non attenuated */
801 srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
802 srcs[nsrcs].as_float = v;
803 #else
804 assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
805 srcs[nsrcs].as_int = v;
806 #endif
807 if (v != SND_PCM_PLUGIN_ROUTE_FULL)
808 att = 1;
809 t += v;
810 nsrcs++;
813 #if 0
814 assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
815 #endif
816 dptr->att = att;
817 dptr->nsrcs = nsrcs;
818 if (nsrcs == 0)
819 dptr->func = snd_pcm_route_convert1_zero;
820 else
821 dptr->func = snd_pcm_route_convert1_many;
822 if (nsrcs > 0) {
823 dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
824 if (!dptr->srcs)
825 return -ENOMEM;
826 memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
827 } else
828 dptr->srcs = 0;
829 dptr++;
831 return 0;
835 * \brief Creates a new Route & Volume PCM
836 * \param pcmp Returns created PCM handle
837 * \param name Name of PCM
838 * \param sformat Slave format
839 * \param schannels Slave channels
840 * \param ttable Attenuation table
841 * \param tt_ssize Attenuation table - slave size
842 * \param tt_cused Attenuation table - client used count
843 * \param tt_sused Attenuation table - slave used count
844 * \param slave Slave PCM handle
845 * \param close_slave When set, the slave PCM handle is closed with copy PCM
846 * \retval zero on success otherwise a negative error code
847 * \warning Using of this function might be dangerous in the sense
848 * of compatibility reasons. The prototype might be freely
849 * changed in future.
851 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
852 snd_pcm_format_t sformat, int schannels,
853 snd_pcm_route_ttable_entry_t *ttable,
854 unsigned int tt_ssize,
855 unsigned int tt_cused, unsigned int tt_sused,
856 snd_pcm_t *slave, int close_slave)
858 snd_pcm_t *pcm;
859 snd_pcm_route_t *route;
860 int err;
861 assert(pcmp && slave && ttable);
862 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
863 snd_pcm_format_linear(sformat) != 1)
864 return -EINVAL;
865 route = calloc(1, sizeof(snd_pcm_route_t));
866 if (!route) {
867 return -ENOMEM;
869 snd_pcm_plugin_init(&route->plug);
870 route->sformat = sformat;
871 route->schannels = schannels;
872 route->plug.read = snd_pcm_route_read_areas;
873 route->plug.write = snd_pcm_route_write_areas;
874 route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
875 route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
876 route->plug.gen.slave = slave;
877 route->plug.gen.close_slave = close_slave;
879 err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
880 if (err < 0) {
881 free(route);
882 return err;
884 pcm->ops = &snd_pcm_route_ops;
885 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
886 pcm->private_data = route;
887 pcm->poll_fd = slave->poll_fd;
888 pcm->poll_events = slave->poll_events;
889 pcm->monotonic = slave->monotonic;
890 snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
891 snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
892 err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
893 if (err < 0) {
894 snd_pcm_close(pcm);
895 return err;
897 *pcmp = pcm;
899 return 0;
903 * \brief Determine route matrix sizes
904 * \param tt Configuration root describing route matrix
905 * \param tt_csize Returned client size in elements
906 * \param tt_ssize Returned slave size in elements
907 * \retval zero on success otherwise a negative error code
909 int snd_pcm_route_determine_ttable(snd_config_t *tt,
910 unsigned int *tt_csize,
911 unsigned int *tt_ssize)
913 snd_config_iterator_t i, inext;
914 long csize = 0, ssize = 0;
915 int err;
917 assert(tt && tt_csize && tt_ssize);
918 snd_config_for_each(i, inext, tt) {
919 snd_config_t *in = snd_config_iterator_entry(i);
920 snd_config_iterator_t j, jnext;
921 long cchannel;
922 const char *id;
923 if (!snd_config_get_id(in, &id) < 0)
924 continue;
925 err = safe_strtol(id, &cchannel);
926 if (err < 0) {
927 SNDERR("Invalid client channel: %s", id);
928 return -EINVAL;
930 if (cchannel + 1 > csize)
931 csize = cchannel + 1;
932 if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
933 return -EINVAL;
934 snd_config_for_each(j, jnext, in) {
935 snd_config_t *jnode = snd_config_iterator_entry(j);
936 long schannel;
937 const char *id;
938 if (snd_config_get_id(jnode, &id) < 0)
939 continue;
940 err = safe_strtol(id, &schannel);
941 if (err < 0) {
942 SNDERR("Invalid slave channel: %s", id);
943 return -EINVAL;
945 if (schannel + 1 > ssize)
946 ssize = schannel + 1;
949 if (csize == 0 || ssize == 0) {
950 SNDERR("Invalid null ttable configuration");
951 return -EINVAL;
953 *tt_csize = csize;
954 *tt_ssize = ssize;
955 return 0;
959 * \brief Load route matrix
960 * \param tt Configuration root describing route matrix
961 * \param ttable Returned route matrix
962 * \param tt_csize Client size in elements
963 * \param tt_ssize Slave size in elements
964 * \param tt_cused Used client elements
965 * \param tt_sused Used slave elements
966 * \param schannels Slave channels
967 * \retval zero on success otherwise a negative error code
969 int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
970 unsigned int tt_csize, unsigned int tt_ssize,
971 unsigned int *tt_cused, unsigned int *tt_sused,
972 int schannels)
974 int cused = -1;
975 int sused = -1;
976 snd_config_iterator_t i, inext;
977 unsigned int k;
978 int err;
979 for (k = 0; k < tt_csize * tt_ssize; ++k)
980 ttable[k] = 0.0;
981 snd_config_for_each(i, inext, tt) {
982 snd_config_t *in = snd_config_iterator_entry(i);
983 snd_config_iterator_t j, jnext;
984 long cchannel;
985 const char *id;
986 if (!snd_config_get_id(in, &id) < 0)
987 continue;
988 err = safe_strtol(id, &cchannel);
989 if (err < 0 ||
990 cchannel < 0 || (unsigned int) cchannel > tt_csize) {
991 SNDERR("Invalid client channel: %s", id);
992 return -EINVAL;
994 if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
995 return -EINVAL;
996 snd_config_for_each(j, jnext, in) {
997 snd_config_t *jnode = snd_config_iterator_entry(j);
998 double value;
999 long schannel;
1000 const char *id;
1001 if (snd_config_get_id(jnode, &id) < 0)
1002 continue;
1003 err = safe_strtol(id, &schannel);
1004 if (err < 0 ||
1005 schannel < 0 || (unsigned int) schannel > tt_ssize ||
1006 (schannels > 0 && schannel >= schannels)) {
1007 SNDERR("Invalid slave channel: %s", id);
1008 return -EINVAL;
1010 err = snd_config_get_real(jnode, &value);
1011 if (err < 0) {
1012 long v;
1013 err = snd_config_get_integer(jnode, &v);
1014 if (err < 0) {
1015 SNDERR("Invalid type for %s", id);
1016 return -EINVAL;
1018 value = v;
1020 ttable[cchannel * tt_ssize + schannel] = value;
1021 if (schannel > sused)
1022 sused = schannel;
1024 if (cchannel > cused)
1025 cused = cchannel;
1027 *tt_sused = sused + 1;
1028 *tt_cused = cused + 1;
1029 return 0;
1032 /*! \page pcm_plugins
1034 \section pcm_plugins_route Plugin: Route & Volume
1036 This plugin converts channels and applies volume during the conversion.
1037 The format and rate must match for both of them.
1039 \code
1040 pcm.name {
1041 type route # Route & Volume conversion PCM
1042 slave STR # Slave name
1043 # or
1044 slave { # Slave definition
1045 pcm STR # Slave PCM name
1046 # or
1047 pcm { } # Slave PCM definition
1048 [format STR] # Slave format
1049 [channels INT] # Slave channels
1051 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1052 CCHANNEL {
1053 SCHANNEL REAL # route value (0.0 - 1.0)
1057 \endcode
1059 \subsection pcm_plugins_route_funcref Function reference
1061 <UL>
1062 <LI>snd_pcm_route_open()
1063 <LI>_snd_pcm_route_open()
1064 </UL>
1069 * \brief Creates a new Route & Volume PCM
1070 * \param pcmp Returns created PCM handle
1071 * \param name Name of PCM
1072 * \param root Root configuration node
1073 * \param conf Configuration node with Route & Volume PCM description
1074 * \param stream Stream type
1075 * \param mode Stream mode
1076 * \retval zero on success otherwise a negative error code
1077 * \warning Using of this function might be dangerous in the sense
1078 * of compatibility reasons. The prototype might be freely
1079 * changed in future.
1081 int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1082 snd_config_t *root, snd_config_t *conf,
1083 snd_pcm_stream_t stream, int mode)
1085 snd_config_iterator_t i, next;
1086 int err;
1087 snd_pcm_t *spcm;
1088 snd_config_t *slave = NULL, *sconf;
1089 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1090 int schannels = -1;
1091 snd_config_t *tt = NULL;
1092 snd_pcm_route_ttable_entry_t *ttable = NULL;
1093 unsigned int csize, ssize;
1094 unsigned int cused, sused;
1095 snd_config_for_each(i, next, conf) {
1096 snd_config_t *n = snd_config_iterator_entry(i);
1097 const char *id;
1098 if (snd_config_get_id(n, &id) < 0)
1099 continue;
1100 if (snd_pcm_conf_generic_id(id))
1101 continue;
1102 if (strcmp(id, "slave") == 0) {
1103 slave = n;
1104 continue;
1106 if (strcmp(id, "ttable") == 0) {
1107 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1108 SNDERR("Invalid type for %s", id);
1109 return -EINVAL;
1111 tt = n;
1112 continue;
1114 SNDERR("Unknown field %s", id);
1115 return -EINVAL;
1117 if (!slave) {
1118 SNDERR("slave is not defined");
1119 return -EINVAL;
1121 if (!tt) {
1122 SNDERR("ttable is not defined");
1123 return -EINVAL;
1125 err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1126 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1127 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
1128 if (err < 0)
1129 return err;
1130 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1131 snd_pcm_format_linear(sformat) != 1) {
1132 snd_config_delete(sconf);
1133 SNDERR("slave format is not linear");
1134 return -EINVAL;
1137 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1138 if (err < 0) {
1139 snd_config_delete(sconf);
1140 return err;
1142 ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
1143 if (ttable == NULL) {
1144 snd_config_delete(sconf);
1145 return -ENOMEM;
1147 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
1148 &cused, &sused, schannels);
1149 if (err < 0) {
1150 free(ttable);
1151 snd_config_delete(sconf);
1152 return err;
1155 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1156 snd_config_delete(sconf);
1157 if (err < 0) {
1158 free(ttable);
1159 return err;
1161 err = snd_pcm_route_open(pcmp, name, sformat, schannels,
1162 ttable, ssize,
1163 cused, sused,
1164 spcm, 1);
1165 free(ttable);
1166 if (err < 0)
1167 snd_pcm_close(spcm);
1168 return err;
1170 #ifndef DOC_HIDDEN
1171 SND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
1172 #endif