4 * \brief PCM Route & Volume Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
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"
33 /* entry for static linking */
34 const char *_snd_module_pcm_plug = "";
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,
48 snd_pcm_generic_t gen;
50 snd_pcm_format_t sformat;
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;
62 static int snd_pcm_plug_close(snd_pcm_t *pcm)
64 snd_pcm_plug_t *plug = pcm->private_data;
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);
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;
85 if ((err = snd_pcm_info(slave, info)) < 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,
97 SND_PCM_FORMAT_S16_BE,
98 SND_PCM_FORMAT_U16_BE,
99 SND_PCM_FORMAT_S16_LE,
100 SND_PCM_FORMAT_U16_LE,
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,
108 SND_PCM_FORMAT_S32_BE,
109 SND_PCM_FORMAT_U32_BE,
110 SND_PCM_FORMAT_S32_LE,
111 SND_PCM_FORMAT_U32_LE,
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,
121 SND_PCM_FORMAT_FLOAT_BE,
122 SND_PCM_FORMAT_FLOAT64_BE,
123 SND_PCM_FORMAT_FLOAT_LE,
124 SND_PCM_FORMAT_FLOAT64_LE,
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,
132 SND_PCM_FORMAT_S24_BE,
133 SND_PCM_FORMAT_U24_BE,
134 SND_PCM_FORMAT_S24_LE,
135 SND_PCM_FORMAT_U24_LE,
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,
143 SND_PCM_FORMAT_S24_3BE,
144 SND_PCM_FORMAT_U24_3BE,
145 SND_PCM_FORMAT_S24_3LE,
146 SND_PCM_FORMAT_U24_3LE,
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,
154 SND_PCM_FORMAT_S20_3BE,
155 SND_PCM_FORMAT_U20_3BE,
156 SND_PCM_FORMAT_S20_3LE,
157 SND_PCM_FORMAT_U20_3LE,
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,
165 SND_PCM_FORMAT_S18_3BE,
166 SND_PCM_FORMAT_U18_3BE,
167 SND_PCM_FORMAT_S18_3LE,
168 SND_PCM_FORMAT_U18_3LE,
172 #if defined(BUILD_PCM_PLUGIN_MULAW) || \
173 defined(BUILD_PCM_PLUGIN_ALAW) || \
174 defined(BUILD_PCM_PLUGIN_ADPCM)
175 #define BUILD_PCM_NONLINEAR
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,
183 #ifdef BUILD_PCM_PLUGIN_ALAW
184 SND_PCM_FORMAT_A_LAW,
186 #ifdef BUILD_PCM_PLUGIN_ADPCM
187 SND_PCM_FORMAT_IMA_ADPCM,
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,
200 SND_PCM_FORMAT_FLOAT_BE,
201 SND_PCM_FORMAT_FLOAT64_BE,
202 SND_PCM_FORMAT_FLOAT_LE,
203 SND_PCM_FORMAT_FLOAT64_LE,
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)
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) {
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))
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)
241 snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
242 snd_pcm_format_mask_t fl = {
243 #ifdef BUILD_PCM_PLUGIN_LFLOAT
249 if (snd_pcm_format_mask_test(format_mask, format))
251 if (!snd_pcm_format_mask_test(&lin, format) &&
252 !snd_pcm_format_mask_test(&fl, format)) {
255 #ifdef BUILD_PCM_PLUGIN_MULAW
256 case SND_PCM_FORMAT_MU_LAW:
258 #ifdef BUILD_PCM_PLUGIN_ALAW
259 case SND_PCM_FORMAT_A_LAW:
261 #ifdef BUILD_PCM_PLUGIN_ADPCM
262 case SND_PCM_FORMAT_IMA_ADPCM:
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))
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
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))
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)) {
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))
300 e = snd_pcm_format_big_endian(format);
303 if (snd_mask_empty(&lin)) {
304 #ifdef BUILD_PCM_PLUGIN_LFLOAT
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))
312 return SND_PCM_FORMAT_UNKNOWN;
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)
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)
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;
348 snd_pcm_access_t access;
349 snd_pcm_format_t format;
350 unsigned int channels;
352 } snd_pcm_plug_params_t;
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;
360 if (clt->rate == slv->rate)
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);
367 slv->access = clt->access;
368 slv->rate = clt->rate;
369 if (snd_pcm_format_linear(clt->format))
370 slv->format = clt->format;
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;
382 if (clt->channels == slv->channels &&
383 (!plug->ttable || !plug->ttable_last))
385 if (clt->rate != slv->rate &&
386 clt->channels > slv->channels)
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)
400 else if (s >= plug->tt_sused)
403 v = plug->ttable[c * plug->tt_ssize + s];
404 ttable[c * tt_ssize + s] = v;
410 unsigned int c = 0, s = 0;
411 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
413 for (k = 0; k < tt_cused * tt_sused; ++k)
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;
422 case PLUG_ROUTE_POLICY_AVERAGE:
423 case PLUG_ROUTE_POLICY_DUP:
424 if (clt->channels > slv->channels) {
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)
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)
446 ttable[c * tt_ssize + s] = v;
447 if (++c == clt->channels)
449 if (++s == slv->channels)
453 case PLUG_ROUTE_POLICY_COPY:
454 if (clt->channels < slv->channels) {
459 for (c = 0; (int)c < n; c++)
460 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
463 SNDERR("Invalid route policy");
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);
470 slv->channels = clt->channels;
471 slv->access = clt->access;
472 if (snd_pcm_format_linear(clt->format))
473 slv->format = clt->format;
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;
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 == clt->channels)
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)
497 switch (clt->format) {
498 #ifdef BUILD_PCM_PLUGIN_MULAW
499 case SND_PCM_FORMAT_MU_LAW:
500 f = snd_pcm_mulaw_open;
503 #ifdef BUILD_PCM_PLUGIN_ALAW
504 case SND_PCM_FORMAT_A_LAW:
505 f = snd_pcm_alaw_open;
508 #ifdef BUILD_PCM_PLUGIN_ADPCM
509 case SND_PCM_FORMAT_IMA_ADPCM:
510 f = snd_pcm_adpcm_open;
514 #ifdef BUILD_PCM_PLUGIN_LFLOAT
515 if (snd_pcm_format_float(clt->format))
516 f = snd_pcm_lfloat_open;
520 f = snd_pcm_linear_open;
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)
531 if (snd_pcm_format_linear(clt->format))
532 f = snd_pcm_lfloat_open;
536 #ifdef BUILD_PCM_NONLINEAR
538 switch (slv->format) {
539 #ifdef BUILD_PCM_PLUGIN_MULAW
540 case SND_PCM_FORMAT_MU_LAW:
541 f = snd_pcm_mulaw_open;
544 #ifdef BUILD_PCM_PLUGIN_ALAW
545 case SND_PCM_FORMAT_A_LAW:
546 f = snd_pcm_alaw_open;
549 #ifdef BUILD_PCM_PLUGIN_ADPCM
550 case SND_PCM_FORMAT_IMA_ADPCM:
551 f = snd_pcm_adpcm_open;
557 if (snd_pcm_format_linear(clt->format))
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);
567 slv->access = clt->access;
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;
575 if (clt->access == slv->access)
577 err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
580 slv->access = clt->access;
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;
592 if (clt->access == slv->access)
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:
604 err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
605 plug->gen.slave != plug->req_slave);
608 slv->access = clt->access;
613 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
614 snd_pcm_plug_params_t *client,
615 snd_pcm_plug_params_t *slave)
617 snd_pcm_plug_t *plug = pcm->private_data;
618 static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
619 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
620 snd_pcm_plug_change_mmap,
622 snd_pcm_plug_change_format,
623 #ifdef BUILD_PCM_PLUGIN_ROUTE
624 snd_pcm_plug_change_channels,
626 #ifdef BUILD_PCM_PLUGIN_RATE
627 snd_pcm_plug_change_rate,
629 #ifdef BUILD_PCM_PLUGIN_ROUTE
630 snd_pcm_plug_change_channels,
632 snd_pcm_plug_change_format,
633 snd_pcm_plug_change_access
635 snd_pcm_plug_params_t p = *slave;
637 plug->ttable_ok = plug->ttable_last = 0;
638 while (client->format != p.format ||
639 client->channels != p.channels ||
640 client->rate != p.rate ||
641 client->access != p.access) {
644 if (k >= sizeof(funcs)/sizeof(*funcs))
646 err = funcs[k](pcm, &new, client, &p);
648 snd_pcm_plug_clear(pcm);
652 plug->gen.slave = new;
653 pcm->fast_ops = new->fast_ops;
654 pcm->fast_op_arg = new->fast_op_arg;
658 #ifdef BUILD_PCM_PLUGIN_ROUTE
659 /* it's exception, user specified ttable, but no reduction/expand */
660 if (plug->ttable && !plug->ttable_ok) {
663 plug->ttable_last = 1;
664 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
666 snd_pcm_plug_clear(pcm);
670 assert(plug->ttable_ok);
671 plug->gen.slave = new;
672 pcm->fast_ops = new->fast_ops;
673 pcm->fast_op_arg = new->fast_op_arg;
679 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
681 unsigned int rate_min, channels_max;
684 /* HACK: to avoid overflow in PARTBIT_RATE code */
685 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
688 if (rate_min < 4000) {
689 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
690 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
693 /* HACK: to avoid overflow in PERIOD_SIZE code */
694 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
697 if (channels_max > 10000) {
698 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
699 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
705 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
707 snd_pcm_plug_t *plug = pcm->private_data;
710 _snd_pcm_hw_params_any(sparams);
711 if (plug->sformat >= 0) {
712 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
713 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
715 if (plug->schannels > 0)
716 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
719 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
720 plug->srate, 0, plug->srate + 1, -1);
721 /* reduce the available configurations */
722 err = snd_pcm_hw_refine(plug->req_slave, sparams);
728 static int check_access_change(snd_pcm_hw_params_t *cparams,
729 snd_pcm_hw_params_t *sparams)
731 snd_pcm_access_mask_t *smask;
732 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
733 const snd_pcm_access_mask_t *cmask;
734 snd_pcm_access_mask_t mask;
737 smask = (snd_pcm_access_mask_t *)
738 snd_pcm_hw_param_get_mask(sparams,
739 SND_PCM_HW_PARAM_ACCESS);
740 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
741 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
742 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
743 return 0; /* OK, we have mmap support */
744 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
745 /* no mmap support - we need mmap emulation */
746 cmask = (const snd_pcm_access_mask_t *)
747 snd_pcm_hw_param_get_mask(cparams,
748 SND_PCM_HW_PARAM_ACCESS);
749 snd_mask_none(&mask);
750 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
751 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
752 snd_pcm_access_mask_set(&mask,
753 SND_PCM_ACCESS_RW_INTERLEAVED);
754 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
755 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
756 snd_pcm_access_mask_set(&mask,
757 SND_PCM_ACCESS_RW_NONINTERLEAVED);
765 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
766 snd_pcm_hw_params_t *sparams)
768 snd_pcm_plug_t *plug = pcm->private_data;
769 snd_pcm_t *slave = plug->req_slave;
770 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
771 SND_PCM_HW_PARBIT_TICK_TIME);
772 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
773 snd_pcm_format_mask_t sfmt_mask;
775 snd_pcm_format_t format;
776 snd_interval_t t, buffer_size;
777 const snd_interval_t *srate, *crate;
779 if (plug->srate == -2 ||
780 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
781 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
782 links |= SND_PCM_HW_PARBIT_RATE;
784 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
789 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
790 links |= SND_PCM_HW_PARBIT_CHANNELS;
792 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
796 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
797 links |= SND_PCM_HW_PARBIT_FORMAT;
799 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
800 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
801 snd_mask_none(&sfmt_mask);
802 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
804 if (!snd_pcm_format_mask_test(format_mask, format))
806 if (snd_pcm_format_mask_test(sformat_mask, format))
809 f = snd_pcm_plug_slave_format(format, sformat_mask);
810 if (f == SND_PCM_FORMAT_UNKNOWN)
813 snd_pcm_format_mask_set(&sfmt_mask, f);
816 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
817 SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
818 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
819 if (!snd_pcm_format_mask_test(format_mask, format))
821 SNDERR("Format: %s", snd_pcm_format_name(format));
823 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
824 if (!snd_pcm_format_mask_test(sformat_mask, format))
826 SNDERR("Slave format: %s", snd_pcm_format_name(format));
830 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
831 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
836 if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
837 err = check_access_change(params, sparams);
839 SNDERR("Unable to find an usable access for '%s'",
845 if ((links & SND_PCM_HW_PARBIT_RATE) ||
846 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
847 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
848 SND_PCM_HW_PARBIT_BUFFER_SIZE);
850 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
851 snd_interval_unfloor(&buffer_size);
852 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
853 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
854 snd_interval_muldiv(&buffer_size, srate, crate, &t);
855 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
859 err = _snd_pcm_hw_params_refine(sparams, links, params);
865 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
866 snd_pcm_hw_params_t *params,
867 snd_pcm_hw_params_t *sparams)
869 snd_pcm_plug_t *plug = pcm->private_data;
870 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
871 SND_PCM_HW_PARBIT_TICK_TIME);
872 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
873 snd_pcm_format_mask_t fmt_mask;
875 snd_pcm_format_t format;
877 const snd_interval_t *sbuffer_size;
878 const snd_interval_t *srate, *crate;
880 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
881 links |= SND_PCM_HW_PARBIT_CHANNELS;
883 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
884 links |= SND_PCM_HW_PARBIT_FORMAT;
886 format_mask = snd_pcm_hw_param_get_mask(params,
887 SND_PCM_HW_PARAM_FORMAT);
888 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
889 SND_PCM_HW_PARAM_FORMAT);
890 snd_mask_none(&fmt_mask);
891 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
893 if (!snd_pcm_format_mask_test(format_mask, format))
895 if (snd_pcm_format_mask_test(sformat_mask, format))
898 f = snd_pcm_plug_slave_format(format, sformat_mask);
899 if (f == SND_PCM_FORMAT_UNKNOWN)
902 snd_pcm_format_mask_set(&fmt_mask, format);
905 if (snd_pcm_format_mask_empty(&fmt_mask)) {
906 SNDERR("Unable to find an usable client format");
907 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
908 if (!snd_pcm_format_mask_test(format_mask, format))
910 SNDERR("Format: %s", snd_pcm_format_name(format));
912 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
913 if (!snd_pcm_format_mask_test(sformat_mask, format))
915 SNDERR("Slave format: %s", snd_pcm_format_name(format));
920 err = _snd_pcm_hw_param_set_mask(params,
921 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
926 if (plug->srate == -2 ||
927 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
928 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
929 links |= SND_PCM_HW_PARBIT_RATE;
931 unsigned int rate_min, srate_min;
932 int rate_mindir, srate_mindir;
934 /* This is a temporary hack, waiting for a better solution */
935 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
938 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
941 if (rate_min == srate_min && srate_mindir > rate_mindir) {
942 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
947 if ((links & SND_PCM_HW_PARBIT_RATE) ||
948 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
949 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
950 SND_PCM_HW_PARBIT_BUFFER_SIZE);
952 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
953 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
954 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
955 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
956 snd_interval_floor(&t);
957 if (snd_interval_empty(&t))
959 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
963 err = _snd_pcm_hw_params_refine(params, links, sparams);
967 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
971 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
973 snd_pcm_plug_t *plug = pcm->private_data;
974 return snd_pcm_hw_refine(plug->req_slave, params);
977 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
979 return snd_pcm_hw_refine_slave(pcm, params,
980 snd_pcm_plug_hw_refine_cprepare,
981 snd_pcm_plug_hw_refine_cchange,
982 snd_pcm_plug_hw_refine_sprepare,
983 snd_pcm_plug_hw_refine_schange,
984 snd_pcm_plug_hw_refine_slave);
987 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
989 snd_pcm_plug_t *plug = pcm->private_data;
990 snd_pcm_t *slave = plug->req_slave;
991 snd_pcm_plug_params_t clt_params, slv_params;
992 snd_pcm_hw_params_t sparams;
995 err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
998 err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1001 err = snd_pcm_hw_refine_soft(slave, &sparams);
1005 INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1006 INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1007 INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1008 INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1010 INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1011 INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1012 INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1013 snd_pcm_plug_clear(pcm);
1014 if (!(clt_params.format == slv_params.format &&
1015 clt_params.channels == slv_params.channels &&
1016 clt_params.rate == slv_params.rate &&
1018 snd_pcm_hw_params_test_access(slave, &sparams,
1019 clt_params.access) >= 0)) {
1020 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1021 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1025 slave = plug->gen.slave;
1026 err = _snd_pcm_hw_params(slave, params);
1028 snd_pcm_plug_clear(pcm);
1031 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1032 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1033 snd_pcm_link_hw_ptr(pcm, slave);
1034 snd_pcm_link_appl_ptr(pcm, slave);
1038 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1040 snd_pcm_plug_t *plug = pcm->private_data;
1041 snd_pcm_t *slave = plug->gen.slave;
1042 int err = snd_pcm_hw_free(slave);
1043 snd_pcm_plug_clear(pcm);
1047 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1049 snd_pcm_plug_t *plug = pcm->private_data;
1050 snd_output_printf(out, "Plug PCM: ");
1051 snd_pcm_dump(plug->gen.slave, out);
1054 static const snd_pcm_ops_t snd_pcm_plug_ops = {
1055 .close = snd_pcm_plug_close,
1056 .info = snd_pcm_plug_info,
1057 .hw_refine = snd_pcm_plug_hw_refine,
1058 .hw_params = snd_pcm_plug_hw_params,
1059 .hw_free = snd_pcm_plug_hw_free,
1060 .sw_params = snd_pcm_generic_sw_params,
1061 .channel_info = snd_pcm_generic_channel_info,
1062 .dump = snd_pcm_plug_dump,
1063 .nonblock = snd_pcm_generic_nonblock,
1064 .async = snd_pcm_generic_async,
1065 .mmap = snd_pcm_generic_mmap,
1066 .munmap = snd_pcm_generic_munmap,
1070 * \brief Creates a new Plug PCM
1071 * \param pcmp Returns created PCM handle
1072 * \param name Name of PCM
1073 * \param sformat Slave (destination) format
1074 * \param slave Slave PCM handle
1075 * \param close_slave When set, the slave PCM handle is closed with copy PCM
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_plug_open(snd_pcm_t **pcmp,
1083 snd_pcm_format_t sformat, int schannels, int srate,
1084 const snd_config_t *rate_converter,
1085 enum snd_pcm_plug_route_policy route_policy,
1086 snd_pcm_route_ttable_entry_t *ttable,
1087 unsigned int tt_ssize,
1088 unsigned int tt_cused, unsigned int tt_sused,
1089 snd_pcm_t *slave, int close_slave)
1092 snd_pcm_plug_t *plug;
1094 assert(pcmp && slave);
1096 plug = calloc(1, sizeof(snd_pcm_plug_t));
1099 plug->sformat = sformat;
1100 plug->schannels = schannels;
1101 plug->srate = srate;
1102 plug->rate_converter = rate_converter;
1103 plug->gen.slave = plug->req_slave = slave;
1104 plug->gen.close_slave = close_slave;
1105 plug->route_policy = route_policy;
1106 plug->ttable = ttable;
1107 plug->tt_ssize = tt_ssize;
1108 plug->tt_cused = tt_cused;
1109 plug->tt_sused = tt_sused;
1111 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1116 pcm->ops = &snd_pcm_plug_ops;
1117 pcm->fast_ops = slave->fast_ops;
1118 pcm->fast_op_arg = slave->fast_op_arg;
1119 pcm->private_data = plug;
1120 pcm->poll_fd = slave->poll_fd;
1121 pcm->poll_events = slave->poll_events;
1122 pcm->mmap_shadow = 1;
1123 pcm->monotonic = slave->monotonic;
1124 snd_pcm_link_hw_ptr(pcm, slave);
1125 snd_pcm_link_appl_ptr(pcm, slave);
1131 /*! \page pcm_plugins
1133 \section pcm_plugins_plug Automatic conversion plugin
1135 This plugin converts channels, rate and format on request.
1139 type plug # Automatic conversion PCM
1140 slave STR # Slave name
1142 slave { # Slave definition
1143 pcm STR # Slave PCM name
1145 pcm { } # Slave PCM definition
1146 [format STR] # Slave format (default nearest) or "unchanged"
1147 [channels INT] # Slave channels (default nearest) or "unchanged"
1148 [rate INT] # Slave rate (default nearest) or "unchanged"
1150 route_policy STR # route policy for automatic ttable generation
1151 # STR can be 'default', 'average', 'copy', 'duplicate'
1152 # average: result is average of input channels
1153 # copy: only first channels are copied to destination
1154 # duplicate: duplicate first set of channels
1155 # default: copy policy, except for mono capture - sum
1156 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1158 SCHANNEL REAL # route value (0.0 - 1.0)
1161 rate_converter STR # type of rate converter
1163 rate_converter [ STR1 STR2 ... ]
1164 # type of rate converter
1165 # default value is taken from defaults.pcm.rate_converter
1169 \subsection pcm_plugins_plug_funcref Function reference
1172 <LI>snd_pcm_plug_open()
1173 <LI>_snd_pcm_plug_open()
1179 * \brief Creates a new Plug PCM
1180 * \param pcmp Returns created PCM handle
1181 * \param name Name of PCM
1182 * \param root Root configuration node
1183 * \param conf Configuration node with Plug PCM description
1184 * \param stream Stream type
1185 * \param mode Stream mode
1186 * \retval zero on success otherwise a negative error code
1187 * \warning Using of this function might be dangerous in the sense
1188 * of compatibility reasons. The prototype might be freely
1189 * changed in future.
1191 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1192 snd_config_t *root, snd_config_t *conf,
1193 snd_pcm_stream_t stream, int mode)
1195 snd_config_iterator_t i, next;
1198 snd_config_t *slave = NULL, *sconf;
1199 snd_config_t *tt = NULL;
1200 enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1201 snd_pcm_route_ttable_entry_t *ttable = NULL;
1202 unsigned int csize, ssize;
1203 unsigned int cused, sused;
1204 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1205 int schannels = -1, srate = -1;
1206 const snd_config_t *rate_converter = NULL;
1208 snd_config_for_each(i, next, conf) {
1209 snd_config_t *n = snd_config_iterator_entry(i);
1211 if (snd_config_get_id(n, &id) < 0)
1213 if (snd_pcm_conf_generic_id(id))
1215 if (strcmp(id, "slave") == 0) {
1219 #ifdef BUILD_PCM_PLUGIN_ROUTE
1220 if (strcmp(id, "ttable") == 0) {
1221 route_policy = PLUG_ROUTE_POLICY_NONE;
1222 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1223 SNDERR("Invalid type for %s", id);
1229 if (strcmp(id, "route_policy") == 0) {
1231 if ((err = snd_config_get_string(n, &str)) < 0) {
1232 SNDERR("Invalid type for %s", id);
1236 SNDERR("Table is defined, route policy is ignored");
1237 if (!strcmp(str, "default"))
1238 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1239 else if (!strcmp(str, "average"))
1240 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1241 else if (!strcmp(str, "copy"))
1242 route_policy = PLUG_ROUTE_POLICY_COPY;
1243 else if (!strcmp(str, "duplicate"))
1244 route_policy = PLUG_ROUTE_POLICY_DUP;
1248 #ifdef BUILD_PCM_PLUGIN_RATE
1249 if (strcmp(id, "rate_converter") == 0) {
1254 SNDERR("Unknown field %s", id);
1258 SNDERR("slave is not defined");
1261 err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1262 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1263 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1264 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1267 #ifdef BUILD_PCM_PLUGIN_ROUTE
1269 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1271 snd_config_delete(sconf);
1274 ttable = malloc(csize * ssize * sizeof(*ttable));
1275 if (ttable == NULL) {
1276 snd_config_delete(sconf);
1279 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1281 snd_config_delete(sconf);
1287 #ifdef BUILD_PCM_PLUGIN_RATE
1288 if (! rate_converter)
1289 rate_converter = snd_pcm_rate_get_default_converter(root);
1292 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1293 snd_config_delete(sconf);
1296 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1297 route_policy, ttable, ssize, cused, sused, spcm, 1);
1299 snd_pcm_close(spcm);
1303 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);