~ubuntu-branches/ubuntu/maverick/alsa-lib/maverick-proposed

« back to all changes in this revision

Viewing changes to .pc/relibtoolise.patch/src/pcm/pcm_plug.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel T Chen
  • Date: 2010-08-29 13:13:19 UTC
  • Revision ID: james.westby@ubuntu.com-20100829131319-ybw1m417ja2d2nv4
Tags: 1.0.23-1ubuntu2
* Apply upstream git changesets aadcbab through 72c7260ce
  and rediff debian/patches/relibtoolise.patch
  - correctly free dl handles, fixes LP: #552411, #584393
    (Closes: #589896)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        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