OSDN Git Service

Update to version 1.0.19
[android-x86/external-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 == clt->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         slv->access = clt->access;
609         return 1;
610 }
611 #endif
612
613 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
614                                        snd_pcm_plug_params_t *client,
615                                        snd_pcm_plug_params_t *slave)
616 {
617         snd_pcm_plug_t *plug = pcm->private_data;
618         static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
619 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
620                 snd_pcm_plug_change_mmap,
621 #endif
622                 snd_pcm_plug_change_format,
623 #ifdef BUILD_PCM_PLUGIN_ROUTE
624                 snd_pcm_plug_change_channels,
625 #endif
626 #ifdef BUILD_PCM_PLUGIN_RATE
627                 snd_pcm_plug_change_rate,
628 #endif
629 #ifdef BUILD_PCM_PLUGIN_ROUTE
630                 snd_pcm_plug_change_channels,
631 #endif
632                 snd_pcm_plug_change_format,
633                 snd_pcm_plug_change_access
634         };
635         snd_pcm_plug_params_t p = *slave;
636         unsigned int k = 0;
637         plug->ttable_ok = plug->ttable_last = 0;
638         while (client->format != p.format ||
639                client->channels != p.channels ||
640                client->rate != p.rate ||
641                client->access != p.access) {
642                 snd_pcm_t *new;
643                 int err;
644                 if (k >= sizeof(funcs)/sizeof(*funcs))
645                         return -EINVAL;
646                 err = funcs[k](pcm, &new, client, &p);
647                 if (err < 0) {
648                         snd_pcm_plug_clear(pcm);
649                         return err;
650                 }
651                 if (err) {
652                         plug->gen.slave = new;
653                         pcm->fast_ops = new->fast_ops;
654                         pcm->fast_op_arg = new->fast_op_arg;
655                 }
656                 k++;
657         }
658 #ifdef BUILD_PCM_PLUGIN_ROUTE
659         /* it's exception, user specified ttable, but no reduction/expand */
660         if (plug->ttable && !plug->ttable_ok) {
661                 snd_pcm_t *new;
662                 int err;
663                 plug->ttable_last = 1;
664                 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
665                 if (err < 0) {
666                         snd_pcm_plug_clear(pcm);
667                         return err;
668                 }
669                 assert(err);
670                 assert(plug->ttable_ok);
671                 plug->gen.slave = new;
672                 pcm->fast_ops = new->fast_ops;
673                 pcm->fast_op_arg = new->fast_op_arg;
674         }
675 #endif
676         return 0;
677 }
678
679 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
680 {
681         unsigned int rate_min, channels_max;
682         int err;
683
684         /* HACK: to avoid overflow in PARTBIT_RATE code */
685         err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
686         if (err < 0)
687                 return err;
688         if (rate_min < 4000) {
689                 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
690                 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
691                         return -EINVAL;
692         }
693         /* HACK: to avoid overflow in PERIOD_SIZE code */
694         err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
695         if (err < 0)
696                 return err;
697         if (channels_max > 10000) {
698                 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
699                 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
700                         return -EINVAL;
701         }
702         return 0;
703 }
704
705 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
706 {
707         snd_pcm_plug_t *plug = pcm->private_data;
708         int err;
709         
710         _snd_pcm_hw_params_any(sparams);
711         if (plug->sformat >= 0) {
712                 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
713                 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
714         }
715         if (plug->schannels > 0)
716                 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
717                                       plug->schannels, 0);
718         if (plug->srate > 0)
719                 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
720                                               plug->srate, 0, plug->srate + 1, -1);
721         /* reduce the available configurations */
722         err = snd_pcm_hw_refine(plug->req_slave, sparams);
723         if (err < 0)
724                 return err;
725         return 0;
726 }
727
728 static int check_access_change(snd_pcm_hw_params_t *cparams,
729                                snd_pcm_hw_params_t *sparams)
730 {
731         snd_pcm_access_mask_t *smask;
732 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
733         const snd_pcm_access_mask_t *cmask;
734         snd_pcm_access_mask_t mask;
735 #endif
736
737         smask = (snd_pcm_access_mask_t *)
738                 snd_pcm_hw_param_get_mask(sparams,
739                                           SND_PCM_HW_PARAM_ACCESS);
740         if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
741             snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
742             snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
743                 return 0; /* OK, we have mmap support */
744 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
745         /* no mmap support - we need mmap emulation */
746         cmask = (const snd_pcm_access_mask_t *)
747                 snd_pcm_hw_param_get_mask(cparams,
748                                           SND_PCM_HW_PARAM_ACCESS);
749         snd_mask_none(&mask);
750         if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
751             snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
752                 snd_pcm_access_mask_set(&mask,
753                                         SND_PCM_ACCESS_RW_INTERLEAVED);
754         if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
755             snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
756                 snd_pcm_access_mask_set(&mask,
757                                         SND_PCM_ACCESS_RW_NONINTERLEAVED);
758         *smask = mask;
759         return 0;
760 #else
761         return -EINVAL;
762 #endif
763 }
764
765 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
766                                           snd_pcm_hw_params_t *sparams)
767 {
768         snd_pcm_plug_t *plug = pcm->private_data;
769         snd_pcm_t *slave = plug->req_slave;
770         unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
771                               SND_PCM_HW_PARBIT_TICK_TIME);
772         const snd_pcm_format_mask_t *format_mask, *sformat_mask;
773         snd_pcm_format_mask_t sfmt_mask;
774         int err;
775         snd_pcm_format_t format;
776         snd_interval_t t, buffer_size;
777         const snd_interval_t *srate, *crate;
778
779         if (plug->srate == -2 ||
780             (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
781             (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
782                 links |= SND_PCM_HW_PARBIT_RATE;
783         else {
784                 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
785                 if (err < 0)
786                         return err;
787         }
788         
789         if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
790                 links |= SND_PCM_HW_PARBIT_CHANNELS;
791         else {
792                 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
793                 if (err < 0)
794                         return err;
795         }
796         if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
797                 links |= SND_PCM_HW_PARBIT_FORMAT;
798         else {
799                 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
800                 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
801                 snd_mask_none(&sfmt_mask);
802                 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
803                         snd_pcm_format_t f;
804                         if (!snd_pcm_format_mask_test(format_mask, format))
805                                 continue;
806                         if (snd_pcm_format_mask_test(sformat_mask, format))
807                                 f = format;
808                         else {
809                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
810                                 if (f == SND_PCM_FORMAT_UNKNOWN)
811                                         continue;
812                         }
813                         snd_pcm_format_mask_set(&sfmt_mask, f);
814                 }
815
816                 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
817                         SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
818                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
819                                 if (!snd_pcm_format_mask_test(format_mask, format))
820                                         continue;
821                                 SNDERR("Format: %s", snd_pcm_format_name(format));
822                         }
823                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
824                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
825                                         continue;
826                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
827                         }
828                         return -EINVAL;
829                 }
830                 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
831                                                 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
832                 if (err < 0)
833                         return -EINVAL;
834         }
835
836         if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
837                 err = check_access_change(params, sparams);
838                 if (err < 0) {
839                         SNDERR("Unable to find an usable access for '%s'",
840                                pcm->name);
841                         return err;
842                 }
843         }
844
845         if ((links & SND_PCM_HW_PARBIT_RATE) ||
846             snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
847                 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
848                           SND_PCM_HW_PARBIT_BUFFER_SIZE);
849         else {
850                 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
851                 snd_interval_unfloor(&buffer_size);
852                 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
853                 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
854                 snd_interval_muldiv(&buffer_size, srate, crate, &t);
855                 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
856                 if (err < 0)
857                         return err;
858         }
859         err = _snd_pcm_hw_params_refine(sparams, links, params);
860         if (err < 0)
861                 return err;
862         return 0;
863 }
864         
865 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
866                                           snd_pcm_hw_params_t *params,
867                                           snd_pcm_hw_params_t *sparams)
868 {
869         snd_pcm_plug_t *plug = pcm->private_data;
870         unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
871                               SND_PCM_HW_PARBIT_TICK_TIME);
872         const snd_pcm_format_mask_t *format_mask, *sformat_mask;
873         snd_pcm_format_mask_t fmt_mask;
874         int err;
875         snd_pcm_format_t format;
876         snd_interval_t t;
877         const snd_interval_t *sbuffer_size;
878         const snd_interval_t *srate, *crate;
879
880         if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
881                 links |= SND_PCM_HW_PARBIT_CHANNELS;
882
883         if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
884                 links |= SND_PCM_HW_PARBIT_FORMAT;
885         else {
886                 format_mask = snd_pcm_hw_param_get_mask(params,
887                                                         SND_PCM_HW_PARAM_FORMAT);
888                 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
889                                                          SND_PCM_HW_PARAM_FORMAT);
890                 snd_mask_none(&fmt_mask);
891                 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
892                         snd_pcm_format_t f;
893                         if (!snd_pcm_format_mask_test(format_mask, format))
894                                 continue;
895                         if (snd_pcm_format_mask_test(sformat_mask, format))
896                                 f = format;
897                         else {
898                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
899                                 if (f == SND_PCM_FORMAT_UNKNOWN)
900                                         continue;
901                         }
902                         snd_pcm_format_mask_set(&fmt_mask, format);
903                 }
904
905                 if (snd_pcm_format_mask_empty(&fmt_mask)) {
906                         SNDERR("Unable to find an usable client format");
907                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
908                                 if (!snd_pcm_format_mask_test(format_mask, format))
909                                         continue;
910                                 SNDERR("Format: %s", snd_pcm_format_name(format));
911                         }
912                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
913                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
914                                         continue;
915                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
916                         }
917                         return -EINVAL;
918                 }
919                 
920                 err = _snd_pcm_hw_param_set_mask(params, 
921                                                  SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
922                 if (err < 0)
923                         return err;
924         }
925
926         if (plug->srate == -2 ||
927             (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
928             (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
929                 links |= SND_PCM_HW_PARBIT_RATE;
930         else {
931                 unsigned int rate_min, srate_min;
932                 int rate_mindir, srate_mindir;
933                 
934                 /* This is a temporary hack, waiting for a better solution */
935                 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
936                 if (err < 0)
937                         return err;
938                 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
939                 if (err < 0)
940                         return err;
941                 if (rate_min == srate_min && srate_mindir > rate_mindir) {
942                         err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
943                         if (err < 0)
944                                 return err;
945                 }
946         }
947         if ((links & SND_PCM_HW_PARBIT_RATE) ||
948             snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
949                 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
950                           SND_PCM_HW_PARBIT_BUFFER_SIZE);
951         else {
952                 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
953                 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
954                 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
955                 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
956                 snd_interval_floor(&t);
957                 if (snd_interval_empty(&t))
958                         return -EINVAL;
959                 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
960                 if (err < 0)
961                         return err;
962         }
963         err = _snd_pcm_hw_params_refine(params, links, sparams);
964         if (err < 0)
965                 return err;
966         /* FIXME */
967         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
968         return 0;
969 }
970
971 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
972 {
973         snd_pcm_plug_t *plug = pcm->private_data;
974         return snd_pcm_hw_refine(plug->req_slave, params);
975 }
976
977 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
978 {
979         return snd_pcm_hw_refine_slave(pcm, params,
980                                        snd_pcm_plug_hw_refine_cprepare,
981                                        snd_pcm_plug_hw_refine_cchange,
982                                        snd_pcm_plug_hw_refine_sprepare,
983                                        snd_pcm_plug_hw_refine_schange,
984                                        snd_pcm_plug_hw_refine_slave);
985 }
986
987 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
988 {
989         snd_pcm_plug_t *plug = pcm->private_data;
990         snd_pcm_t *slave = plug->req_slave;
991         snd_pcm_plug_params_t clt_params, slv_params;
992         snd_pcm_hw_params_t sparams;
993         int err;
994
995         err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
996         if (err < 0)
997                 return err;
998         err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
999         if (err < 0)
1000                 return err;
1001         err = snd_pcm_hw_refine_soft(slave, &sparams);
1002         if (err < 0)
1003                 return err;
1004
1005         INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1006         INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1007         INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1008         INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1009
1010         INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1011         INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1012         INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1013         snd_pcm_plug_clear(pcm);
1014         if (!(clt_params.format == slv_params.format &&
1015               clt_params.channels == slv_params.channels &&
1016               clt_params.rate == slv_params.rate &&
1017               !plug->ttable &&
1018               snd_pcm_hw_params_test_access(slave, &sparams,
1019                                             clt_params.access) >= 0)) {
1020                 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1021                 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1022                 if (err < 0)
1023                         return err;
1024         }
1025         slave = plug->gen.slave;
1026         err = _snd_pcm_hw_params(slave, params);
1027         if (err < 0) {
1028                 snd_pcm_plug_clear(pcm);
1029                 return err;
1030         }
1031         snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1032         snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1033         snd_pcm_link_hw_ptr(pcm, slave);
1034         snd_pcm_link_appl_ptr(pcm, slave);
1035         return 0;
1036 }
1037
1038 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1039 {
1040         snd_pcm_plug_t *plug = pcm->private_data;
1041         snd_pcm_t *slave = plug->gen.slave;
1042         int err = snd_pcm_hw_free(slave);
1043         snd_pcm_plug_clear(pcm);
1044         return err;
1045 }
1046
1047 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1048 {
1049         snd_pcm_plug_t *plug = pcm->private_data;
1050         snd_output_printf(out, "Plug PCM: ");
1051         snd_pcm_dump(plug->gen.slave, out);
1052 }
1053
1054 static const snd_pcm_ops_t snd_pcm_plug_ops = {
1055         .close = snd_pcm_plug_close,
1056         .info = snd_pcm_plug_info,
1057         .hw_refine = snd_pcm_plug_hw_refine,
1058         .hw_params = snd_pcm_plug_hw_params,
1059         .hw_free = snd_pcm_plug_hw_free,
1060         .sw_params = snd_pcm_generic_sw_params,
1061         .channel_info = snd_pcm_generic_channel_info,
1062         .dump = snd_pcm_plug_dump,
1063         .nonblock = snd_pcm_generic_nonblock,
1064         .async = snd_pcm_generic_async,
1065         .mmap = snd_pcm_generic_mmap,
1066         .munmap = snd_pcm_generic_munmap,
1067 };
1068
1069 /**
1070  * \brief Creates a new Plug PCM
1071  * \param pcmp Returns created PCM handle
1072  * \param name Name of PCM
1073  * \param sformat Slave (destination) format
1074  * \param slave Slave PCM handle
1075  * \param close_slave When set, the slave PCM handle is closed with copy PCM
1076  * \retval zero on success otherwise a negative error code
1077  * \warning Using of this function might be dangerous in the sense
1078  *          of compatibility reasons. The prototype might be freely
1079  *          changed in future.
1080  */
1081 int snd_pcm_plug_open(snd_pcm_t **pcmp,
1082                       const char *name,
1083                       snd_pcm_format_t sformat, int schannels, int srate,
1084                       const snd_config_t *rate_converter,
1085                       enum snd_pcm_plug_route_policy route_policy,
1086                       snd_pcm_route_ttable_entry_t *ttable,
1087                       unsigned int tt_ssize,
1088                       unsigned int tt_cused, unsigned int tt_sused,
1089                       snd_pcm_t *slave, int close_slave)
1090 {
1091         snd_pcm_t *pcm;
1092         snd_pcm_plug_t *plug;
1093         int err;
1094         assert(pcmp && slave);
1095
1096         plug = calloc(1, sizeof(snd_pcm_plug_t));
1097         if (!plug)
1098                 return -ENOMEM;
1099         plug->sformat = sformat;
1100         plug->schannels = schannels;
1101         plug->srate = srate;
1102         plug->rate_converter = rate_converter;
1103         plug->gen.slave = plug->req_slave = slave;
1104         plug->gen.close_slave = close_slave;
1105         plug->route_policy = route_policy;
1106         plug->ttable = ttable;
1107         plug->tt_ssize = tt_ssize;
1108         plug->tt_cused = tt_cused;
1109         plug->tt_sused = tt_sused;
1110         
1111         err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1112         if (err < 0) {
1113                 free(plug);
1114                 return err;
1115         }
1116         pcm->ops = &snd_pcm_plug_ops;
1117         pcm->fast_ops = slave->fast_ops;
1118         pcm->fast_op_arg = slave->fast_op_arg;
1119         pcm->private_data = plug;
1120         pcm->poll_fd = slave->poll_fd;
1121         pcm->poll_events = slave->poll_events;
1122         pcm->mmap_shadow = 1;
1123         pcm->monotonic = slave->monotonic;
1124         snd_pcm_link_hw_ptr(pcm, slave);
1125         snd_pcm_link_appl_ptr(pcm, slave);
1126         *pcmp = pcm;
1127
1128         return 0;
1129 }
1130
1131 /*! \page pcm_plugins
1132
1133 \section pcm_plugins_plug Automatic conversion plugin
1134
1135 This plugin converts channels, rate and format on request.
1136
1137 \code
1138 pcm.name {
1139         type plug               # Automatic conversion PCM
1140         slave STR               # Slave name
1141         # or
1142         slave {                 # Slave definition
1143                 pcm STR         # Slave PCM name
1144                 # or
1145                 pcm { }         # Slave PCM definition
1146                 [format STR]    # Slave format (default nearest) or "unchanged"
1147                 [channels INT]  # Slave channels (default nearest) or "unchanged"
1148                 [rate INT]      # Slave rate (default nearest) or "unchanged"
1149         }
1150         route_policy STR        # route policy for automatic ttable generation
1151                                 # STR can be 'default', 'average', 'copy', 'duplicate'
1152                                 # average: result is average of input channels
1153                                 # copy: only first channels are copied to destination
1154                                 # duplicate: duplicate first set of channels
1155                                 # default: copy policy, except for mono capture - sum
1156         ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1157                 CCHANNEL {
1158                         SCHANNEL REAL   # route value (0.0 - 1.0)
1159                 }
1160         }
1161         rate_converter STR      # type of rate converter
1162         # or
1163         rate_converter [ STR1 STR2 ... ]
1164                                 # type of rate converter
1165                                 # default value is taken from defaults.pcm.rate_converter
1166 }
1167 \endcode
1168
1169 \subsection pcm_plugins_plug_funcref Function reference
1170
1171 <UL>
1172   <LI>snd_pcm_plug_open()
1173   <LI>_snd_pcm_plug_open()
1174 </UL>
1175
1176 */
1177
1178 /**
1179  * \brief Creates a new Plug PCM
1180  * \param pcmp Returns created PCM handle
1181  * \param name Name of PCM
1182  * \param root Root configuration node
1183  * \param conf Configuration node with Plug PCM description
1184  * \param stream Stream type
1185  * \param mode Stream mode
1186  * \retval zero on success otherwise a negative error code
1187  * \warning Using of this function might be dangerous in the sense
1188  *          of compatibility reasons. The prototype might be freely
1189  *          changed in future.
1190  */
1191 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1192                        snd_config_t *root, snd_config_t *conf, 
1193                        snd_pcm_stream_t stream, int mode)
1194 {
1195         snd_config_iterator_t i, next;
1196         int err;
1197         snd_pcm_t *spcm;
1198         snd_config_t *slave = NULL, *sconf;
1199         snd_config_t *tt = NULL;
1200         enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1201         snd_pcm_route_ttable_entry_t *ttable = NULL;
1202         unsigned int csize, ssize;
1203         unsigned int cused, sused;
1204         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1205         int schannels = -1, srate = -1;
1206         const snd_config_t *rate_converter = NULL;
1207
1208         snd_config_for_each(i, next, conf) {
1209                 snd_config_t *n = snd_config_iterator_entry(i);
1210                 const char *id;
1211                 if (snd_config_get_id(n, &id) < 0)
1212                         continue;
1213                 if (snd_pcm_conf_generic_id(id))
1214                         continue;
1215                 if (strcmp(id, "slave") == 0) {
1216                         slave = n;
1217                         continue;
1218                 }
1219 #ifdef BUILD_PCM_PLUGIN_ROUTE
1220                 if (strcmp(id, "ttable") == 0) {
1221                         route_policy = PLUG_ROUTE_POLICY_NONE;
1222                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1223                                 SNDERR("Invalid type for %s", id);
1224                                 return -EINVAL;
1225                         }
1226                         tt = n;
1227                         continue;
1228                 }
1229                 if (strcmp(id, "route_policy") == 0) {
1230                         const char *str;
1231                         if ((err = snd_config_get_string(n, &str)) < 0) {
1232                                 SNDERR("Invalid type for %s", id);
1233                                 return -EINVAL;
1234                         }
1235                         if (tt != NULL)
1236                                 SNDERR("Table is defined, route policy is ignored");
1237                         if (!strcmp(str, "default"))
1238                                 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1239                         else if (!strcmp(str, "average"))
1240                                 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1241                         else if (!strcmp(str, "copy"))
1242                                 route_policy = PLUG_ROUTE_POLICY_COPY;
1243                         else if (!strcmp(str, "duplicate"))
1244                                 route_policy = PLUG_ROUTE_POLICY_DUP;
1245                         continue;
1246                 }
1247 #endif
1248 #ifdef BUILD_PCM_PLUGIN_RATE
1249                 if (strcmp(id, "rate_converter") == 0) {
1250                         rate_converter = n;
1251                         continue;
1252                 }
1253 #endif
1254                 SNDERR("Unknown field %s", id);
1255                 return -EINVAL;
1256         }
1257         if (!slave) {
1258                 SNDERR("slave is not defined");
1259                 return -EINVAL;
1260         }
1261         err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1262                                  SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1263                                  SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1264                                  SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1265         if (err < 0)
1266                 return err;
1267 #ifdef BUILD_PCM_PLUGIN_ROUTE
1268         if (tt) {
1269                 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1270                 if (err < 0) {
1271                         snd_config_delete(sconf);
1272                         return err;
1273                 }
1274                 ttable = malloc(csize * ssize * sizeof(*ttable));
1275                 if (ttable == NULL) {
1276                         snd_config_delete(sconf);
1277                         return err;
1278                 }
1279                 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1280                 if (err < 0) {
1281                         snd_config_delete(sconf);
1282                         return err;
1283                 }
1284         }
1285 #endif
1286         
1287 #ifdef BUILD_PCM_PLUGIN_RATE
1288         if (! rate_converter)
1289                 rate_converter = snd_pcm_rate_get_default_converter(root);
1290 #endif
1291
1292         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1293         snd_config_delete(sconf);
1294         if (err < 0)
1295                 return err;
1296         err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1297                                 route_policy, ttable, ssize, cused, sused, spcm, 1);
1298         if (err < 0)
1299                 snd_pcm_close(spcm);
1300         return err;
1301 }
1302 #ifndef DOC_HIDDEN
1303 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1304 #endif