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,
51 snd_pcm_format_t sformat;
54 enum snd_pcm_plug_route_policy route_policy;
55 snd_pcm_route_ttable_entry_t *ttable;
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;
68 if (plug->slave != plug->req_slave) {
69 SNDERR("plug slaves mismatch");
72 if (plug->close_slave) {
73 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
74 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
75 err = snd_pcm_close(plug->req_slave);
83 static int snd_pcm_plug_nonblock(snd_pcm_t *pcm, int nonblock)
85 snd_pcm_plug_t *plug = pcm->private_data;
86 return snd_pcm_nonblock(plug->slave, nonblock);
89 static int snd_pcm_plug_async(snd_pcm_t *pcm, int sig, pid_t pid)
91 snd_pcm_plug_t *plug = pcm->private_data;
92 return snd_pcm_async(plug->slave, sig, pid);
95 static int snd_pcm_plug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
97 snd_pcm_plug_t *plug = pcm->private_data;
98 return snd_pcm_poll_descriptors_revents(plug->slave, pfds, nfds, revents);
101 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
103 snd_pcm_plug_t *plug = pcm->private_data;
104 snd_pcm_t *slave = plug->req_slave;
107 if ((err = snd_pcm_info(slave, info)) < 0)
112 static snd_pcm_format_t linear_preferred_formats[] = {
113 #ifdef SND_LITTLE_ENDIAN
114 SND_PCM_FORMAT_S16_LE,
115 SND_PCM_FORMAT_U16_LE,
116 SND_PCM_FORMAT_S16_BE,
117 SND_PCM_FORMAT_U16_BE,
119 SND_PCM_FORMAT_S16_BE,
120 SND_PCM_FORMAT_U16_BE,
121 SND_PCM_FORMAT_S16_LE,
122 SND_PCM_FORMAT_U16_LE,
124 #ifdef SND_LITTLE_ENDIAN
125 SND_PCM_FORMAT_S32_LE,
126 SND_PCM_FORMAT_U32_LE,
127 SND_PCM_FORMAT_S32_BE,
128 SND_PCM_FORMAT_U32_BE,
130 SND_PCM_FORMAT_S32_BE,
131 SND_PCM_FORMAT_U32_BE,
132 SND_PCM_FORMAT_S32_LE,
133 SND_PCM_FORMAT_U32_LE,
137 #ifdef SND_LITTLE_ENDIAN
138 SND_PCM_FORMAT_FLOAT_LE,
139 SND_PCM_FORMAT_FLOAT64_LE,
140 SND_PCM_FORMAT_FLOAT_BE,
141 SND_PCM_FORMAT_FLOAT64_BE,
143 SND_PCM_FORMAT_FLOAT_BE,
144 SND_PCM_FORMAT_FLOAT64_BE,
145 SND_PCM_FORMAT_FLOAT_LE,
146 SND_PCM_FORMAT_FLOAT64_LE,
148 #ifdef SND_LITTLE_ENDIAN
149 SND_PCM_FORMAT_S24_LE,
150 SND_PCM_FORMAT_U24_LE,
151 SND_PCM_FORMAT_S24_BE,
152 SND_PCM_FORMAT_U24_BE,
154 SND_PCM_FORMAT_S24_BE,
155 SND_PCM_FORMAT_U24_BE,
156 SND_PCM_FORMAT_S24_LE,
157 SND_PCM_FORMAT_U24_LE,
159 #ifdef SND_LITTLE_ENDIAN
160 SND_PCM_FORMAT_S24_3LE,
161 SND_PCM_FORMAT_U24_3LE,
162 SND_PCM_FORMAT_S24_3BE,
163 SND_PCM_FORMAT_U24_3BE,
165 SND_PCM_FORMAT_S24_3BE,
166 SND_PCM_FORMAT_U24_3BE,
167 SND_PCM_FORMAT_S24_3LE,
168 SND_PCM_FORMAT_U24_3LE,
170 #ifdef SND_LITTLE_ENDIAN
171 SND_PCM_FORMAT_S20_3LE,
172 SND_PCM_FORMAT_U20_3LE,
173 SND_PCM_FORMAT_S20_3BE,
174 SND_PCM_FORMAT_U20_3BE,
176 SND_PCM_FORMAT_S20_3BE,
177 SND_PCM_FORMAT_U20_3BE,
178 SND_PCM_FORMAT_S20_3LE,
179 SND_PCM_FORMAT_U20_3LE,
181 #ifdef SND_LITTLE_ENDIAN
182 SND_PCM_FORMAT_S18_3LE,
183 SND_PCM_FORMAT_U18_3LE,
184 SND_PCM_FORMAT_S18_3BE,
185 SND_PCM_FORMAT_U18_3BE,
187 SND_PCM_FORMAT_S18_3BE,
188 SND_PCM_FORMAT_U18_3BE,
189 SND_PCM_FORMAT_S18_3LE,
190 SND_PCM_FORMAT_U18_3LE,
194 static snd_pcm_format_t nonlinear_preferred_formats[] = {
195 SND_PCM_FORMAT_MU_LAW,
196 SND_PCM_FORMAT_A_LAW,
197 SND_PCM_FORMAT_IMA_ADPCM,
200 static snd_pcm_format_t float_preferred_formats[] = {
201 #ifdef SND_LITTLE_ENDIAN
202 SND_PCM_FORMAT_FLOAT_LE,
203 SND_PCM_FORMAT_FLOAT64_LE,
204 SND_PCM_FORMAT_FLOAT_BE,
205 SND_PCM_FORMAT_FLOAT64_BE,
207 SND_PCM_FORMAT_FLOAT_BE,
208 SND_PCM_FORMAT_FLOAT64_BE,
209 SND_PCM_FORMAT_FLOAT_LE,
210 SND_PCM_FORMAT_FLOAT64_LE,
214 static char linear_format_widths[32] = {
215 0, 0, 0, 0, 0, 0, 0, 1,
216 0, 0, 0, 0, 0, 0, 0, 1,
217 0, 1, 0, 1, 0, 0, 0, 1,
218 0, 0, 0, 0, 0, 0, 0, 1,
221 static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
224 if (! linear_format_widths[wid - 1])
225 return SND_PCM_FORMAT_UNKNOWN;
226 for (e = 0; e < 2; e++) {
227 for (s = 0; s < 2; s++) {
228 int pw = ((wid + 7) / 8) * 8;
229 for (; pw <= 32; pw += 8) {
231 f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
232 if (f != SND_PCM_FORMAT_UNKNOWN &&
233 snd_pcm_format_mask_test(format_mask, f))
240 return SND_PCM_FORMAT_UNKNOWN;
243 static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
247 snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
248 snd_pcm_format_mask_t fl = { SND_PCM_FMTBIT_FLOAT };
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 case SND_PCM_FORMAT_MU_LAW:
256 case SND_PCM_FORMAT_A_LAW:
257 case SND_PCM_FORMAT_IMA_ADPCM:
258 for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
259 snd_pcm_format_t f = linear_preferred_formats[i];
260 if (snd_pcm_format_mask_test(format_mask, f))
265 return SND_PCM_FORMAT_UNKNOWN;
269 snd_mask_intersect(&lin, format_mask);
270 snd_mask_intersect(&fl, format_mask);
271 if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
273 for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
274 snd_pcm_format_t f = nonlinear_preferred_formats[i];
275 if (snd_pcm_format_mask_test(format_mask, f))
278 return SND_PCM_FORMAT_UNKNOWN;
280 if (snd_pcm_format_float(format)) {
281 if (snd_pcm_format_mask_test(&fl, format)) {
283 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
284 snd_pcm_format_t f = float_preferred_formats[i];
285 if (snd_pcm_format_mask_test(format_mask, f))
291 e = snd_pcm_format_big_endian(format);
292 } else if (snd_mask_empty(&lin)) {
294 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
295 snd_pcm_format_t f = float_preferred_formats[i];
296 if (snd_pcm_format_mask_test(format_mask, f))
299 return SND_PCM_FORMAT_UNKNOWN;
301 w = snd_pcm_format_width(format);
302 u = snd_pcm_format_unsigned(format);
303 e = snd_pcm_format_big_endian(format);
305 for (w1 = w; w1 <= 32; w1++) {
306 f = check_linear_format(format_mask, w1, u, e);
307 if (f != SND_PCM_FORMAT_UNKNOWN)
310 for (w1 = w - 1; w1 > 0; w1--) {
311 f = check_linear_format(format_mask, w1, u, e);
312 if (f != SND_PCM_FORMAT_UNKNOWN)
315 return SND_PCM_FORMAT_UNKNOWN;
318 static void snd_pcm_plug_clear(snd_pcm_t *pcm)
320 snd_pcm_plug_t *plug = pcm->private_data;
321 snd_pcm_t *slave = plug->req_slave;
322 /* Clear old plugins */
323 if (plug->slave != slave) {
324 snd_pcm_unlink_hw_ptr(pcm, plug->slave);
325 snd_pcm_unlink_appl_ptr(pcm, plug->slave);
326 snd_pcm_close(plug->slave);
328 pcm->fast_ops = slave->fast_ops;
329 pcm->fast_op_arg = slave->fast_op_arg;
334 snd_pcm_access_t access;
335 snd_pcm_format_t format;
336 unsigned int channels;
338 } snd_pcm_plug_params_t;
340 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)
342 snd_pcm_plug_t *plug = pcm->private_data;
344 assert(snd_pcm_format_linear(slv->format));
345 if (clt->rate == slv->rate)
347 err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->slave, plug->slave != plug->req_slave);
350 slv->access = clt->access;
351 slv->rate = clt->rate;
352 if (snd_pcm_format_linear(clt->format))
353 slv->format = clt->format;
357 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)
359 snd_pcm_plug_t *plug = pcm->private_data;
360 unsigned int tt_ssize, tt_cused, tt_sused;
361 snd_pcm_route_ttable_entry_t *ttable;
363 assert(snd_pcm_format_linear(slv->format));
364 if (clt->channels == slv->channels && !plug->ttable)
366 if (clt->rate != slv->rate &&
367 clt->channels > slv->channels)
369 tt_ssize = slv->channels;
370 tt_cused = clt->channels;
371 tt_sused = slv->channels;
372 ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
373 if (plug->ttable) { /* expand or shrink table */
374 unsigned int c = 0, s = 0;
375 for (c = 0; c < tt_cused; c++) {
376 for (s = 0; s < tt_sused; s++) {
377 snd_pcm_route_ttable_entry_t v;
378 if (c >= plug->tt_cused)
380 else if (s >= plug->tt_sused)
383 v = plug->ttable[c * plug->tt_ssize + s];
384 ttable[c * tt_ssize + s] = v;
390 unsigned int c = 0, s = 0;
391 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
393 for (k = 0; k < tt_cused * tt_sused; ++k)
395 if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
396 rpolicy = PLUG_ROUTE_POLICY_COPY;
397 /* it's hack for mono conversion */
398 if (clt->channels == 1 || slv->channels == 1)
399 rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
402 case PLUG_ROUTE_POLICY_AVERAGE:
403 case PLUG_ROUTE_POLICY_DUP:
404 if (clt->channels > slv->channels) {
410 snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
411 if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
412 if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
413 clt->channels > slv->channels) {
414 int srcs = clt->channels / slv->channels;
415 if (s < clt->channels % slv->channels)
418 } else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
419 slv->channels > clt->channels) {
420 int srcs = slv->channels / clt->channels;
421 if (s < slv->channels % clt->channels)
426 ttable[c * tt_ssize + s] = v;
427 if (++c == clt->channels)
429 if (++s == slv->channels)
433 case PLUG_ROUTE_POLICY_COPY:
434 if (clt->channels < slv->channels) {
439 for (c = 0; (int)c < n; c++)
440 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
443 SNDERR("Invalid route policy");
447 err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave);
450 slv->channels = clt->channels;
451 slv->access = clt->access;
452 if (snd_pcm_format_linear(clt->format))
453 slv->format = clt->format;
457 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)
459 snd_pcm_plug_t *plug = pcm->private_data;
461 snd_pcm_format_t cfmt;
462 int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
463 if (snd_pcm_format_linear(slv->format)) {
464 /* Conversion is done in another plugin */
465 if (clt->format == slv->format ||
466 clt->rate != slv->rate ||
467 clt->channels != slv->channels)
470 switch (clt->format) {
471 case SND_PCM_FORMAT_MU_LAW:
472 f = snd_pcm_mulaw_open;
474 case SND_PCM_FORMAT_A_LAW:
475 f = snd_pcm_alaw_open;
477 case SND_PCM_FORMAT_IMA_ADPCM:
478 f = snd_pcm_adpcm_open;
481 if (snd_pcm_format_float(clt->format)) {
482 f = snd_pcm_lfloat_open;
484 assert(snd_pcm_format_linear(clt->format));
485 f = snd_pcm_linear_open;
489 } else if (snd_pcm_format_float(slv->format)) {
490 /* Conversion is done in another plugin */
491 if (clt->format == slv->format &&
492 clt->rate == slv->rate &&
493 clt->channels == slv->channels)
496 if (snd_pcm_format_linear(clt->format))
497 f = snd_pcm_lfloat_open;
499 assert(0); /* TODO */
503 /* No conversion is needed */
504 if (clt->format == slv->format &&
505 clt->rate == slv->rate &&
506 clt->channels == clt->channels)
508 switch (slv->format) {
509 case SND_PCM_FORMAT_MU_LAW:
510 f = snd_pcm_mulaw_open;
512 case SND_PCM_FORMAT_A_LAW:
513 f = snd_pcm_alaw_open;
515 case SND_PCM_FORMAT_IMA_ADPCM:
516 f = snd_pcm_adpcm_open;
522 if (snd_pcm_format_linear(clt->format))
525 cfmt = SND_PCM_FORMAT_S16;
527 err = f(new, NULL, slv->format, plug->slave, plug->slave != plug->req_slave);
531 slv->access = clt->access;
535 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)
537 snd_pcm_plug_t *plug = pcm->private_data;
539 if (clt->access == slv->access)
541 err = snd_pcm_copy_open(new, NULL, plug->slave, plug->slave != plug->req_slave);
544 slv->access = clt->access;
548 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
549 snd_pcm_plug_params_t *client,
550 snd_pcm_plug_params_t *slave)
552 snd_pcm_plug_t *plug = pcm->private_data;
553 int (*funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
554 snd_pcm_plug_change_format,
555 snd_pcm_plug_change_channels,
556 snd_pcm_plug_change_rate,
557 snd_pcm_plug_change_channels,
558 snd_pcm_plug_change_format,
559 snd_pcm_plug_change_access
561 snd_pcm_plug_params_t p = *slave;
564 while (client->format != p.format ||
565 client->channels != p.channels ||
566 client->rate != p.rate ||
567 client->access != p.access) {
570 assert(k < sizeof(funcs)/sizeof(*funcs));
571 err = funcs[k](pcm, &new, client, &p);
573 snd_pcm_plug_clear(pcm);
578 pcm->fast_ops = new->fast_ops;
579 pcm->fast_op_arg = new->fast_op_arg;
583 /* it's exception, user specified ttable, but no reduction/expand */
584 if (plug->ttable && !plug->ttable_ok) {
587 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
589 snd_pcm_plug_clear(pcm);
593 assert(plug->ttable_ok);
595 pcm->fast_ops = new->fast_ops;
596 pcm->fast_op_arg = new->fast_op_arg;
601 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
603 unsigned int rate_min, channels_max;
606 /* HACK: to avoid overflow in PARTBIT_RATE code */
607 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
610 if (rate_min < 4000) {
611 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
612 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
615 /* HACK: to avoid overflow in PERIOD_SIZE code */
616 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
619 if (channels_max > 10000) {
620 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
621 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
627 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
629 snd_pcm_plug_t *plug = pcm->private_data;
632 _snd_pcm_hw_params_any(sparams);
633 if (plug->sformat >= 0) {
634 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
635 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
637 if (plug->schannels > 0)
638 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
641 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
642 plug->srate, 0, plug->srate + 1, -1);
643 /* reduce the available configurations */
644 err = snd_pcm_hw_refine(plug->req_slave, sparams);
650 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
651 snd_pcm_hw_params_t *sparams)
653 snd_pcm_plug_t *plug = pcm->private_data;
654 snd_pcm_t *slave = plug->req_slave;
655 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
656 SND_PCM_HW_PARBIT_TICK_TIME);
657 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
658 snd_pcm_format_mask_t sfmt_mask;
660 snd_pcm_format_t format;
661 snd_interval_t t, buffer_size;
662 const snd_interval_t *srate, *crate;
664 if (plug->srate == -2)
665 links |= SND_PCM_HW_PARBIT_RATE;
667 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
672 if (plug->schannels == -2)
673 links |= SND_PCM_HW_PARBIT_CHANNELS;
675 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
679 if (plug->sformat == -2)
680 links |= SND_PCM_HW_PARBIT_FORMAT;
682 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
683 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
684 snd_mask_none(&sfmt_mask);
685 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
687 if (!snd_pcm_format_mask_test(format_mask, format))
689 if (snd_pcm_format_mask_test(sformat_mask, format))
692 f = snd_pcm_plug_slave_format(format, sformat_mask);
693 if (f == SND_PCM_FORMAT_UNKNOWN)
696 snd_pcm_format_mask_set(&sfmt_mask, f);
699 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
700 SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
701 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
702 if (!snd_pcm_format_mask_test(format_mask, format))
704 SNDERR("Format: %s", snd_pcm_format_name(format));
706 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
707 if (!snd_pcm_format_mask_test(sformat_mask, format))
709 SNDERR("Slave format: %s", snd_pcm_format_name(format));
713 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
714 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
719 if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_FORMAT, sparams) ||
720 snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_CHANNELS, sparams) ||
721 snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_RATE, sparams) ||
722 snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
723 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_MMAP };
724 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
726 if (snd_pcm_access_mask_empty(snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS))) {
727 SNDERR("Unable to find an usable access for '%s'", pcm->name);
731 if ((links & SND_PCM_HW_PARBIT_RATE) ||
732 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
733 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
734 SND_PCM_HW_PARBIT_BUFFER_SIZE);
736 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
737 snd_interval_unfloor(&buffer_size);
738 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
739 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
740 snd_interval_muldiv(&buffer_size, srate, crate, &t);
741 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
745 err = _snd_pcm_hw_params_refine(sparams, links, params);
751 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
752 snd_pcm_hw_params_t *params,
753 snd_pcm_hw_params_t *sparams)
755 snd_pcm_plug_t *plug = pcm->private_data;
756 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
757 SND_PCM_HW_PARBIT_TICK_TIME);
758 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
759 snd_pcm_format_mask_t fmt_mask;
761 snd_pcm_format_t format;
763 const snd_interval_t *sbuffer_size;
764 const snd_interval_t *srate, *crate;
766 if (plug->schannels == -2)
767 links |= SND_PCM_HW_PARBIT_CHANNELS;
769 if (plug->sformat == -2)
770 links |= SND_PCM_HW_PARBIT_FORMAT;
772 format_mask = snd_pcm_hw_param_get_mask(params,
773 SND_PCM_HW_PARAM_FORMAT);
774 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
775 SND_PCM_HW_PARAM_FORMAT);
776 snd_mask_none(&fmt_mask);
777 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
779 if (!snd_pcm_format_mask_test(format_mask, format))
781 if (snd_pcm_format_mask_test(sformat_mask, format))
784 f = snd_pcm_plug_slave_format(format, sformat_mask);
785 if (f == SND_PCM_FORMAT_UNKNOWN)
788 snd_pcm_format_mask_set(&fmt_mask, format);
791 if (snd_pcm_format_mask_empty(&fmt_mask)) {
792 SNDERR("Unable to find an usable client format");
793 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
794 if (!snd_pcm_format_mask_test(format_mask, format))
796 SNDERR("Format: %s", snd_pcm_format_name(format));
798 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
799 if (!snd_pcm_format_mask_test(sformat_mask, format))
801 SNDERR("Slave format: %s", snd_pcm_format_name(format));
806 err = _snd_pcm_hw_param_set_mask(params,
807 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
812 if (plug->srate == -2)
813 links |= SND_PCM_HW_PARBIT_RATE;
815 unsigned int rate_min, srate_min;
816 int rate_mindir, srate_mindir;
818 /* This is a temporary hack, waiting for a better solution */
819 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
822 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
825 if (rate_min == srate_min && srate_mindir > rate_mindir) {
826 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
831 if ((links & SND_PCM_HW_PARBIT_RATE) ||
832 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
833 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
834 SND_PCM_HW_PARBIT_BUFFER_SIZE);
836 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
837 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
838 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
839 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
840 snd_interval_floor(&t);
841 if (snd_interval_empty(&t))
843 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
847 err = _snd_pcm_hw_params_refine(params, links, sparams);
851 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
855 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
857 snd_pcm_plug_t *plug = pcm->private_data;
858 return snd_pcm_hw_refine(plug->req_slave, params);
861 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
863 return snd_pcm_hw_refine_slave(pcm, params,
864 snd_pcm_plug_hw_refine_cprepare,
865 snd_pcm_plug_hw_refine_cchange,
866 snd_pcm_plug_hw_refine_sprepare,
867 snd_pcm_plug_hw_refine_schange,
868 snd_pcm_plug_hw_refine_slave);
871 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
873 snd_pcm_plug_t *plug = pcm->private_data;
874 snd_pcm_t *slave = plug->req_slave;
875 snd_pcm_plug_params_t clt_params, slv_params;
876 snd_pcm_hw_params_t sparams;
879 err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
881 err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
883 err = snd_pcm_hw_refine_soft(slave, &sparams);
886 INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
887 INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
888 INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
889 INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
891 INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
892 INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
893 INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
894 snd_pcm_plug_clear(pcm);
895 if (!(clt_params.format == slv_params.format &&
896 clt_params.channels == slv_params.channels &&
897 clt_params.rate == slv_params.rate &&
899 snd_pcm_hw_params_test_access(slave, &sparams,
900 clt_params.access) >= 0)) {
901 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
902 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
907 err = _snd_pcm_hw_params(slave, params);
909 snd_pcm_plug_clear(pcm);
912 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
913 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
914 snd_pcm_link_hw_ptr(pcm, slave);
915 snd_pcm_link_appl_ptr(pcm, slave);
919 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
921 snd_pcm_plug_t *plug = pcm->private_data;
922 snd_pcm_t *slave = plug->slave;
923 int err = snd_pcm_hw_free(slave);
924 snd_pcm_plug_clear(pcm);
928 static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
930 snd_pcm_plug_t *plug = pcm->private_data;
931 return snd_pcm_sw_params(plug->slave, params);
934 static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
936 snd_pcm_plug_t *plug = pcm->private_data;
937 return snd_pcm_channel_info(plug->slave, info);
940 static int snd_pcm_plug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
945 static int snd_pcm_plug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
950 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
952 snd_pcm_plug_t *plug = pcm->private_data;
953 snd_output_printf(out, "Plug PCM: ");
954 snd_pcm_dump(plug->slave, out);
957 static snd_pcm_ops_t snd_pcm_plug_ops = {
958 .close = snd_pcm_plug_close,
959 .info = snd_pcm_plug_info,
960 .hw_refine = snd_pcm_plug_hw_refine,
961 .hw_params = snd_pcm_plug_hw_params,
962 .hw_free = snd_pcm_plug_hw_free,
963 .sw_params = snd_pcm_plug_sw_params,
964 .channel_info = snd_pcm_plug_channel_info,
965 .dump = snd_pcm_plug_dump,
966 .nonblock = snd_pcm_plug_nonblock,
967 .async = snd_pcm_plug_async,
968 .poll_revents = snd_pcm_plug_poll_revents,
969 .mmap = snd_pcm_plug_mmap,
970 .munmap = snd_pcm_plug_munmap,
974 * \brief Creates a new Plug PCM
975 * \param pcmp Returns created PCM handle
976 * \param name Name of PCM
977 * \param sformat Slave (destination) format
978 * \param slave Slave PCM handle
979 * \param close_slave When set, the slave PCM handle is closed with copy PCM
980 * \retval zero on success otherwise a negative error code
981 * \warning Using of this function might be dangerous in the sense
982 * of compatibility reasons. The prototype might be freely
985 int snd_pcm_plug_open(snd_pcm_t **pcmp,
987 snd_pcm_format_t sformat, int schannels, int srate,
988 enum snd_pcm_plug_route_policy route_policy,
989 snd_pcm_route_ttable_entry_t *ttable,
990 unsigned int tt_ssize,
991 unsigned int tt_cused, unsigned int tt_sused,
992 snd_pcm_t *slave, int close_slave)
995 snd_pcm_plug_t *plug;
997 assert(pcmp && slave);
999 plug = calloc(1, sizeof(snd_pcm_plug_t));
1002 plug->sformat = sformat;
1003 plug->schannels = schannels;
1004 plug->srate = srate;
1005 plug->slave = plug->req_slave = slave;
1006 plug->close_slave = close_slave;
1007 plug->route_policy = route_policy;
1008 plug->ttable = ttable;
1009 plug->tt_ssize = tt_ssize;
1010 plug->tt_cused = tt_cused;
1011 plug->tt_sused = tt_sused;
1013 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1018 pcm->ops = &snd_pcm_plug_ops;
1019 pcm->fast_ops = slave->fast_ops;
1020 pcm->fast_op_arg = slave->fast_op_arg;
1021 pcm->private_data = plug;
1022 pcm->poll_fd = slave->poll_fd;
1023 pcm->poll_events = slave->poll_events;
1024 snd_pcm_link_hw_ptr(pcm, slave);
1025 snd_pcm_link_appl_ptr(pcm, slave);
1031 /*! \page pcm_plugins
1033 \section pcm_plugins_plug Automatic conversion plugin
1035 This plugin converts channels, rate and format on request.
1039 type plug # Automatic conversion PCM
1040 slave STR # Slave name
1042 slave { # Slave definition
1043 pcm STR # Slave PCM name
1045 pcm { } # Slave PCM definition
1046 [format STR] # Slave format (default nearest) or "unchanged"
1047 [channels INT] # Slave channels (default nearest) or "unchanged"
1048 [rate INT] # Slave rate (default nearest) or "unchanged"
1050 route_policy STR # route policy for automatic ttable generation
1051 # STR can be 'default', 'average', 'copy', 'duplicate'
1052 # average: result is average of input channels
1053 # copy: only first channels are copied to destination
1054 # duplicate: duplicate first set of channels
1055 # default: copy policy, except for mono capture - sum
1056 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1058 SCHANNEL REAL # route value (0.0 - 1.0)
1064 \subsection pcm_plugins_plug_funcref Function reference
1067 <LI>snd_pcm_plug_open()
1068 <LI>_snd_pcm_plug_open()
1074 * \brief Creates a new Plug PCM
1075 * \param pcmp Returns created PCM handle
1076 * \param name Name of PCM
1077 * \param root Root configuration node
1078 * \param conf Configuration node with Plug PCM description
1079 * \param stream Stream type
1080 * \param mode Stream mode
1081 * \retval zero on success otherwise a negative error code
1082 * \warning Using of this function might be dangerous in the sense
1083 * of compatibility reasons. The prototype might be freely
1084 * changed in future.
1086 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1087 snd_config_t *root, snd_config_t *conf,
1088 snd_pcm_stream_t stream, int mode)
1090 snd_config_iterator_t i, next;
1093 snd_config_t *slave = NULL, *sconf;
1094 snd_config_t *tt = NULL;
1095 enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1096 snd_pcm_route_ttable_entry_t *ttable = NULL;
1097 unsigned int csize, ssize;
1098 unsigned int cused, sused;
1099 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1100 int schannels = -1, srate = -1;
1101 snd_config_for_each(i, next, conf) {
1102 snd_config_t *n = snd_config_iterator_entry(i);
1104 if (snd_config_get_id(n, &id) < 0)
1106 if (snd_pcm_conf_generic_id(id))
1108 if (strcmp(id, "slave") == 0) {
1112 if (strcmp(id, "ttable") == 0) {
1113 route_policy = PLUG_ROUTE_POLICY_NONE;
1114 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1115 SNDERR("Invalid type for %s", id);
1121 if (strcmp(id, "route_policy") == 0) {
1123 if ((err = snd_config_get_string(n, &str)) < 0) {
1124 SNDERR("Invalid type for %s", id);
1128 SNDERR("Table is defined, route policy is ignored");
1129 if (!strcmp(str, "default"))
1130 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1131 else if (!strcmp(str, "average"))
1132 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1133 else if (!strcmp(str, "copy"))
1134 route_policy = PLUG_ROUTE_POLICY_COPY;
1135 else if (!strcmp(str, "duplicate"))
1136 route_policy = PLUG_ROUTE_POLICY_DUP;
1139 SNDERR("Unknown field %s", id);
1143 SNDERR("slave is not defined");
1146 err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1147 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1148 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1149 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1153 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1155 snd_config_delete(sconf);
1158 ttable = malloc(csize * ssize * sizeof(*ttable));
1159 if (ttable == NULL) {
1160 snd_config_delete(sconf);
1163 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1165 snd_config_delete(sconf);
1170 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
1171 snd_config_delete(sconf);
1174 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate,
1175 route_policy, ttable, ssize, cused, sused, spcm, 1);
1177 snd_pcm_close(spcm);
1181 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);