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 == slv->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 switch (slv->access) {
609 case SND_PCM_ACCESS_RW_INTERLEAVED:
610 slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
612 case SND_PCM_ACCESS_RW_NONINTERLEAVED:
613 slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
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,
631 snd_pcm_plug_change_format,
632 #ifdef BUILD_PCM_PLUGIN_ROUTE
633 snd_pcm_plug_change_channels,
635 #ifdef BUILD_PCM_PLUGIN_RATE
636 snd_pcm_plug_change_rate,
638 #ifdef BUILD_PCM_PLUGIN_ROUTE
639 snd_pcm_plug_change_channels,
641 snd_pcm_plug_change_format,
642 snd_pcm_plug_change_access
644 snd_pcm_plug_params_t p = *slave;
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) {
653 if (k >= sizeof(funcs)/sizeof(*funcs))
655 err = funcs[k](pcm, &new, client, &p);
657 snd_pcm_plug_clear(pcm);
661 plug->gen.slave = new;
662 pcm->fast_ops = new->fast_ops;
663 pcm->fast_op_arg = new->fast_op_arg;
667 #ifdef BUILD_PCM_PLUGIN_ROUTE
668 /* it's exception, user specified ttable, but no reduction/expand */
669 if (plug->ttable && !plug->ttable_ok) {
672 plug->ttable_last = 1;
673 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
675 snd_pcm_plug_clear(pcm);
679 assert(plug->ttable_ok);
680 plug->gen.slave = new;
681 pcm->fast_ops = new->fast_ops;
682 pcm->fast_op_arg = new->fast_op_arg;
688 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
690 unsigned int rate_min, channels_max;
693 /* HACK: to avoid overflow in PARTBIT_RATE code */
694 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
697 if (rate_min < 4000) {
698 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
699 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
702 /* HACK: to avoid overflow in PERIOD_SIZE code */
703 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
706 if (channels_max > 10000) {
707 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
708 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
714 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
716 snd_pcm_plug_t *plug = pcm->private_data;
719 _snd_pcm_hw_params_any(sparams);
720 if (plug->sformat >= 0) {
721 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
722 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
724 if (plug->schannels > 0)
725 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
728 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
729 plug->srate, 0, plug->srate + 1, -1);
730 /* reduce the available configurations */
731 err = snd_pcm_hw_refine(plug->req_slave, sparams);
737 static int check_access_change(snd_pcm_hw_params_t *cparams,
738 snd_pcm_hw_params_t *sparams)
740 snd_pcm_access_mask_t *smask;
741 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
742 const snd_pcm_access_mask_t *cmask;
743 snd_pcm_access_mask_t mask;
746 smask = (snd_pcm_access_mask_t *)
747 snd_pcm_hw_param_get_mask(sparams,
748 SND_PCM_HW_PARAM_ACCESS);
749 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
750 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
751 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
752 return 0; /* OK, we have mmap support */
753 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
754 /* no mmap support - we need mmap emulation */
756 if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
757 !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
758 return -EINVAL; /* even no RW access? no way! */
760 cmask = (const snd_pcm_access_mask_t *)
761 snd_pcm_hw_param_get_mask(cparams,
762 SND_PCM_HW_PARAM_ACCESS);
763 snd_mask_none(&mask);
764 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
765 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
766 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
767 snd_pcm_access_mask_set(&mask,
768 SND_PCM_ACCESS_RW_INTERLEAVED);
770 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
771 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
772 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
773 snd_pcm_access_mask_set(&mask,
774 SND_PCM_ACCESS_RW_NONINTERLEAVED);
776 if (!snd_mask_empty(&mask))
777 *smask = mask; /* prefer the straight conversion */
784 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
785 snd_pcm_hw_params_t *sparams)
787 snd_pcm_plug_t *plug = pcm->private_data;
788 snd_pcm_t *slave = plug->req_slave;
789 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
790 SND_PCM_HW_PARBIT_TICK_TIME);
791 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
792 snd_pcm_format_mask_t sfmt_mask;
794 snd_pcm_format_t format;
795 snd_interval_t t, buffer_size;
796 const snd_interval_t *srate, *crate;
798 if (plug->srate == -2 ||
799 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
800 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
801 links |= SND_PCM_HW_PARBIT_RATE;
803 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
808 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
809 links |= SND_PCM_HW_PARBIT_CHANNELS;
811 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
815 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
816 links |= SND_PCM_HW_PARBIT_FORMAT;
818 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
819 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
820 snd_mask_none(&sfmt_mask);
821 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
823 if (!snd_pcm_format_mask_test(format_mask, format))
825 if (snd_pcm_format_mask_test(sformat_mask, format))
828 f = snd_pcm_plug_slave_format(format, sformat_mask);
829 if (f == SND_PCM_FORMAT_UNKNOWN)
832 snd_pcm_format_mask_set(&sfmt_mask, f);
835 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
836 SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
837 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
838 if (!snd_pcm_format_mask_test(format_mask, format))
840 SNDERR("Format: %s", snd_pcm_format_name(format));
842 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
843 if (!snd_pcm_format_mask_test(sformat_mask, format))
845 SNDERR("Slave format: %s", snd_pcm_format_name(format));
849 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
850 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
855 if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
856 err = check_access_change(params, sparams);
858 SNDERR("Unable to find an usable access for '%s'",
864 if ((links & SND_PCM_HW_PARBIT_RATE) ||
865 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
866 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
867 SND_PCM_HW_PARBIT_BUFFER_SIZE);
869 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
870 snd_interval_unfloor(&buffer_size);
871 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
872 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
873 snd_interval_muldiv(&buffer_size, srate, crate, &t);
874 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
878 err = _snd_pcm_hw_params_refine(sparams, links, params);
884 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
885 snd_pcm_hw_params_t *params,
886 snd_pcm_hw_params_t *sparams)
888 snd_pcm_plug_t *plug = pcm->private_data;
889 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
890 SND_PCM_HW_PARBIT_TICK_TIME);
891 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
892 snd_pcm_format_mask_t fmt_mask;
894 snd_pcm_format_t format;
896 const snd_interval_t *sbuffer_size;
897 const snd_interval_t *srate, *crate;
899 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
900 links |= SND_PCM_HW_PARBIT_CHANNELS;
902 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
903 links |= SND_PCM_HW_PARBIT_FORMAT;
905 format_mask = snd_pcm_hw_param_get_mask(params,
906 SND_PCM_HW_PARAM_FORMAT);
907 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
908 SND_PCM_HW_PARAM_FORMAT);
909 snd_mask_none(&fmt_mask);
910 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
912 if (!snd_pcm_format_mask_test(format_mask, format))
914 if (snd_pcm_format_mask_test(sformat_mask, format))
917 f = snd_pcm_plug_slave_format(format, sformat_mask);
918 if (f == SND_PCM_FORMAT_UNKNOWN)
921 snd_pcm_format_mask_set(&fmt_mask, format);
924 if (snd_pcm_format_mask_empty(&fmt_mask)) {
925 SNDERR("Unable to find an usable client format");
926 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
927 if (!snd_pcm_format_mask_test(format_mask, format))
929 SNDERR("Format: %s", snd_pcm_format_name(format));
931 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
932 if (!snd_pcm_format_mask_test(sformat_mask, format))
934 SNDERR("Slave format: %s", snd_pcm_format_name(format));
939 err = _snd_pcm_hw_param_set_mask(params,
940 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
945 if (plug->srate == -2 ||
946 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
947 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
948 links |= SND_PCM_HW_PARBIT_RATE;
950 unsigned int rate_min, srate_min;
951 int rate_mindir, srate_mindir;
953 /* This is a temporary hack, waiting for a better solution */
954 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
957 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
960 if (rate_min == srate_min && srate_mindir > rate_mindir) {
961 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
966 if ((links & SND_PCM_HW_PARBIT_RATE) ||
967 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
968 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
969 SND_PCM_HW_PARBIT_BUFFER_SIZE);
971 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
972 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
973 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
974 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
975 snd_interval_floor(&t);
976 if (snd_interval_empty(&t))
978 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
982 err = _snd_pcm_hw_params_refine(params, links, sparams);
986 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
990 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
992 snd_pcm_plug_t *plug = pcm->private_data;
993 return snd_pcm_hw_refine(plug->req_slave, params);
996 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
998 return snd_pcm_hw_refine_slave(pcm, params,
999 snd_pcm_plug_hw_refine_cprepare,
1000 snd_pcm_plug_hw_refine_cchange,
1001 snd_pcm_plug_hw_refine_sprepare,
1002 snd_pcm_plug_hw_refine_schange,
1003 snd_pcm_plug_hw_refine_slave);
1006 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1008 snd_pcm_plug_t *plug = pcm->private_data;
1009 snd_pcm_t *slave = plug->req_slave;
1010 snd_pcm_plug_params_t clt_params, slv_params;
1011 snd_pcm_hw_params_t sparams;
1014 err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
1017 err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1020 err = snd_pcm_hw_refine_soft(slave, &sparams);
1024 INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1025 INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1026 INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1027 INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1029 INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1030 INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1031 INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1032 snd_pcm_plug_clear(pcm);
1033 if (!(clt_params.format == slv_params.format &&
1034 clt_params.channels == slv_params.channels &&
1035 clt_params.rate == slv_params.rate &&
1037 snd_pcm_hw_params_test_access(slave, &sparams,
1038 clt_params.access) >= 0)) {
1039 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1040 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1044 slave = plug->gen.slave;
1045 err = _snd_pcm_hw_params(slave, params);
1047 snd_pcm_plug_clear(pcm);
1050 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1051 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1052 snd_pcm_link_hw_ptr(pcm, slave);
1053 snd_pcm_link_appl_ptr(pcm, slave);
1057 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1059 snd_pcm_plug_t *plug = pcm->private_data;
1060 snd_pcm_t *slave = plug->gen.slave;
1061 int err = snd_pcm_hw_free(slave);
1062 snd_pcm_plug_clear(pcm);
1066 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1068 snd_pcm_plug_t *plug = pcm->private_data;
1069 snd_output_printf(out, "Plug PCM: ");
1070 snd_pcm_dump(plug->gen.slave, out);
1073 static const snd_pcm_ops_t snd_pcm_plug_ops = {
1074 .close = snd_pcm_plug_close,
1075 .info = snd_pcm_plug_info,
1076 .hw_refine = snd_pcm_plug_hw_refine,
1077 .hw_params = snd_pcm_plug_hw_params,
1078 .hw_free = snd_pcm_plug_hw_free,
1079 .sw_params = snd_pcm_generic_sw_params,
1080 .channel_info = snd_pcm_generic_channel_info,
1081 .dump = snd_pcm_plug_dump,
1082 .nonblock = snd_pcm_generic_nonblock,
1083 .async = snd_pcm_generic_async,
1084 .mmap = snd_pcm_generic_mmap,
1085 .munmap = snd_pcm_generic_munmap,
1089 * \brief Creates a new Plug PCM
1090 * \param pcmp Returns created PCM handle
1091 * \param name Name of PCM
1092 * \param sformat Slave (destination) format
1093 * \param slave Slave PCM handle
1094 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1095 * \retval zero on success otherwise a negative error code
1096 * \warning Using of this function might be dangerous in the sense
1097 * of compatibility reasons. The prototype might be freely
1098 * changed in future.
1100 int snd_pcm_plug_open(snd_pcm_t **pcmp,
1102 snd_pcm_format_t sformat, int schannels, int srate,
1103 const snd_config_t *rate_converter,
1104 enum snd_pcm_plug_route_policy route_policy,
1105 snd_pcm_route_ttable_entry_t *ttable,
1106 unsigned int tt_ssize,
1107 unsigned int tt_cused, unsigned int tt_sused,
1108 snd_pcm_t *slave, int close_slave)
1111 snd_pcm_plug_t *plug;
1113 assert(pcmp && slave);
1115 plug = calloc(1, sizeof(snd_pcm_plug_t));
1118 plug->sformat = sformat;
1119 plug->schannels = schannels;
1120 plug->srate = srate;
1121 plug->rate_converter = rate_converter;
1122 plug->gen.slave = plug->req_slave = slave;
1123 plug->gen.close_slave = close_slave;
1124 plug->route_policy = route_policy;
1125 plug->ttable = ttable;
1126 plug->tt_ssize = tt_ssize;
1127 plug->tt_cused = tt_cused;
1128 plug->tt_sused = tt_sused;
1130 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1135 pcm->ops = &snd_pcm_plug_ops;
1136 pcm->fast_ops = slave->fast_ops;
1137 pcm->fast_op_arg = slave->fast_op_arg;
1138 pcm->private_data = plug;
1139 pcm->poll_fd = slave->poll_fd;
1140 pcm->poll_events = slave->poll_events;
1141 pcm->mmap_shadow = 1;
1142 pcm->monotonic = slave->monotonic;
1143 snd_pcm_link_hw_ptr(pcm, slave);
1144 snd_pcm_link_appl_ptr(pcm, slave);
1150 /*! \page pcm_plugins
1152 \section pcm_plugins_plug Automatic conversion plugin
1154 This plugin converts channels, rate and format on request.
1158 type plug # Automatic conversion PCM
1159 slave STR # Slave name
1161 slave { # Slave definition
1162 pcm STR # Slave PCM name
1164 pcm { } # Slave PCM definition
1165 [format STR] # Slave format (default nearest) or "unchanged"
1166 [channels INT] # Slave channels (default nearest) or "unchanged"
1167 [rate INT] # Slave rate (default nearest) or "unchanged"
1169 route_policy STR # route policy for automatic ttable generation
1170 # STR can be 'default', 'average', 'copy', 'duplicate'
1171 # average: result is average of input channels
1172 # copy: only first channels are copied to destination
1173 # duplicate: duplicate first set of channels
1174 # default: copy policy, except for mono capture - sum
1175 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1177 SCHANNEL REAL # route value (0.0 - 1.0)
1180 rate_converter STR # type of rate converter
1182 rate_converter [ STR1 STR2 ... ]
1183 # type of rate converter
1184 # default value is taken from defaults.pcm.rate_converter
1188 \subsection pcm_plugins_plug_funcref Function reference
1191 <LI>snd_pcm_plug_open()
1192 <LI>_snd_pcm_plug_open()
1198 * \brief Creates a new Plug PCM
1199 * \param pcmp Returns created PCM handle
1200 * \param name Name of PCM
1201 * \param root Root configuration node
1202 * \param conf Configuration node with Plug PCM description
1203 * \param stream Stream type
1204 * \param mode Stream mode
1205 * \retval zero on success otherwise a negative error code
1206 * \warning Using of this function might be dangerous in the sense
1207 * of compatibility reasons. The prototype might be freely
1208 * changed in future.
1210 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1211 snd_config_t *root, snd_config_t *conf,
1212 snd_pcm_stream_t stream, int mode)
1214 snd_config_iterator_t i, next;
1217 snd_config_t *slave = NULL, *sconf;
1218 snd_config_t *tt = NULL;
1219 enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1220 snd_pcm_route_ttable_entry_t *ttable = NULL;
1221 unsigned int csize, ssize;
1222 unsigned int cused, sused;
1223 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1224 int schannels = -1, srate = -1;
1225 const snd_config_t *rate_converter = NULL;
1227 snd_config_for_each(i, next, conf) {
1228 snd_config_t *n = snd_config_iterator_entry(i);
1230 if (snd_config_get_id(n, &id) < 0)
1232 if (snd_pcm_conf_generic_id(id))
1234 if (strcmp(id, "slave") == 0) {
1238 #ifdef BUILD_PCM_PLUGIN_ROUTE
1239 if (strcmp(id, "ttable") == 0) {
1240 route_policy = PLUG_ROUTE_POLICY_NONE;
1241 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1242 SNDERR("Invalid type for %s", id);
1248 if (strcmp(id, "route_policy") == 0) {
1250 if ((err = snd_config_get_string(n, &str)) < 0) {
1251 SNDERR("Invalid type for %s", id);
1255 SNDERR("Table is defined, route policy is ignored");
1256 if (!strcmp(str, "default"))
1257 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1258 else if (!strcmp(str, "average"))
1259 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1260 else if (!strcmp(str, "copy"))
1261 route_policy = PLUG_ROUTE_POLICY_COPY;
1262 else if (!strcmp(str, "duplicate"))
1263 route_policy = PLUG_ROUTE_POLICY_DUP;
1267 #ifdef BUILD_PCM_PLUGIN_RATE
1268 if (strcmp(id, "rate_converter") == 0) {
1273 SNDERR("Unknown field %s", id);
1277 SNDERR("slave is not defined");
1280 err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1281 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1282 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1283 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1286 #ifdef BUILD_PCM_PLUGIN_ROUTE
1288 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1290 snd_config_delete(sconf);
1293 ttable = malloc(csize * ssize * sizeof(*ttable));
1294 if (ttable == NULL) {
1295 snd_config_delete(sconf);
1298 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1300 snd_config_delete(sconf);
1306 #ifdef BUILD_PCM_PLUGIN_RATE
1307 if (! rate_converter)
1308 rate_converter = snd_pcm_rate_get_default_converter(root);
1311 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1312 snd_config_delete(sconf);
1315 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1316 route_policy, ttable, ssize, cused, sused, spcm, 1);
1318 snd_pcm_close(spcm);
1322 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);