Initial Import
[profile/ivi/alsa-lib.git] / src / pcm / pcm_plug.c
1 /*
2  * \file pcm/pcm_plug.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Route & Volume Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Plug
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11  *
12  *
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.
17  *
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.
22  *
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
26  *
27  */
28   
29 #include "pcm_local.h"
30 #include "pcm_plugin.h"
31
32 #ifndef PIC
33 /* entry for static linking */
34 const char *_snd_module_pcm_plug = "";
35 #endif
36
37 #ifndef DOC_HIDDEN
38
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,
45 };
46
47 typedef struct {
48         snd_pcm_generic_t gen;
49         snd_pcm_t *req_slave;
50         snd_pcm_format_t sformat;
51         int schannels;
52         int srate;
53         const snd_config_t *rate_converter;
54         enum snd_pcm_plug_route_policy route_policy;
55         snd_pcm_route_ttable_entry_t *ttable;
56         int ttable_ok, ttable_last;
57         unsigned int tt_ssize, tt_cused, tt_sused;
58 } snd_pcm_plug_t;
59
60 #endif
61
62 static int snd_pcm_plug_close(snd_pcm_t *pcm)
63 {
64         snd_pcm_plug_t *plug = pcm->private_data;
65         int err, result = 0;
66         free(plug->ttable);
67         assert(plug->gen.slave == plug->req_slave);
68         if (plug->gen.close_slave) {
69                 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
70                 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
71                 err = snd_pcm_close(plug->req_slave);
72                 if (err < 0)
73                         result = err;
74         }
75         free(plug);
76         return result;
77 }
78
79 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
80 {
81         snd_pcm_plug_t *plug = pcm->private_data;
82         snd_pcm_t *slave = plug->req_slave;
83         int err;
84         
85         if ((err = snd_pcm_info(slave, info)) < 0)
86                 return err;
87         return 0;
88 }
89
90 static const snd_pcm_format_t linear_preferred_formats[] = {
91 #ifdef SND_LITTLE_ENDIAN
92         SND_PCM_FORMAT_S16_LE,
93         SND_PCM_FORMAT_U16_LE,
94         SND_PCM_FORMAT_S16_BE,
95         SND_PCM_FORMAT_U16_BE,
96 #else
97         SND_PCM_FORMAT_S16_BE,
98         SND_PCM_FORMAT_U16_BE,
99         SND_PCM_FORMAT_S16_LE,
100         SND_PCM_FORMAT_U16_LE,
101 #endif
102 #ifdef SND_LITTLE_ENDIAN
103         SND_PCM_FORMAT_S32_LE,
104         SND_PCM_FORMAT_U32_LE,
105         SND_PCM_FORMAT_S32_BE,
106         SND_PCM_FORMAT_U32_BE,
107 #else
108         SND_PCM_FORMAT_S32_BE,
109         SND_PCM_FORMAT_U32_BE,
110         SND_PCM_FORMAT_S32_LE,
111         SND_PCM_FORMAT_U32_LE,
112 #endif
113         SND_PCM_FORMAT_S8,
114         SND_PCM_FORMAT_U8,
115 #ifdef SND_LITTLE_ENDIAN
116         SND_PCM_FORMAT_FLOAT_LE,
117         SND_PCM_FORMAT_FLOAT64_LE,
118         SND_PCM_FORMAT_FLOAT_BE,
119         SND_PCM_FORMAT_FLOAT64_BE,
120 #else
121         SND_PCM_FORMAT_FLOAT_BE,
122         SND_PCM_FORMAT_FLOAT64_BE,
123         SND_PCM_FORMAT_FLOAT_LE,
124         SND_PCM_FORMAT_FLOAT64_LE,
125 #endif
126 #ifdef SND_LITTLE_ENDIAN
127         SND_PCM_FORMAT_S24_LE,
128         SND_PCM_FORMAT_U24_LE,
129         SND_PCM_FORMAT_S24_BE,
130         SND_PCM_FORMAT_U24_BE,
131 #else
132         SND_PCM_FORMAT_S24_BE,
133         SND_PCM_FORMAT_U24_BE,
134         SND_PCM_FORMAT_S24_LE,
135         SND_PCM_FORMAT_U24_LE,
136 #endif
137 #ifdef SND_LITTLE_ENDIAN
138         SND_PCM_FORMAT_S24_3LE,
139         SND_PCM_FORMAT_U24_3LE,
140         SND_PCM_FORMAT_S24_3BE,
141         SND_PCM_FORMAT_U24_3BE,
142 #else
143         SND_PCM_FORMAT_S24_3BE,
144         SND_PCM_FORMAT_U24_3BE,
145         SND_PCM_FORMAT_S24_3LE,
146         SND_PCM_FORMAT_U24_3LE,
147 #endif
148 #ifdef SND_LITTLE_ENDIAN
149         SND_PCM_FORMAT_S20_3LE,
150         SND_PCM_FORMAT_U20_3LE,
151         SND_PCM_FORMAT_S20_3BE,
152         SND_PCM_FORMAT_U20_3BE,
153 #else
154         SND_PCM_FORMAT_S20_3BE,
155         SND_PCM_FORMAT_U20_3BE,
156         SND_PCM_FORMAT_S20_3LE,
157         SND_PCM_FORMAT_U20_3LE,
158 #endif
159 #ifdef SND_LITTLE_ENDIAN
160         SND_PCM_FORMAT_S18_3LE,
161         SND_PCM_FORMAT_U18_3LE,
162         SND_PCM_FORMAT_S18_3BE,
163         SND_PCM_FORMAT_U18_3BE,
164 #else
165         SND_PCM_FORMAT_S18_3BE,
166         SND_PCM_FORMAT_U18_3BE,
167         SND_PCM_FORMAT_S18_3LE,
168         SND_PCM_FORMAT_U18_3LE,
169 #endif
170 };
171
172 #if defined(BUILD_PCM_PLUGIN_MULAW) || \
173         defined(BUILD_PCM_PLUGIN_ALAW) || \
174         defined(BUILD_PCM_PLUGIN_ADPCM)
175 #define BUILD_PCM_NONLINEAR
176 #endif
177
178 #ifdef BUILD_PCM_NONLINEAR
179 static const snd_pcm_format_t nonlinear_preferred_formats[] = {
180 #ifdef BUILD_PCM_PLUGIN_MULAW
181         SND_PCM_FORMAT_MU_LAW,
182 #endif
183 #ifdef BUILD_PCM_PLUGIN_ALAW
184         SND_PCM_FORMAT_A_LAW,
185 #endif
186 #ifdef BUILD_PCM_PLUGIN_ADPCM
187         SND_PCM_FORMAT_IMA_ADPCM,
188 #endif
189 };
190 #endif
191
192 #ifdef BUILD_PCM_PLUGIN_LFLOAT
193 static const snd_pcm_format_t float_preferred_formats[] = {
194 #ifdef SND_LITTLE_ENDIAN
195         SND_PCM_FORMAT_FLOAT_LE,
196         SND_PCM_FORMAT_FLOAT64_LE,
197         SND_PCM_FORMAT_FLOAT_BE,
198         SND_PCM_FORMAT_FLOAT64_BE,
199 #else
200         SND_PCM_FORMAT_FLOAT_BE,
201         SND_PCM_FORMAT_FLOAT64_BE,
202         SND_PCM_FORMAT_FLOAT_LE,
203         SND_PCM_FORMAT_FLOAT64_LE,
204 #endif
205 };
206 #endif
207
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,
213 };
214
215 static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
216 {
217         int e, s;
218         if (! linear_format_widths[wid - 1])
219                 return SND_PCM_FORMAT_UNKNOWN;
220         for (e = 0; e < 2; e++) {
221                 for (s = 0; s < 2; s++) {
222                         int pw = ((wid + 7) / 8) * 8;
223                         for (; pw <= 32; pw += 8) {
224                                 snd_pcm_format_t f;
225                                 f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
226                                 if (f != SND_PCM_FORMAT_UNKNOWN &&
227                                     snd_pcm_format_mask_test(format_mask, f))
228                                         return f;
229                         }
230                         sgn = !sgn;
231                 }
232                 ed = !ed;
233         }
234         return SND_PCM_FORMAT_UNKNOWN;
235 }
236
237 static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
238 {
239         int w, w1, u, e;
240         snd_pcm_format_t f;
241         snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
242         snd_pcm_format_mask_t fl = {
243 #ifdef BUILD_PCM_PLUGIN_LFLOAT
244                 SND_PCM_FMTBIT_FLOAT
245 #else
246                 { 0 }
247 #endif
248         };
249         if (snd_pcm_format_mask_test(format_mask, format))
250                 return format;
251         if (!snd_pcm_format_mask_test(&lin, format) &&
252             !snd_pcm_format_mask_test(&fl, format)) {
253                 unsigned int i;
254                 switch (format) {
255 #ifdef BUILD_PCM_PLUGIN_MULAW
256                 case SND_PCM_FORMAT_MU_LAW:
257 #endif
258 #ifdef BUILD_PCM_PLUGIN_ALAW
259                 case SND_PCM_FORMAT_A_LAW:
260 #endif
261 #ifdef BUILD_PCM_PLUGIN_ADPCM
262                 case SND_PCM_FORMAT_IMA_ADPCM:
263 #endif
264                         for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
265                                 snd_pcm_format_t f = linear_preferred_formats[i];
266                                 if (snd_pcm_format_mask_test(format_mask, f))
267                                         return f;
268                         }
269                         /* Fall through */
270                 default:
271                         return SND_PCM_FORMAT_UNKNOWN;
272                 }
273
274         }
275         snd_mask_intersect(&lin, format_mask);
276         snd_mask_intersect(&fl, format_mask);
277         if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
278 #ifdef BUILD_PCM_NONLINEAR
279                 unsigned int i;
280                 for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
281                         snd_pcm_format_t f = nonlinear_preferred_formats[i];
282                         if (snd_pcm_format_mask_test(format_mask, f))
283                                 return f;
284                 }
285 #endif
286                 return SND_PCM_FORMAT_UNKNOWN;
287         }
288 #ifdef BUILD_PCM_PLUGIN_LFLOAT
289         if (snd_pcm_format_float(format)) {
290                 if (snd_pcm_format_mask_test(&fl, format)) {
291                         unsigned int i;
292                         for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
293                                 snd_pcm_format_t f = float_preferred_formats[i];
294                                 if (snd_pcm_format_mask_test(format_mask, f))
295                                         return f;
296                         }
297                 }
298                 w = 32;
299                 u = 0;
300                 e = snd_pcm_format_big_endian(format);
301         } else
302 #endif
303         if (snd_mask_empty(&lin)) {
304 #ifdef BUILD_PCM_PLUGIN_LFLOAT
305                 unsigned int i;
306                 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
307                         snd_pcm_format_t f = float_preferred_formats[i];
308                         if (snd_pcm_format_mask_test(format_mask, f))
309                                 return f;
310                 }
311 #endif
312                 return SND_PCM_FORMAT_UNKNOWN;
313         } else {
314                 w = snd_pcm_format_width(format);
315                 u = snd_pcm_format_unsigned(format);
316                 e = snd_pcm_format_big_endian(format);
317         }
318         for (w1 = w; w1 <= 32; w1++) {
319                 f = check_linear_format(format_mask, w1, u, e);
320                 if (f != SND_PCM_FORMAT_UNKNOWN)
321                         return f;
322         }
323         for (w1 = w - 1; w1 > 0; w1--) {
324                 f = check_linear_format(format_mask, w1, u, e);
325                 if (f != SND_PCM_FORMAT_UNKNOWN)
326                         return f;
327         }
328         return SND_PCM_FORMAT_UNKNOWN;
329 }
330
331 static void snd_pcm_plug_clear(snd_pcm_t *pcm)
332 {
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;
343         }
344 }
345
346 #ifndef DOC_HIDDEN
347 typedef struct {
348         snd_pcm_access_t access;
349         snd_pcm_format_t format;
350         unsigned int channels;
351         unsigned int rate;
352 } snd_pcm_plug_params_t;
353 #endif
354
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)
357 {
358         snd_pcm_plug_t *plug = pcm->private_data;
359         int err;
360         if (clt->rate == slv->rate)
361                 return 0;
362         assert(snd_pcm_format_linear(slv->format));
363         err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
364                                 plug->gen.slave, plug->gen.slave != plug->req_slave);
365         if (err < 0)
366                 return err;
367         slv->access = clt->access;
368         slv->rate = clt->rate;
369         if (snd_pcm_format_linear(clt->format))
370                 slv->format = clt->format;
371         return 1;
372 }
373 #endif
374
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)
377 {
378         snd_pcm_plug_t *plug = pcm->private_data;
379         unsigned int tt_ssize, tt_cused, tt_sused;
380         snd_pcm_route_ttable_entry_t *ttable;
381         int err;
382         if (clt->channels == slv->channels &&
383             (!plug->ttable || !plug->ttable_last))
384                 return 0;
385         if (clt->rate != slv->rate &&
386             clt->channels > slv->channels)
387                 return 0;
388         assert(snd_pcm_format_linear(slv->format));
389         tt_ssize = slv->channels;
390         tt_cused = clt->channels;
391         tt_sused = slv->channels;
392         ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
393         if (plug->ttable) {     /* expand or shrink table */
394                 unsigned int c = 0, s = 0;
395                 for (c = 0; c < tt_cused; c++) {
396                         for (s = 0; s < tt_sused; s++) {
397                                 snd_pcm_route_ttable_entry_t v;
398                                 if (c >= plug->tt_cused)
399                                         v = 0;
400                                 else if (s >= plug->tt_sused)
401                                         v = 0;
402                                 else
403                                         v = plug->ttable[c * plug->tt_ssize + s];
404                                 ttable[c * tt_ssize + s] = v;
405                         }
406                 }
407                 plug->ttable_ok = 1;
408         } else {
409                 unsigned int k;
410                 unsigned int c = 0, s = 0;
411                 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
412                 int n;
413                 for (k = 0; k < tt_cused * tt_sused; ++k)
414                         ttable[k] = 0;
415                 if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
416                         rpolicy = PLUG_ROUTE_POLICY_COPY;
417                         /* it's hack for mono conversion */
418                         if (clt->channels == 1 || slv->channels == 1)
419                                 rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
420                 }
421                 switch (rpolicy) {
422                 case PLUG_ROUTE_POLICY_AVERAGE:
423                 case PLUG_ROUTE_POLICY_DUP:
424                         if (clt->channels > slv->channels) {
425                                 n = clt->channels;
426                         } else {
427                                 n = slv->channels;
428                         }
429                         while (n-- > 0) {
430                                 snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
431                                 if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
432                                         if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
433                                             clt->channels > slv->channels) {
434                                                 int srcs = clt->channels / slv->channels;
435                                                 if (s < clt->channels % slv->channels)
436                                                         srcs++;
437                                                 v /= srcs;
438                                         } else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
439                                                    slv->channels > clt->channels) {
440                                                         int srcs = slv->channels / clt->channels;
441                                                 if (s < slv->channels % clt->channels)
442                                                         srcs++;
443                                                 v /= srcs;
444                                         }
445                                 }
446                                 ttable[c * tt_ssize + s] = v;
447                                 if (++c == clt->channels)
448                                         c = 0;
449                                 if (++s == slv->channels)
450                                         s = 0;
451                         }
452                         break;
453                 case PLUG_ROUTE_POLICY_COPY:
454                         if (clt->channels < slv->channels) {
455                                 n = clt->channels;
456                         } else {
457                                 n = slv->channels;
458                         }
459                         for (c = 0; (int)c < n; c++)
460                                 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
461                         break;
462                 default:
463                         SNDERR("Invalid route policy");
464                         break;
465                 }
466         }
467         err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->gen.slave, plug->gen.slave != plug->req_slave);
468         if (err < 0)
469                 return err;
470         slv->channels = clt->channels;
471         slv->access = clt->access;
472         if (snd_pcm_format_linear(clt->format))
473                 slv->format = clt->format;
474         return 1;
475 }
476 #endif
477
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)
479 {
480         snd_pcm_plug_t *plug = pcm->private_data;
481         int err;
482         snd_pcm_format_t cfmt;
483         int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
484
485         /* No conversion is needed */
486         if (clt->format == slv->format &&
487             clt->rate == slv->rate &&
488             clt->channels == slv->channels)
489                 return 0;
490
491         if (snd_pcm_format_linear(slv->format)) {
492                 /* Conversion is done in another plugin */
493                 if (clt->rate != slv->rate ||
494                     clt->channels != slv->channels)
495                         return 0;
496                 cfmt = clt->format;
497                 switch (clt->format) {
498 #ifdef BUILD_PCM_PLUGIN_MULAW
499                 case SND_PCM_FORMAT_MU_LAW:
500                         f = snd_pcm_mulaw_open;
501                         break;
502 #endif
503 #ifdef BUILD_PCM_PLUGIN_ALAW
504                 case SND_PCM_FORMAT_A_LAW:
505                         f = snd_pcm_alaw_open;
506                         break;
507 #endif
508 #ifdef BUILD_PCM_PLUGIN_ADPCM
509                 case SND_PCM_FORMAT_IMA_ADPCM:
510                         f = snd_pcm_adpcm_open;
511                         break;
512 #endif
513                 default:
514 #ifdef BUILD_PCM_PLUGIN_LFLOAT
515                         if (snd_pcm_format_float(clt->format))
516                                 f = snd_pcm_lfloat_open;
517
518                         else
519 #endif
520                                 f = snd_pcm_linear_open;
521                         break;
522                 }
523 #ifdef BUILD_PCM_PLUGIN_LFLOAT
524         } else if (snd_pcm_format_float(slv->format)) {
525                 /* Conversion is done in another plugin */
526                 if (clt->format == slv->format &&
527                     clt->rate == slv->rate &&
528                     clt->channels == slv->channels)
529                         return 0;
530                 cfmt = clt->format;
531                 if (snd_pcm_format_linear(clt->format))
532                         f = snd_pcm_lfloat_open;
533                 else
534                         return -EINVAL;
535 #endif
536 #ifdef BUILD_PCM_NONLINEAR
537         } else {
538                 switch (slv->format) {
539 #ifdef BUILD_PCM_PLUGIN_MULAW
540                 case SND_PCM_FORMAT_MU_LAW:
541                         f = snd_pcm_mulaw_open;
542                         break;
543 #endif
544 #ifdef BUILD_PCM_PLUGIN_ALAW
545                 case SND_PCM_FORMAT_A_LAW:
546                         f = snd_pcm_alaw_open;
547                         break;
548 #endif
549 #ifdef BUILD_PCM_PLUGIN_ADPCM
550                 case SND_PCM_FORMAT_IMA_ADPCM:
551                         f = snd_pcm_adpcm_open;
552                         break;
553 #endif
554                 default:
555                         return -EINVAL;
556                 }
557                 if (snd_pcm_format_linear(clt->format))
558                         cfmt = clt->format;
559                 else
560                         cfmt = SND_PCM_FORMAT_S16;
561 #endif /* NONLINEAR */
562         }
563         err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
564         if (err < 0)
565                 return err;
566         slv->format = cfmt;
567         slv->access = clt->access;
568         return 1;
569 }
570
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)
572 {
573         snd_pcm_plug_t *plug = pcm->private_data;
574         int err;
575         if (clt->access == slv->access)
576                 return 0;
577         err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
578         if (err < 0)
579                 return err;
580         slv->access = clt->access;
581         return 1;
582 }
583
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)
588 {
589         snd_pcm_plug_t *plug = pcm->private_data;
590         int err;
591
592         if (clt->access == slv->access)
593                 return 0;
594
595         switch (slv->access) {
596         case SND_PCM_ACCESS_MMAP_INTERLEAVED:
597         case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
598         case SND_PCM_ACCESS_MMAP_COMPLEX:
599                 return 0;
600         default:
601                 break;
602         }
603
604         err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
605                                        plug->gen.slave != plug->req_slave);
606         if (err < 0)
607                 return err;
608         switch (slv->access) {
609         case SND_PCM_ACCESS_RW_INTERLEAVED:
610                 slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
611                 break;
612         case SND_PCM_ACCESS_RW_NONINTERLEAVED:
613                 slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
614                 break;
615         default:
616                 break;
617         }
618         return 1;
619 }
620 #endif
621
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)
625 {
626         snd_pcm_plug_t *plug = pcm->private_data;
627         static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
628 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
629                 snd_pcm_plug_change_mmap,
630 #endif
631                 snd_pcm_plug_change_format,
632 #ifdef BUILD_PCM_PLUGIN_ROUTE
633                 snd_pcm_plug_change_channels,
634 #endif
635 #ifdef BUILD_PCM_PLUGIN_RATE
636                 snd_pcm_plug_change_rate,
637 #endif
638 #ifdef BUILD_PCM_PLUGIN_ROUTE
639                 snd_pcm_plug_change_channels,
640 #endif
641                 snd_pcm_plug_change_format,
642                 snd_pcm_plug_change_access
643         };
644         snd_pcm_plug_params_t p = *slave;
645         unsigned int k = 0;
646         plug->ttable_ok = plug->ttable_last = 0;
647         while (client->format != p.format ||
648                client->channels != p.channels ||
649                client->rate != p.rate ||
650                client->access != p.access) {
651                 snd_pcm_t *new;
652                 int err;
653                 if (k >= sizeof(funcs)/sizeof(*funcs))
654                         return -EINVAL;
655                 err = funcs[k](pcm, &new, client, &p);
656                 if (err < 0) {
657                         snd_pcm_plug_clear(pcm);
658                         return err;
659                 }
660                 if (err) {
661                         plug->gen.slave = new;
662                         pcm->fast_ops = new->fast_ops;
663                         pcm->fast_op_arg = new->fast_op_arg;
664                 }
665                 k++;
666         }
667 #ifdef BUILD_PCM_PLUGIN_ROUTE
668         /* it's exception, user specified ttable, but no reduction/expand */
669         if (plug->ttable && !plug->ttable_ok) {
670                 snd_pcm_t *new;
671                 int err;
672                 plug->ttable_last = 1;
673                 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
674                 if (err < 0) {
675                         snd_pcm_plug_clear(pcm);
676                         return err;
677                 }
678                 assert(err);
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;
683         }
684 #endif
685         return 0;
686 }
687
688 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
689 {
690         unsigned int rate_min, channels_max;
691         int err;
692
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);
695         if (err < 0)
696                 return err;
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))
700                         return -EINVAL;
701         }
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);
704         if (err < 0)
705                 return err;
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))
709                         return -EINVAL;
710         }
711         return 0;
712 }
713
714 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
715 {
716         snd_pcm_plug_t *plug = pcm->private_data;
717         int err;
718         
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);
723         }
724         if (plug->schannels > 0)
725                 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
726                                       plug->schannels, 0);
727         if (plug->srate > 0)
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);
732         if (err < 0)
733                 return err;
734         return 0;
735 }
736
737 static int check_access_change(snd_pcm_hw_params_t *cparams,
738                                snd_pcm_hw_params_t *sparams)
739 {
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;
744 #endif
745
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 */
755
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! */
759
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);
769         }
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);
775         }
776         if (!snd_mask_empty(&mask))
777                 *smask = mask; /* prefer the straight conversion */
778         return 0;
779 #else
780         return -EINVAL;
781 #endif
782 }
783
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)
786 {
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;
793         int err;
794         snd_pcm_format_t format;
795         snd_interval_t t, buffer_size;
796         const snd_interval_t *srate, *crate;
797
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;
802         else {
803                 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
804                 if (err < 0)
805                         return err;
806         }
807         
808         if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
809                 links |= SND_PCM_HW_PARBIT_CHANNELS;
810         else {
811                 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
812                 if (err < 0)
813                         return err;
814         }
815         if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
816                 links |= SND_PCM_HW_PARBIT_FORMAT;
817         else {
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++) {
822                         snd_pcm_format_t f;
823                         if (!snd_pcm_format_mask_test(format_mask, format))
824                                 continue;
825                         if (snd_pcm_format_mask_test(sformat_mask, format))
826                                 f = format;
827                         else {
828                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
829                                 if (f == SND_PCM_FORMAT_UNKNOWN)
830                                         continue;
831                         }
832                         snd_pcm_format_mask_set(&sfmt_mask, f);
833                 }
834
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))
839                                         continue;
840                                 SNDERR("Format: %s", snd_pcm_format_name(format));
841                         }
842                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
843                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
844                                         continue;
845                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
846                         }
847                         return -EINVAL;
848                 }
849                 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
850                                                 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
851                 if (err < 0)
852                         return -EINVAL;
853         }
854
855         if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
856                 err = check_access_change(params, sparams);
857                 if (err < 0) {
858                         SNDERR("Unable to find an usable access for '%s'",
859                                pcm->name);
860                         return err;
861                 }
862         }
863
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);
868         else {
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);
875                 if (err < 0)
876                         return err;
877         }
878         err = _snd_pcm_hw_params_refine(sparams, links, params);
879         if (err < 0)
880                 return err;
881         return 0;
882 }
883         
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)
887 {
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;
893         int err;
894         snd_pcm_format_t format;
895         snd_interval_t t;
896         const snd_interval_t *sbuffer_size;
897         const snd_interval_t *srate, *crate;
898
899         if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
900                 links |= SND_PCM_HW_PARBIT_CHANNELS;
901
902         if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
903                 links |= SND_PCM_HW_PARBIT_FORMAT;
904         else {
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++) {
911                         snd_pcm_format_t f;
912                         if (!snd_pcm_format_mask_test(format_mask, format))
913                                 continue;
914                         if (snd_pcm_format_mask_test(sformat_mask, format))
915                                 f = format;
916                         else {
917                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
918                                 if (f == SND_PCM_FORMAT_UNKNOWN)
919                                         continue;
920                         }
921                         snd_pcm_format_mask_set(&fmt_mask, format);
922                 }
923
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))
928                                         continue;
929                                 SNDERR("Format: %s", snd_pcm_format_name(format));
930                         }
931                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
932                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
933                                         continue;
934                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
935                         }
936                         return -EINVAL;
937                 }
938                 
939                 err = _snd_pcm_hw_param_set_mask(params, 
940                                                  SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
941                 if (err < 0)
942                         return err;
943         }
944
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;
949         else {
950                 unsigned int rate_min, srate_min;
951                 int rate_mindir, srate_mindir;
952                 
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);
955                 if (err < 0)
956                         return err;
957                 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
958                 if (err < 0)
959                         return err;
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);
962                         if (err < 0)
963                                 return err;
964                 }
965         }
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);
970         else {
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))
977                         return -EINVAL;
978                 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
979                 if (err < 0)
980                         return err;
981         }
982         err = _snd_pcm_hw_params_refine(params, links, sparams);
983         if (err < 0)
984                 return err;
985         /* FIXME */
986         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
987         return 0;
988 }
989
990 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
991 {
992         snd_pcm_plug_t *plug = pcm->private_data;
993         return snd_pcm_hw_refine(plug->req_slave, params);
994 }
995
996 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
997 {
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);
1004 }
1005
1006 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1007 {
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;
1012         int err;
1013
1014         err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
1015         if (err < 0)
1016                 return err;
1017         err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1018         if (err < 0)
1019                 return err;
1020         err = snd_pcm_hw_refine_soft(slave, &sparams);
1021         if (err < 0)
1022                 return err;
1023
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);
1028
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 &&
1036               !plug->ttable &&
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);
1041                 if (err < 0)
1042                         return err;
1043         }
1044         slave = plug->gen.slave;
1045         err = _snd_pcm_hw_params(slave, params);
1046         if (err < 0) {
1047                 snd_pcm_plug_clear(pcm);
1048                 return err;
1049         }
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);
1054         return 0;
1055 }
1056
1057 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1058 {
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);
1063         return err;
1064 }
1065
1066 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1067 {
1068         snd_pcm_plug_t *plug = pcm->private_data;
1069         snd_output_printf(out, "Plug PCM: ");
1070         snd_pcm_dump(plug->gen.slave, out);
1071 }
1072
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,
1086 };
1087
1088 /**
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.
1099  */
1100 int snd_pcm_plug_open(snd_pcm_t **pcmp,
1101                       const char *name,
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)
1109 {
1110         snd_pcm_t *pcm;
1111         snd_pcm_plug_t *plug;
1112         int err;
1113         assert(pcmp && slave);
1114
1115         plug = calloc(1, sizeof(snd_pcm_plug_t));
1116         if (!plug)
1117                 return -ENOMEM;
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;
1129         
1130         err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1131         if (err < 0) {
1132                 free(plug);
1133                 return err;
1134         }
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);
1145         *pcmp = pcm;
1146
1147         return 0;
1148 }
1149
1150 /*! \page pcm_plugins
1151
1152 \section pcm_plugins_plug Automatic conversion plugin
1153
1154 This plugin converts channels, rate and format on request.
1155
1156 \code
1157 pcm.name {
1158         type plug               # Automatic conversion PCM
1159         slave STR               # Slave name
1160         # or
1161         slave {                 # Slave definition
1162                 pcm STR         # Slave PCM name
1163                 # or
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"
1168         }
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)
1176                 CCHANNEL {
1177                         SCHANNEL REAL   # route value (0.0 - 1.0)
1178                 }
1179         }
1180         rate_converter STR      # type of rate converter
1181         # or
1182         rate_converter [ STR1 STR2 ... ]
1183                                 # type of rate converter
1184                                 # default value is taken from defaults.pcm.rate_converter
1185 }
1186 \endcode
1187
1188 \subsection pcm_plugins_plug_funcref Function reference
1189
1190 <UL>
1191   <LI>snd_pcm_plug_open()
1192   <LI>_snd_pcm_plug_open()
1193 </UL>
1194
1195 */
1196
1197 /**
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.
1209  */
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)
1213 {
1214         snd_config_iterator_t i, next;
1215         int err;
1216         snd_pcm_t *spcm;
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;
1226
1227         snd_config_for_each(i, next, conf) {
1228                 snd_config_t *n = snd_config_iterator_entry(i);
1229                 const char *id;
1230                 if (snd_config_get_id(n, &id) < 0)
1231                         continue;
1232                 if (snd_pcm_conf_generic_id(id))
1233                         continue;
1234                 if (strcmp(id, "slave") == 0) {
1235                         slave = n;
1236                         continue;
1237                 }
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);
1243                                 return -EINVAL;
1244                         }
1245                         tt = n;
1246                         continue;
1247                 }
1248                 if (strcmp(id, "route_policy") == 0) {
1249                         const char *str;
1250                         if ((err = snd_config_get_string(n, &str)) < 0) {
1251                                 SNDERR("Invalid type for %s", id);
1252                                 return -EINVAL;
1253                         }
1254                         if (tt != NULL)
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;
1264                         continue;
1265                 }
1266 #endif
1267 #ifdef BUILD_PCM_PLUGIN_RATE
1268                 if (strcmp(id, "rate_converter") == 0) {
1269                         rate_converter = n;
1270                         continue;
1271                 }
1272 #endif
1273                 SNDERR("Unknown field %s", id);
1274                 return -EINVAL;
1275         }
1276         if (!slave) {
1277                 SNDERR("slave is not defined");
1278                 return -EINVAL;
1279         }
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);
1284         if (err < 0)
1285                 return err;
1286 #ifdef BUILD_PCM_PLUGIN_ROUTE
1287         if (tt) {
1288                 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1289                 if (err < 0) {
1290                         snd_config_delete(sconf);
1291                         return err;
1292                 }
1293                 ttable = malloc(csize * ssize * sizeof(*ttable));
1294                 if (ttable == NULL) {
1295                         snd_config_delete(sconf);
1296                         return err;
1297                 }
1298                 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1299                 if (err < 0) {
1300                         snd_config_delete(sconf);
1301                         return err;
1302                 }
1303         }
1304 #endif
1305         
1306 #ifdef BUILD_PCM_PLUGIN_RATE
1307         if (! rate_converter)
1308                 rate_converter = snd_pcm_rate_get_default_converter(root);
1309 #endif
1310
1311         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1312         snd_config_delete(sconf);
1313         if (err < 0)
1314                 return err;
1315         err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1316                                 route_policy, ttable, ssize, cused, sused, spcm, 1);
1317         if (err < 0)
1318                 snd_pcm_close(spcm);
1319         return err;
1320 }
1321 #ifndef DOC_HIDDEN
1322 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1323 #endif