• 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_plug.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 - Plug
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 "pcm_local.h"
30 #include "pcm_plugin.h"
32 #ifndef PIC
33 /* entry for static linking */
34 const char *_snd_module_pcm_plug = "";
35 #endif
37 #ifndef DOC_HIDDEN
39 enum snd_pcm_plug_route_policy {
40 PLUG_ROUTE_POLICY_NONE,
41 PLUG_ROUTE_POLICY_DEFAULT,
42 PLUG_ROUTE_POLICY_COPY,
43 PLUG_ROUTE_POLICY_AVERAGE,
44 PLUG_ROUTE_POLICY_DUP,
47 typedef struct {
48 snd_pcm_generic_t gen;
49 snd_pcm_t *req_slave;
50 snd_pcm_format_t sformat;
51 int schannels;
52 int srate;
53 const snd_config_t *rate_converter;
54 enum snd_pcm_plug_route_policy route_policy;
55 snd_pcm_route_ttable_entry_t *ttable;
56 int ttable_ok, ttable_last;
57 unsigned int tt_ssize, tt_cused, tt_sused;
58 } snd_pcm_plug_t;
60 #endif
62 static int snd_pcm_plug_close(snd_pcm_t *pcm)
64 snd_pcm_plug_t *plug = pcm->private_data;
65 int err, result = 0;
66 free(plug->ttable);
67 assert(plug->gen.slave == plug->req_slave);
68 if (plug->gen.close_slave) {
69 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
70 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
71 err = snd_pcm_close(plug->req_slave);
72 if (err < 0)
73 result = err;
75 free(plug);
76 return result;
79 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
81 snd_pcm_plug_t *plug = pcm->private_data;
82 snd_pcm_t *slave = plug->req_slave;
83 int err;
85 if ((err = snd_pcm_info(slave, info)) < 0)
86 return err;
87 return 0;
90 static const snd_pcm_format_t linear_preferred_formats[] = {
91 #ifdef SND_LITTLE_ENDIAN
92 SND_PCM_FORMAT_S16_LE,
93 SND_PCM_FORMAT_U16_LE,
94 SND_PCM_FORMAT_S16_BE,
95 SND_PCM_FORMAT_U16_BE,
96 #else
97 SND_PCM_FORMAT_S16_BE,
98 SND_PCM_FORMAT_U16_BE,
99 SND_PCM_FORMAT_S16_LE,
100 SND_PCM_FORMAT_U16_LE,
101 #endif
102 #ifdef SND_LITTLE_ENDIAN
103 SND_PCM_FORMAT_S32_LE,
104 SND_PCM_FORMAT_U32_LE,
105 SND_PCM_FORMAT_S32_BE,
106 SND_PCM_FORMAT_U32_BE,
107 #else
108 SND_PCM_FORMAT_S32_BE,
109 SND_PCM_FORMAT_U32_BE,
110 SND_PCM_FORMAT_S32_LE,
111 SND_PCM_FORMAT_U32_LE,
112 #endif
113 SND_PCM_FORMAT_S8,
114 SND_PCM_FORMAT_U8,
115 #ifdef SND_LITTLE_ENDIAN
116 SND_PCM_FORMAT_FLOAT_LE,
117 SND_PCM_FORMAT_FLOAT64_LE,
118 SND_PCM_FORMAT_FLOAT_BE,
119 SND_PCM_FORMAT_FLOAT64_BE,
120 #else
121 SND_PCM_FORMAT_FLOAT_BE,
122 SND_PCM_FORMAT_FLOAT64_BE,
123 SND_PCM_FORMAT_FLOAT_LE,
124 SND_PCM_FORMAT_FLOAT64_LE,
125 #endif
126 #ifdef SND_LITTLE_ENDIAN
127 SND_PCM_FORMAT_S24_LE,
128 SND_PCM_FORMAT_U24_LE,
129 SND_PCM_FORMAT_S24_BE,
130 SND_PCM_FORMAT_U24_BE,
131 #else
132 SND_PCM_FORMAT_S24_BE,
133 SND_PCM_FORMAT_U24_BE,
134 SND_PCM_FORMAT_S24_LE,
135 SND_PCM_FORMAT_U24_LE,
136 #endif
137 #ifdef SND_LITTLE_ENDIAN
138 SND_PCM_FORMAT_S24_3LE,
139 SND_PCM_FORMAT_U24_3LE,
140 SND_PCM_FORMAT_S24_3BE,
141 SND_PCM_FORMAT_U24_3BE,
142 #else
143 SND_PCM_FORMAT_S24_3BE,
144 SND_PCM_FORMAT_U24_3BE,
145 SND_PCM_FORMAT_S24_3LE,
146 SND_PCM_FORMAT_U24_3LE,
147 #endif
148 #ifdef SND_LITTLE_ENDIAN
149 SND_PCM_FORMAT_S20_3LE,
150 SND_PCM_FORMAT_U20_3LE,
151 SND_PCM_FORMAT_S20_3BE,
152 SND_PCM_FORMAT_U20_3BE,
153 #else
154 SND_PCM_FORMAT_S20_3BE,
155 SND_PCM_FORMAT_U20_3BE,
156 SND_PCM_FORMAT_S20_3LE,
157 SND_PCM_FORMAT_U20_3LE,
158 #endif
159 #ifdef SND_LITTLE_ENDIAN
160 SND_PCM_FORMAT_S18_3LE,
161 SND_PCM_FORMAT_U18_3LE,
162 SND_PCM_FORMAT_S18_3BE,
163 SND_PCM_FORMAT_U18_3BE,
164 #else
165 SND_PCM_FORMAT_S18_3BE,
166 SND_PCM_FORMAT_U18_3BE,
167 SND_PCM_FORMAT_S18_3LE,
168 SND_PCM_FORMAT_U18_3LE,
169 #endif
172 #if defined(BUILD_PCM_PLUGIN_MULAW) || \
173 defined(BUILD_PCM_PLUGIN_ALAW) || \
174 defined(BUILD_PCM_PLUGIN_ADPCM)
175 #define BUILD_PCM_NONLINEAR
176 #endif
178 #ifdef BUILD_PCM_NONLINEAR
179 static const snd_pcm_format_t nonlinear_preferred_formats[] = {
180 #ifdef BUILD_PCM_PLUGIN_MULAW
181 SND_PCM_FORMAT_MU_LAW,
182 #endif
183 #ifdef BUILD_PCM_PLUGIN_ALAW
184 SND_PCM_FORMAT_A_LAW,
185 #endif
186 #ifdef BUILD_PCM_PLUGIN_ADPCM
187 SND_PCM_FORMAT_IMA_ADPCM,
188 #endif
190 #endif
192 #ifdef BUILD_PCM_PLUGIN_LFLOAT
193 static const snd_pcm_format_t float_preferred_formats[] = {
194 #ifdef SND_LITTLE_ENDIAN
195 SND_PCM_FORMAT_FLOAT_LE,
196 SND_PCM_FORMAT_FLOAT64_LE,
197 SND_PCM_FORMAT_FLOAT_BE,
198 SND_PCM_FORMAT_FLOAT64_BE,
199 #else
200 SND_PCM_FORMAT_FLOAT_BE,
201 SND_PCM_FORMAT_FLOAT64_BE,
202 SND_PCM_FORMAT_FLOAT_LE,
203 SND_PCM_FORMAT_FLOAT64_LE,
204 #endif
206 #endif
208 static const char linear_format_widths[32] = {
209 0, 0, 0, 0, 0, 0, 0, 1,
210 0, 0, 0, 0, 0, 0, 0, 1,
211 0, 1, 0, 1, 0, 0, 0, 1,
212 0, 0, 0, 0, 0, 0, 0, 1,
215 static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
217 int e, s;
218 if (! linear_format_widths[wid - 1])
219 return SND_PCM_FORMAT_UNKNOWN;
220 for (e = 0; e < 2; e++) {
221 for (s = 0; s < 2; s++) {
222 int pw = ((wid + 7) / 8) * 8;
223 for (; pw <= 32; pw += 8) {
224 snd_pcm_format_t f;
225 f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
226 if (f != SND_PCM_FORMAT_UNKNOWN &&
227 snd_pcm_format_mask_test(format_mask, f))
228 return f;
230 sgn = !sgn;
232 ed = !ed;
234 return SND_PCM_FORMAT_UNKNOWN;
237 static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
239 int w, w1, u, e;
240 snd_pcm_format_t f;
241 snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
242 snd_pcm_format_mask_t fl = {
243 #ifdef BUILD_PCM_PLUGIN_LFLOAT
244 SND_PCM_FMTBIT_FLOAT
245 #else
246 { 0 }
247 #endif
249 if (snd_pcm_format_mask_test(format_mask, format))
250 return format;
251 if (!snd_pcm_format_mask_test(&lin, format) &&
252 !snd_pcm_format_mask_test(&fl, format)) {
253 unsigned int i;
254 switch (format) {
255 #ifdef BUILD_PCM_PLUGIN_MULAW
256 case SND_PCM_FORMAT_MU_LAW:
257 #endif
258 #ifdef BUILD_PCM_PLUGIN_ALAW
259 case SND_PCM_FORMAT_A_LAW:
260 #endif
261 #ifdef BUILD_PCM_PLUGIN_ADPCM
262 case SND_PCM_FORMAT_IMA_ADPCM:
263 #endif
264 for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
265 snd_pcm_format_t f = linear_preferred_formats[i];
266 if (snd_pcm_format_mask_test(format_mask, f))
267 return f;
269 /* Fall through */
270 default:
271 return SND_PCM_FORMAT_UNKNOWN;
275 snd_mask_intersect(&lin, format_mask);
276 snd_mask_intersect(&fl, format_mask);
277 if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
278 #ifdef BUILD_PCM_NONLINEAR
279 unsigned int i;
280 for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
281 snd_pcm_format_t f = nonlinear_preferred_formats[i];
282 if (snd_pcm_format_mask_test(format_mask, f))
283 return f;
285 #endif
286 return SND_PCM_FORMAT_UNKNOWN;
288 #ifdef BUILD_PCM_PLUGIN_LFLOAT
289 if (snd_pcm_format_float(format)) {
290 if (snd_pcm_format_mask_test(&fl, format)) {
291 unsigned int i;
292 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
293 snd_pcm_format_t f = float_preferred_formats[i];
294 if (snd_pcm_format_mask_test(format_mask, f))
295 return f;
298 w = 32;
299 u = 0;
300 e = snd_pcm_format_big_endian(format);
301 } else
302 #endif
303 if (snd_mask_empty(&lin)) {
304 #ifdef BUILD_PCM_PLUGIN_LFLOAT
305 unsigned int i;
306 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
307 snd_pcm_format_t f = float_preferred_formats[i];
308 if (snd_pcm_format_mask_test(format_mask, f))
309 return f;
311 #endif
312 return SND_PCM_FORMAT_UNKNOWN;
313 } else {
314 w = snd_pcm_format_width(format);
315 u = snd_pcm_format_unsigned(format);
316 e = snd_pcm_format_big_endian(format);
318 for (w1 = w; w1 <= 32; w1++) {
319 f = check_linear_format(format_mask, w1, u, e);
320 if (f != SND_PCM_FORMAT_UNKNOWN)
321 return f;
323 for (w1 = w - 1; w1 > 0; w1--) {
324 f = check_linear_format(format_mask, w1, u, e);
325 if (f != SND_PCM_FORMAT_UNKNOWN)
326 return f;
328 return SND_PCM_FORMAT_UNKNOWN;
331 static void snd_pcm_plug_clear(snd_pcm_t *pcm)
333 snd_pcm_plug_t *plug = pcm->private_data;
334 snd_pcm_t *slave = plug->req_slave;
335 /* Clear old plugins */
336 if (plug->gen.slave != slave) {
337 snd_pcm_unlink_hw_ptr(pcm, plug->gen.slave);
338 snd_pcm_unlink_appl_ptr(pcm, plug->gen.slave);
339 snd_pcm_close(plug->gen.slave);
340 plug->gen.slave = slave;
341 pcm->fast_ops = slave->fast_ops;
342 pcm->fast_op_arg = slave->fast_op_arg;
346 #ifndef DOC_HIDDEN
347 typedef struct {
348 snd_pcm_access_t access;
349 snd_pcm_format_t format;
350 unsigned int channels;
351 unsigned int rate;
352 } snd_pcm_plug_params_t;
353 #endif
355 #ifdef BUILD_PCM_PLUGIN_RATE
356 static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
358 snd_pcm_plug_t *plug = pcm->private_data;
359 int err;
360 if (clt->rate == slv->rate)
361 return 0;
362 assert(snd_pcm_format_linear(slv->format));
363 err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
364 plug->gen.slave, plug->gen.slave != plug->req_slave);
365 if (err < 0)
366 return err;
367 slv->access = clt->access;
368 slv->rate = clt->rate;
369 if (snd_pcm_format_linear(clt->format))
370 slv->format = clt->format;
371 return 1;
373 #endif
375 #ifdef BUILD_PCM_PLUGIN_ROUTE
376 static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
378 snd_pcm_plug_t *plug = pcm->private_data;
379 unsigned int tt_ssize, tt_cused, tt_sused;
380 snd_pcm_route_ttable_entry_t *ttable;
381 int err;
382 if (clt->channels == slv->channels &&
383 (!plug->ttable || !plug->ttable_last))
384 return 0;
385 if (clt->rate != slv->rate &&
386 clt->channels > slv->channels)
387 return 0;
388 assert(snd_pcm_format_linear(slv->format));
389 tt_ssize = slv->channels;
390 tt_cused = clt->channels;
391 tt_sused = slv->channels;
392 ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
393 if (plug->ttable) { /* expand or shrink table */
394 unsigned int c = 0, s = 0;
395 for (c = 0; c < tt_cused; c++) {
396 for (s = 0; s < tt_sused; s++) {
397 snd_pcm_route_ttable_entry_t v;
398 if (c >= plug->tt_cused)
399 v = 0;
400 else if (s >= plug->tt_sused)
401 v = 0;
402 else
403 v = plug->ttable[c * plug->tt_ssize + s];
404 ttable[c * tt_ssize + s] = v;
407 plug->ttable_ok = 1;
408 } else {
409 unsigned int k;
410 unsigned int c = 0, s = 0;
411 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
412 int n;
413 for (k = 0; k < tt_cused * tt_sused; ++k)
414 ttable[k] = 0;
415 if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
416 rpolicy = PLUG_ROUTE_POLICY_COPY;
417 /* it's hack for mono conversion */
418 if (clt->channels == 1 || slv->channels == 1)
419 rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
421 switch (rpolicy) {
422 case PLUG_ROUTE_POLICY_AVERAGE:
423 case PLUG_ROUTE_POLICY_DUP:
424 if (clt->channels > slv->channels) {
425 n = clt->channels;
426 } else {
427 n = slv->channels;
429 while (n-- > 0) {
430 snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
431 if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
432 if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
433 clt->channels > slv->channels) {
434 int srcs = clt->channels / slv->channels;
435 if (s < clt->channels % slv->channels)
436 srcs++;
437 v /= srcs;
438 } else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
439 slv->channels > clt->channels) {
440 int srcs = slv->channels / clt->channels;
441 if (s < slv->channels % clt->channels)
442 srcs++;
443 v /= srcs;
446 ttable[c * tt_ssize + s] = v;
447 if (++c == clt->channels)
448 c = 0;
449 if (++s == slv->channels)
450 s = 0;
452 break;
453 case PLUG_ROUTE_POLICY_COPY:
454 if (clt->channels < slv->channels) {
455 n = clt->channels;
456 } else {
457 n = slv->channels;
459 for (c = 0; (int)c < n; c++)
460 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
461 break;
462 default:
463 SNDERR("Invalid route policy");
464 break;
467 err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->gen.slave, plug->gen.slave != plug->req_slave);
468 if (err < 0)
469 return err;
470 slv->channels = clt->channels;
471 slv->access = clt->access;
472 if (snd_pcm_format_linear(clt->format))
473 slv->format = clt->format;
474 return 1;
476 #endif
478 static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
480 snd_pcm_plug_t *plug = pcm->private_data;
481 int err;
482 snd_pcm_format_t cfmt;
483 int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
485 /* No conversion is needed */
486 if (clt->format == slv->format &&
487 clt->rate == slv->rate &&
488 clt->channels == slv->channels)
489 return 0;
491 if (snd_pcm_format_linear(slv->format)) {
492 /* Conversion is done in another plugin */
493 if (clt->rate != slv->rate ||
494 clt->channels != slv->channels)
495 return 0;
496 cfmt = clt->format;
497 switch (clt->format) {
498 #ifdef BUILD_PCM_PLUGIN_MULAW
499 case SND_PCM_FORMAT_MU_LAW:
500 f = snd_pcm_mulaw_open;
501 break;
502 #endif
503 #ifdef BUILD_PCM_PLUGIN_ALAW
504 case SND_PCM_FORMAT_A_LAW:
505 f = snd_pcm_alaw_open;
506 break;
507 #endif
508 #ifdef BUILD_PCM_PLUGIN_ADPCM
509 case SND_PCM_FORMAT_IMA_ADPCM:
510 f = snd_pcm_adpcm_open;
511 break;
512 #endif
513 default:
514 #ifdef BUILD_PCM_PLUGIN_LFLOAT
515 if (snd_pcm_format_float(clt->format))
516 f = snd_pcm_lfloat_open;
518 else
519 #endif
520 f = snd_pcm_linear_open;
521 break;
523 #ifdef BUILD_PCM_PLUGIN_LFLOAT
524 } else if (snd_pcm_format_float(slv->format)) {
525 /* Conversion is done in another plugin */
526 if (clt->format == slv->format &&
527 clt->rate == slv->rate &&
528 clt->channels == slv->channels)
529 return 0;
530 cfmt = clt->format;
531 if (snd_pcm_format_linear(clt->format))
532 f = snd_pcm_lfloat_open;
533 else
534 return -EINVAL;
535 #endif
536 #ifdef BUILD_PCM_NONLINEAR
537 } else {
538 switch (slv->format) {
539 #ifdef BUILD_PCM_PLUGIN_MULAW
540 case SND_PCM_FORMAT_MU_LAW:
541 f = snd_pcm_mulaw_open;
542 break;
543 #endif
544 #ifdef BUILD_PCM_PLUGIN_ALAW
545 case SND_PCM_FORMAT_A_LAW:
546 f = snd_pcm_alaw_open;
547 break;
548 #endif
549 #ifdef BUILD_PCM_PLUGIN_ADPCM
550 case SND_PCM_FORMAT_IMA_ADPCM:
551 f = snd_pcm_adpcm_open;
552 break;
553 #endif
554 default:
555 return -EINVAL;
557 if (snd_pcm_format_linear(clt->format))
558 cfmt = clt->format;
559 else
560 cfmt = SND_PCM_FORMAT_S16;
561 #endif /* NONLINEAR */
563 err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
564 if (err < 0)
565 return err;
566 slv->format = cfmt;
567 slv->access = clt->access;
568 return 1;
571 static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
573 snd_pcm_plug_t *plug = pcm->private_data;
574 int err;
575 if (clt->access == slv->access)
576 return 0;
577 err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
578 if (err < 0)
579 return err;
580 slv->access = clt->access;
581 return 1;
584 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
585 static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
586 snd_pcm_plug_params_t *clt,
587 snd_pcm_plug_params_t *slv)
589 snd_pcm_plug_t *plug = pcm->private_data;
590 int err;
592 if (clt->access == slv->access)
593 return 0;
595 switch (slv->access) {
596 case SND_PCM_ACCESS_MMAP_INTERLEAVED:
597 case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
598 case SND_PCM_ACCESS_MMAP_COMPLEX:
599 return 0;
600 default:
601 break;
604 err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
605 plug->gen.slave != plug->req_slave);
606 if (err < 0)
607 return err;
608 switch (slv->access) {
609 case SND_PCM_ACCESS_RW_INTERLEAVED:
610 slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
611 break;
612 case SND_PCM_ACCESS_RW_NONINTERLEAVED:
613 slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
614 break;
615 default:
616 break;
618 return 1;
620 #endif
622 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
623 snd_pcm_plug_params_t *client,
624 snd_pcm_plug_params_t *slave)
626 snd_pcm_plug_t *plug = pcm->private_data;
627 static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
628 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
629 snd_pcm_plug_change_mmap,
630 #endif
631 snd_pcm_plug_change_format,
632 #ifdef BUILD_PCM_PLUGIN_ROUTE
633 snd_pcm_plug_change_channels,
634 #endif
635 #ifdef BUILD_PCM_PLUGIN_RATE
636 snd_pcm_plug_change_rate,
637 #endif
638 #ifdef BUILD_PCM_PLUGIN_ROUTE
639 snd_pcm_plug_change_channels,
640 #endif
641 snd_pcm_plug_change_format,
642 snd_pcm_plug_change_access
644 snd_pcm_plug_params_t p = *slave;
645 unsigned int k = 0;
646 plug->ttable_ok = plug->ttable_last = 0;
647 while (client->format != p.format ||
648 client->channels != p.channels ||
649 client->rate != p.rate ||
650 client->access != p.access) {
651 snd_pcm_t *new;
652 int err;
653 if (k >= sizeof(funcs)/sizeof(*funcs))
654 return -EINVAL;
655 err = funcs[k](pcm, &new, client, &p);
656 if (err < 0) {
657 snd_pcm_plug_clear(pcm);
658 return err;
660 if (err) {
661 plug->gen.slave = new;
663 k++;
665 #ifdef BUILD_PCM_PLUGIN_ROUTE
666 /* it's exception, user specified ttable, but no reduction/expand */
667 if (plug->ttable && !plug->ttable_ok) {
668 snd_pcm_t *new;
669 int err;
670 plug->ttable_last = 1;
671 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
672 if (err < 0) {
673 snd_pcm_plug_clear(pcm);
674 return err;
676 assert(err);
677 assert(plug->ttable_ok);
678 plug->gen.slave = new;
679 pcm->fast_ops = new->fast_ops;
680 pcm->fast_op_arg = new->fast_op_arg;
682 #endif
683 return 0;
686 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
688 unsigned int rate_min, channels_max;
689 int err;
691 /* HACK: to avoid overflow in PARTBIT_RATE code */
692 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
693 if (err < 0)
694 return err;
695 if (rate_min < 4000) {
696 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
697 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
698 return -EINVAL;
700 /* HACK: to avoid overflow in PERIOD_SIZE code */
701 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
702 if (err < 0)
703 return err;
704 if (channels_max > 10000) {
705 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
706 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
707 return -EINVAL;
709 return 0;
712 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
714 snd_pcm_plug_t *plug = pcm->private_data;
715 int err;
717 _snd_pcm_hw_params_any(sparams);
718 if (plug->sformat >= 0) {
719 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
720 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
722 if (plug->schannels > 0)
723 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
724 plug->schannels, 0);
725 if (plug->srate > 0)
726 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
727 plug->srate, 0, plug->srate + 1, -1);
728 /* reduce the available configurations */
729 err = snd_pcm_hw_refine(plug->req_slave, sparams);
730 if (err < 0)
731 return err;
732 return 0;
735 static int check_access_change(snd_pcm_hw_params_t *cparams,
736 snd_pcm_hw_params_t *sparams)
738 snd_pcm_access_mask_t *smask;
739 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
740 const snd_pcm_access_mask_t *cmask;
741 snd_pcm_access_mask_t mask;
742 #endif
744 smask = (snd_pcm_access_mask_t *)
745 snd_pcm_hw_param_get_mask(sparams,
746 SND_PCM_HW_PARAM_ACCESS);
747 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
748 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
749 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
750 return 0; /* OK, we have mmap support */
751 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
752 /* no mmap support - we need mmap emulation */
754 if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
755 !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
756 return -EINVAL; /* even no RW access? no way! */
758 cmask = (const snd_pcm_access_mask_t *)
759 snd_pcm_hw_param_get_mask(cparams,
760 SND_PCM_HW_PARAM_ACCESS);
761 snd_mask_none(&mask);
762 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
763 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
764 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
765 snd_pcm_access_mask_set(&mask,
766 SND_PCM_ACCESS_RW_INTERLEAVED);
768 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
769 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
770 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
771 snd_pcm_access_mask_set(&mask,
772 SND_PCM_ACCESS_RW_NONINTERLEAVED);
774 if (!snd_mask_empty(&mask))
775 *smask = mask; /* prefer the straight conversion */
776 return 0;
777 #else
778 return -EINVAL;
779 #endif
782 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
783 snd_pcm_hw_params_t *sparams)
785 snd_pcm_plug_t *plug = pcm->private_data;
786 snd_pcm_t *slave = plug->req_slave;
787 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
788 SND_PCM_HW_PARBIT_TICK_TIME);
789 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
790 snd_pcm_format_mask_t sfmt_mask;
791 int err;
792 snd_pcm_format_t format;
793 snd_interval_t t, buffer_size;
794 const snd_interval_t *srate, *crate;
796 if (plug->srate == -2 ||
797 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
798 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
799 links |= SND_PCM_HW_PARBIT_RATE;
800 else {
801 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
802 if (err < 0)
803 return err;
806 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
807 links |= SND_PCM_HW_PARBIT_CHANNELS;
808 else {
809 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
810 if (err < 0)
811 return err;
813 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
814 links |= SND_PCM_HW_PARBIT_FORMAT;
815 else {
816 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
817 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
818 snd_mask_none(&sfmt_mask);
819 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
820 snd_pcm_format_t f;
821 if (!snd_pcm_format_mask_test(format_mask, format))
822 continue;
823 if (snd_pcm_format_mask_test(sformat_mask, format))
824 f = format;
825 else {
826 f = snd_pcm_plug_slave_format(format, sformat_mask);
827 if (f == SND_PCM_FORMAT_UNKNOWN)
828 continue;
830 snd_pcm_format_mask_set(&sfmt_mask, f);
833 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
834 SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
835 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
836 if (!snd_pcm_format_mask_test(format_mask, format))
837 continue;
838 SNDERR("Format: %s", snd_pcm_format_name(format));
840 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
841 if (!snd_pcm_format_mask_test(sformat_mask, format))
842 continue;
843 SNDERR("Slave format: %s", snd_pcm_format_name(format));
845 return -EINVAL;
847 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
848 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
849 if (err < 0)
850 return -EINVAL;
853 if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
854 err = check_access_change(params, sparams);
855 if (err < 0) {
856 SNDERR("Unable to find an usable access for '%s'",
857 pcm->name);
858 return err;
862 if ((links & SND_PCM_HW_PARBIT_RATE) ||
863 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
864 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
865 SND_PCM_HW_PARBIT_BUFFER_SIZE);
866 else {
867 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
868 snd_interval_unfloor(&buffer_size);
869 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
870 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
871 snd_interval_muldiv(&buffer_size, srate, crate, &t);
872 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
873 if (err < 0)
874 return err;
876 err = _snd_pcm_hw_params_refine(sparams, links, params);
877 if (err < 0)
878 return err;
879 return 0;
882 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
883 snd_pcm_hw_params_t *params,
884 snd_pcm_hw_params_t *sparams)
886 snd_pcm_plug_t *plug = pcm->private_data;
887 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
888 SND_PCM_HW_PARBIT_TICK_TIME);
889 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
890 snd_pcm_format_mask_t fmt_mask;
891 int err;
892 snd_pcm_format_t format;
893 snd_interval_t t;
894 const snd_interval_t *sbuffer_size;
895 const snd_interval_t *srate, *crate;
897 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
898 links |= SND_PCM_HW_PARBIT_CHANNELS;
900 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
901 links |= SND_PCM_HW_PARBIT_FORMAT;
902 else {
903 format_mask = snd_pcm_hw_param_get_mask(params,
904 SND_PCM_HW_PARAM_FORMAT);
905 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
906 SND_PCM_HW_PARAM_FORMAT);
907 snd_mask_none(&fmt_mask);
908 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
909 snd_pcm_format_t f;
910 if (!snd_pcm_format_mask_test(format_mask, format))
911 continue;
912 if (snd_pcm_format_mask_test(sformat_mask, format))
913 f = format;
914 else {
915 f = snd_pcm_plug_slave_format(format, sformat_mask);
916 if (f == SND_PCM_FORMAT_UNKNOWN)
917 continue;
919 snd_pcm_format_mask_set(&fmt_mask, format);
922 if (snd_pcm_format_mask_empty(&fmt_mask)) {
923 SNDERR("Unable to find an usable client format");
924 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
925 if (!snd_pcm_format_mask_test(format_mask, format))
926 continue;
927 SNDERR("Format: %s", snd_pcm_format_name(format));
929 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
930 if (!snd_pcm_format_mask_test(sformat_mask, format))
931 continue;
932 SNDERR("Slave format: %s", snd_pcm_format_name(format));
934 return -EINVAL;
937 err = _snd_pcm_hw_param_set_mask(params,
938 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
939 if (err < 0)
940 return err;
943 if (plug->srate == -2 ||
944 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
945 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
946 links |= SND_PCM_HW_PARBIT_RATE;
947 else {
948 unsigned int rate_min, srate_min;
949 int rate_mindir, srate_mindir;
951 /* This is a temporary hack, waiting for a better solution */
952 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
953 if (err < 0)
954 return err;
955 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
956 if (err < 0)
957 return err;
958 if (rate_min == srate_min && srate_mindir > rate_mindir) {
959 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
960 if (err < 0)
961 return err;
964 if ((links & SND_PCM_HW_PARBIT_RATE) ||
965 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
966 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
967 SND_PCM_HW_PARBIT_BUFFER_SIZE);
968 else {
969 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
970 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
971 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
972 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
973 snd_interval_floor(&t);
974 if (snd_interval_empty(&t))
975 return -EINVAL;
976 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
977 if (err < 0)
978 return err;
980 err = _snd_pcm_hw_params_refine(params, links, sparams);
981 if (err < 0)
982 return err;
983 /* FIXME */
984 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
985 return 0;
988 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
990 snd_pcm_plug_t *plug = pcm->private_data;
991 return snd_pcm_hw_refine(plug->req_slave, params);
994 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
996 return snd_pcm_hw_refine_slave(pcm, params,
997 snd_pcm_plug_hw_refine_cprepare,
998 snd_pcm_plug_hw_refine_cchange,
999 snd_pcm_plug_hw_refine_sprepare,
1000 snd_pcm_plug_hw_refine_schange,
1001 snd_pcm_plug_hw_refine_slave);
1004 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1006 snd_pcm_plug_t *plug = pcm->private_data;
1007 snd_pcm_t *slave = plug->req_slave;
1008 snd_pcm_plug_params_t clt_params, slv_params;
1009 snd_pcm_hw_params_t sparams;
1010 int err;
1012 err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
1013 if (err < 0)
1014 return err;
1015 err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1016 if (err < 0)
1017 return err;
1018 err = snd_pcm_hw_refine_soft(slave, &sparams);
1019 if (err < 0)
1020 return err;
1022 INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1023 INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1024 INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1025 INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1027 INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1028 INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1029 INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1030 snd_pcm_plug_clear(pcm);
1031 if (!(clt_params.format == slv_params.format &&
1032 clt_params.channels == slv_params.channels &&
1033 clt_params.rate == slv_params.rate &&
1034 !plug->ttable &&
1035 snd_pcm_hw_params_test_access(slave, &sparams,
1036 clt_params.access) >= 0)) {
1037 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1038 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1039 if (err < 0)
1040 return err;
1042 slave = plug->gen.slave;
1043 err = _snd_pcm_hw_params(slave, params);
1044 if (err < 0) {
1045 snd_pcm_plug_clear(pcm);
1046 return err;
1048 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1049 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1051 pcm->fast_ops = slave->fast_ops;
1052 pcm->fast_op_arg = slave->fast_op_arg;
1053 snd_pcm_link_hw_ptr(pcm, slave);
1054 snd_pcm_link_appl_ptr(pcm, slave);
1055 return 0;
1058 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1060 snd_pcm_plug_t *plug = pcm->private_data;
1061 snd_pcm_t *slave = plug->gen.slave;
1062 int err = snd_pcm_hw_free(slave);
1063 snd_pcm_plug_clear(pcm);
1064 return err;
1067 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1069 snd_pcm_plug_t *plug = pcm->private_data;
1070 snd_output_printf(out, "Plug PCM: ");
1071 snd_pcm_dump(plug->gen.slave, out);
1074 static const snd_pcm_ops_t snd_pcm_plug_ops = {
1075 .close = snd_pcm_plug_close,
1076 .info = snd_pcm_plug_info,
1077 .hw_refine = snd_pcm_plug_hw_refine,
1078 .hw_params = snd_pcm_plug_hw_params,
1079 .hw_free = snd_pcm_plug_hw_free,
1080 .sw_params = snd_pcm_generic_sw_params,
1081 .channel_info = snd_pcm_generic_channel_info,
1082 .dump = snd_pcm_plug_dump,
1083 .nonblock = snd_pcm_generic_nonblock,
1084 .async = snd_pcm_generic_async,
1085 .mmap = snd_pcm_generic_mmap,
1086 .munmap = snd_pcm_generic_munmap,
1090 * \brief Creates a new Plug PCM
1091 * \param pcmp Returns created PCM handle
1092 * \param name Name of PCM
1093 * \param sformat Slave (destination) format
1094 * \param slave Slave PCM handle
1095 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1096 * \retval zero on success otherwise a negative error code
1097 * \warning Using of this function might be dangerous in the sense
1098 * of compatibility reasons. The prototype might be freely
1099 * changed in future.
1101 int snd_pcm_plug_open(snd_pcm_t **pcmp,
1102 const char *name,
1103 snd_pcm_format_t sformat, int schannels, int srate,
1104 const snd_config_t *rate_converter,
1105 enum snd_pcm_plug_route_policy route_policy,
1106 snd_pcm_route_ttable_entry_t *ttable,
1107 unsigned int tt_ssize,
1108 unsigned int tt_cused, unsigned int tt_sused,
1109 snd_pcm_t *slave, int close_slave)
1111 snd_pcm_t *pcm;
1112 snd_pcm_plug_t *plug;
1113 int err;
1114 assert(pcmp && slave);
1116 plug = calloc(1, sizeof(snd_pcm_plug_t));
1117 if (!plug)
1118 return -ENOMEM;
1119 plug->sformat = sformat;
1120 plug->schannels = schannels;
1121 plug->srate = srate;
1122 plug->rate_converter = rate_converter;
1123 plug->gen.slave = plug->req_slave = slave;
1124 plug->gen.close_slave = close_slave;
1125 plug->route_policy = route_policy;
1126 plug->ttable = ttable;
1127 plug->tt_ssize = tt_ssize;
1128 plug->tt_cused = tt_cused;
1129 plug->tt_sused = tt_sused;
1131 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1132 if (err < 0) {
1133 free(plug);
1134 return err;
1136 pcm->ops = &snd_pcm_plug_ops;
1137 pcm->fast_ops = slave->fast_ops;
1138 pcm->fast_op_arg = slave->fast_op_arg;
1139 pcm->private_data = plug;
1140 pcm->poll_fd = slave->poll_fd;
1141 pcm->poll_events = slave->poll_events;
1142 pcm->mmap_shadow = 1;
1143 pcm->monotonic = slave->monotonic;
1144 snd_pcm_link_hw_ptr(pcm, slave);
1145 snd_pcm_link_appl_ptr(pcm, slave);
1146 *pcmp = pcm;
1148 return 0;
1151 /*! \page pcm_plugins
1153 \section pcm_plugins_plug Automatic conversion plugin
1155 This plugin converts channels, rate and format on request.
1157 \code
1158 pcm.name {
1159 type plug # Automatic conversion PCM
1160 slave STR # Slave name
1161 # or
1162 slave { # Slave definition
1163 pcm STR # Slave PCM name
1164 # or
1165 pcm { } # Slave PCM definition
1166 [format STR] # Slave format (default nearest) or "unchanged"
1167 [channels INT] # Slave channels (default nearest) or "unchanged"
1168 [rate INT] # Slave rate (default nearest) or "unchanged"
1170 route_policy STR # route policy for automatic ttable generation
1171 # STR can be 'default', 'average', 'copy', 'duplicate'
1172 # average: result is average of input channels
1173 # copy: only first channels are copied to destination
1174 # duplicate: duplicate first set of channels
1175 # default: copy policy, except for mono capture - sum
1176 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1177 CCHANNEL {
1178 SCHANNEL REAL # route value (0.0 - 1.0)
1181 rate_converter STR # type of rate converter
1182 # or
1183 rate_converter [ STR1 STR2 ... ]
1184 # type of rate converter
1185 # default value is taken from defaults.pcm.rate_converter
1187 \endcode
1189 \subsection pcm_plugins_plug_funcref Function reference
1191 <UL>
1192 <LI>snd_pcm_plug_open()
1193 <LI>_snd_pcm_plug_open()
1194 </UL>
1199 * \brief Creates a new Plug PCM
1200 * \param pcmp Returns created PCM handle
1201 * \param name Name of PCM
1202 * \param root Root configuration node
1203 * \param conf Configuration node with Plug PCM description
1204 * \param stream Stream type
1205 * \param mode Stream mode
1206 * \retval zero on success otherwise a negative error code
1207 * \warning Using of this function might be dangerous in the sense
1208 * of compatibility reasons. The prototype might be freely
1209 * changed in future.
1211 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1212 snd_config_t *root, snd_config_t *conf,
1213 snd_pcm_stream_t stream, int mode)
1215 snd_config_iterator_t i, next;
1216 int err;
1217 snd_pcm_t *spcm;
1218 snd_config_t *slave = NULL, *sconf;
1219 snd_config_t *tt = NULL;
1220 enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1221 snd_pcm_route_ttable_entry_t *ttable = NULL;
1222 unsigned int csize, ssize;
1223 unsigned int cused, sused;
1224 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1225 int schannels = -1, srate = -1;
1226 const snd_config_t *rate_converter = NULL;
1228 snd_config_for_each(i, next, conf) {
1229 snd_config_t *n = snd_config_iterator_entry(i);
1230 const char *id;
1231 if (snd_config_get_id(n, &id) < 0)
1232 continue;
1233 if (snd_pcm_conf_generic_id(id))
1234 continue;
1235 if (strcmp(id, "slave") == 0) {
1236 slave = n;
1237 continue;
1239 #ifdef BUILD_PCM_PLUGIN_ROUTE
1240 if (strcmp(id, "ttable") == 0) {
1241 route_policy = PLUG_ROUTE_POLICY_NONE;
1242 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1243 SNDERR("Invalid type for %s", id);
1244 return -EINVAL;
1246 tt = n;
1247 continue;
1249 if (strcmp(id, "route_policy") == 0) {
1250 const char *str;
1251 if ((err = snd_config_get_string(n, &str)) < 0) {
1252 SNDERR("Invalid type for %s", id);
1253 return -EINVAL;
1255 if (tt != NULL)
1256 SNDERR("Table is defined, route policy is ignored");
1257 if (!strcmp(str, "default"))
1258 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1259 else if (!strcmp(str, "average"))
1260 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1261 else if (!strcmp(str, "copy"))
1262 route_policy = PLUG_ROUTE_POLICY_COPY;
1263 else if (!strcmp(str, "duplicate"))
1264 route_policy = PLUG_ROUTE_POLICY_DUP;
1265 continue;
1267 #endif
1268 #ifdef BUILD_PCM_PLUGIN_RATE
1269 if (strcmp(id, "rate_converter") == 0) {
1270 rate_converter = n;
1271 continue;
1273 #endif
1274 SNDERR("Unknown field %s", id);
1275 return -EINVAL;
1277 if (!slave) {
1278 SNDERR("slave is not defined");
1279 return -EINVAL;
1281 err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1282 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1283 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1284 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1285 if (err < 0)
1286 return err;
1287 #ifdef BUILD_PCM_PLUGIN_ROUTE
1288 if (tt) {
1289 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1290 if (err < 0) {
1291 snd_config_delete(sconf);
1292 return err;
1294 ttable = malloc(csize * ssize * sizeof(*ttable));
1295 if (ttable == NULL) {
1296 snd_config_delete(sconf);
1297 return err;
1299 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1300 if (err < 0) {
1301 snd_config_delete(sconf);
1302 return err;
1305 #endif
1307 #ifdef BUILD_PCM_PLUGIN_RATE
1308 if (! rate_converter)
1309 rate_converter = snd_pcm_rate_get_default_converter(root);
1310 #endif
1312 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1313 snd_config_delete(sconf);
1314 if (err < 0)
1315 return err;
1316 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1317 route_policy, ttable, ssize, cused, sused, spcm, 1);
1318 if (err < 0)
1319 snd_pcm_close(spcm);
1320 return err;
1322 #ifndef DOC_HIDDEN
1323 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1324 #endif