4
* \brief PCM Route & Volume Plugin Interface
5
* \author Abramo Bagnara <abramo@alsa-project.org>
10
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
13
* This library is free software; you can redistribute it and/or modify
14
* it under the terms of the GNU Lesser General Public License as
15
* published by the Free Software Foundation; either version 2.1 of
16
* the License, or (at your option) any later version.
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU Lesser General Public License for more details.
23
* You should have received a copy of the GNU Lesser General Public
24
* License along with this library; if not, write to the Free Software
25
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
#include "pcm_local.h"
30
#include "pcm_plugin.h"
33
/* entry for static linking */
34
const char *_snd_module_pcm_plug = "";
39
enum snd_pcm_plug_route_policy {
40
PLUG_ROUTE_POLICY_NONE,
41
PLUG_ROUTE_POLICY_DEFAULT,
42
PLUG_ROUTE_POLICY_COPY,
43
PLUG_ROUTE_POLICY_AVERAGE,
44
PLUG_ROUTE_POLICY_DUP,
48
snd_pcm_generic_t gen;
50
snd_pcm_format_t sformat;
53
const snd_config_t *rate_converter;
54
enum snd_pcm_plug_route_policy route_policy;
55
snd_pcm_route_ttable_entry_t *ttable;
56
int ttable_ok, ttable_last;
57
unsigned int tt_ssize, tt_cused, tt_sused;
62
static int snd_pcm_plug_close(snd_pcm_t *pcm)
64
snd_pcm_plug_t *plug = pcm->private_data;
67
assert(plug->gen.slave == plug->req_slave);
68
if (plug->gen.close_slave) {
69
snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
70
snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
71
err = snd_pcm_close(plug->req_slave);
79
static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
81
snd_pcm_plug_t *plug = pcm->private_data;
82
snd_pcm_t *slave = plug->req_slave;
85
if ((err = snd_pcm_info(slave, info)) < 0)
90
static const snd_pcm_format_t linear_preferred_formats[] = {
91
#ifdef SND_LITTLE_ENDIAN
92
SND_PCM_FORMAT_S16_LE,
93
SND_PCM_FORMAT_U16_LE,
94
SND_PCM_FORMAT_S16_BE,
95
SND_PCM_FORMAT_U16_BE,
97
SND_PCM_FORMAT_S16_BE,
98
SND_PCM_FORMAT_U16_BE,
99
SND_PCM_FORMAT_S16_LE,
100
SND_PCM_FORMAT_U16_LE,
102
#ifdef SND_LITTLE_ENDIAN
103
SND_PCM_FORMAT_S32_LE,
104
SND_PCM_FORMAT_U32_LE,
105
SND_PCM_FORMAT_S32_BE,
106
SND_PCM_FORMAT_U32_BE,
108
SND_PCM_FORMAT_S32_BE,
109
SND_PCM_FORMAT_U32_BE,
110
SND_PCM_FORMAT_S32_LE,
111
SND_PCM_FORMAT_U32_LE,
115
#ifdef SND_LITTLE_ENDIAN
116
SND_PCM_FORMAT_FLOAT_LE,
117
SND_PCM_FORMAT_FLOAT64_LE,
118
SND_PCM_FORMAT_FLOAT_BE,
119
SND_PCM_FORMAT_FLOAT64_BE,
121
SND_PCM_FORMAT_FLOAT_BE,
122
SND_PCM_FORMAT_FLOAT64_BE,
123
SND_PCM_FORMAT_FLOAT_LE,
124
SND_PCM_FORMAT_FLOAT64_LE,
126
#ifdef SND_LITTLE_ENDIAN
127
SND_PCM_FORMAT_S24_LE,
128
SND_PCM_FORMAT_U24_LE,
129
SND_PCM_FORMAT_S24_BE,
130
SND_PCM_FORMAT_U24_BE,
132
SND_PCM_FORMAT_S24_BE,
133
SND_PCM_FORMAT_U24_BE,
134
SND_PCM_FORMAT_S24_LE,
135
SND_PCM_FORMAT_U24_LE,
137
#ifdef SND_LITTLE_ENDIAN
138
SND_PCM_FORMAT_S24_3LE,
139
SND_PCM_FORMAT_U24_3LE,
140
SND_PCM_FORMAT_S24_3BE,
141
SND_PCM_FORMAT_U24_3BE,
143
SND_PCM_FORMAT_S24_3BE,
144
SND_PCM_FORMAT_U24_3BE,
145
SND_PCM_FORMAT_S24_3LE,
146
SND_PCM_FORMAT_U24_3LE,
148
#ifdef SND_LITTLE_ENDIAN
149
SND_PCM_FORMAT_S20_3LE,
150
SND_PCM_FORMAT_U20_3LE,
151
SND_PCM_FORMAT_S20_3BE,
152
SND_PCM_FORMAT_U20_3BE,
154
SND_PCM_FORMAT_S20_3BE,
155
SND_PCM_FORMAT_U20_3BE,
156
SND_PCM_FORMAT_S20_3LE,
157
SND_PCM_FORMAT_U20_3LE,
159
#ifdef SND_LITTLE_ENDIAN
160
SND_PCM_FORMAT_S18_3LE,
161
SND_PCM_FORMAT_U18_3LE,
162
SND_PCM_FORMAT_S18_3BE,
163
SND_PCM_FORMAT_U18_3BE,
165
SND_PCM_FORMAT_S18_3BE,
166
SND_PCM_FORMAT_U18_3BE,
167
SND_PCM_FORMAT_S18_3LE,
168
SND_PCM_FORMAT_U18_3LE,
172
#if defined(BUILD_PCM_PLUGIN_MULAW) || \
173
defined(BUILD_PCM_PLUGIN_ALAW) || \
174
defined(BUILD_PCM_PLUGIN_ADPCM)
175
#define BUILD_PCM_NONLINEAR
178
#ifdef BUILD_PCM_NONLINEAR
179
static const snd_pcm_format_t nonlinear_preferred_formats[] = {
180
#ifdef BUILD_PCM_PLUGIN_MULAW
181
SND_PCM_FORMAT_MU_LAW,
183
#ifdef BUILD_PCM_PLUGIN_ALAW
184
SND_PCM_FORMAT_A_LAW,
186
#ifdef BUILD_PCM_PLUGIN_ADPCM
187
SND_PCM_FORMAT_IMA_ADPCM,
192
#ifdef BUILD_PCM_PLUGIN_LFLOAT
193
static const snd_pcm_format_t float_preferred_formats[] = {
194
#ifdef SND_LITTLE_ENDIAN
195
SND_PCM_FORMAT_FLOAT_LE,
196
SND_PCM_FORMAT_FLOAT64_LE,
197
SND_PCM_FORMAT_FLOAT_BE,
198
SND_PCM_FORMAT_FLOAT64_BE,
200
SND_PCM_FORMAT_FLOAT_BE,
201
SND_PCM_FORMAT_FLOAT64_BE,
202
SND_PCM_FORMAT_FLOAT_LE,
203
SND_PCM_FORMAT_FLOAT64_LE,
208
static const char linear_format_widths[32] = {
209
0, 0, 0, 0, 0, 0, 0, 1,
210
0, 0, 0, 0, 0, 0, 0, 1,
211
0, 1, 0, 1, 0, 0, 0, 1,
212
0, 0, 0, 0, 0, 0, 0, 1,
215
static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
218
if (! linear_format_widths[wid - 1])
219
return SND_PCM_FORMAT_UNKNOWN;
220
for (e = 0; e < 2; e++) {
221
for (s = 0; s < 2; s++) {
222
int pw = ((wid + 7) / 8) * 8;
223
for (; pw <= 32; pw += 8) {
225
f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
226
if (f != SND_PCM_FORMAT_UNKNOWN &&
227
snd_pcm_format_mask_test(format_mask, f))
234
return SND_PCM_FORMAT_UNKNOWN;
237
static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
241
snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
242
snd_pcm_format_mask_t fl = {
243
#ifdef BUILD_PCM_PLUGIN_LFLOAT
249
if (snd_pcm_format_mask_test(format_mask, format))
251
if (!snd_pcm_format_mask_test(&lin, format) &&
252
!snd_pcm_format_mask_test(&fl, format)) {
255
#ifdef BUILD_PCM_PLUGIN_MULAW
256
case SND_PCM_FORMAT_MU_LAW:
258
#ifdef BUILD_PCM_PLUGIN_ALAW
259
case SND_PCM_FORMAT_A_LAW:
261
#ifdef BUILD_PCM_PLUGIN_ADPCM
262
case SND_PCM_FORMAT_IMA_ADPCM:
264
for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
265
snd_pcm_format_t f = linear_preferred_formats[i];
266
if (snd_pcm_format_mask_test(format_mask, f))
271
return SND_PCM_FORMAT_UNKNOWN;
275
snd_mask_intersect(&lin, format_mask);
276
snd_mask_intersect(&fl, format_mask);
277
if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
278
#ifdef BUILD_PCM_NONLINEAR
280
for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
281
snd_pcm_format_t f = nonlinear_preferred_formats[i];
282
if (snd_pcm_format_mask_test(format_mask, f))
286
return SND_PCM_FORMAT_UNKNOWN;
288
#ifdef BUILD_PCM_PLUGIN_LFLOAT
289
if (snd_pcm_format_float(format)) {
290
if (snd_pcm_format_mask_test(&fl, format)) {
292
for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
293
snd_pcm_format_t f = float_preferred_formats[i];
294
if (snd_pcm_format_mask_test(format_mask, f))
300
e = snd_pcm_format_big_endian(format);
303
if (snd_mask_empty(&lin)) {
304
#ifdef BUILD_PCM_PLUGIN_LFLOAT
306
for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
307
snd_pcm_format_t f = float_preferred_formats[i];
308
if (snd_pcm_format_mask_test(format_mask, f))
312
return SND_PCM_FORMAT_UNKNOWN;
314
w = snd_pcm_format_width(format);
315
u = snd_pcm_format_unsigned(format);
316
e = snd_pcm_format_big_endian(format);
318
for (w1 = w; w1 <= 32; w1++) {
319
f = check_linear_format(format_mask, w1, u, e);
320
if (f != SND_PCM_FORMAT_UNKNOWN)
323
for (w1 = w - 1; w1 > 0; w1--) {
324
f = check_linear_format(format_mask, w1, u, e);
325
if (f != SND_PCM_FORMAT_UNKNOWN)
328
return SND_PCM_FORMAT_UNKNOWN;
331
static void snd_pcm_plug_clear(snd_pcm_t *pcm)
333
snd_pcm_plug_t *plug = pcm->private_data;
334
snd_pcm_t *slave = plug->req_slave;
335
/* Clear old plugins */
336
if (plug->gen.slave != slave) {
337
snd_pcm_unlink_hw_ptr(pcm, plug->gen.slave);
338
snd_pcm_unlink_appl_ptr(pcm, plug->gen.slave);
339
snd_pcm_close(plug->gen.slave);
340
plug->gen.slave = slave;
341
pcm->fast_ops = slave->fast_ops;
342
pcm->fast_op_arg = slave->fast_op_arg;
348
snd_pcm_access_t access;
349
snd_pcm_format_t format;
350
unsigned int channels;
352
} snd_pcm_plug_params_t;
355
#ifdef BUILD_PCM_PLUGIN_RATE
356
static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
358
snd_pcm_plug_t *plug = pcm->private_data;
360
if (clt->rate == slv->rate)
362
assert(snd_pcm_format_linear(slv->format));
363
err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
364
plug->gen.slave, plug->gen.slave != plug->req_slave);
367
slv->access = clt->access;
368
slv->rate = clt->rate;
369
if (snd_pcm_format_linear(clt->format))
370
slv->format = clt->format;
375
#ifdef BUILD_PCM_PLUGIN_ROUTE
376
static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
378
snd_pcm_plug_t *plug = pcm->private_data;
379
unsigned int tt_ssize, tt_cused, tt_sused;
380
snd_pcm_route_ttable_entry_t *ttable;
382
if (clt->channels == slv->channels &&
383
(!plug->ttable || !plug->ttable_last))
385
if (clt->rate != slv->rate &&
386
clt->channels > slv->channels)
388
assert(snd_pcm_format_linear(slv->format));
389
tt_ssize = slv->channels;
390
tt_cused = clt->channels;
391
tt_sused = slv->channels;
392
ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
393
if (plug->ttable) { /* expand or shrink table */
394
unsigned int c = 0, s = 0;
395
for (c = 0; c < tt_cused; c++) {
396
for (s = 0; s < tt_sused; s++) {
397
snd_pcm_route_ttable_entry_t v;
398
if (c >= plug->tt_cused)
400
else if (s >= plug->tt_sused)
403
v = plug->ttable[c * plug->tt_ssize + s];
404
ttable[c * tt_ssize + s] = v;
410
unsigned int c = 0, s = 0;
411
enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
413
for (k = 0; k < tt_cused * tt_sused; ++k)
415
if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
416
rpolicy = PLUG_ROUTE_POLICY_COPY;
417
/* it's hack for mono conversion */
418
if (clt->channels == 1 || slv->channels == 1)
419
rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
422
case PLUG_ROUTE_POLICY_AVERAGE:
423
case PLUG_ROUTE_POLICY_DUP:
424
if (clt->channels > slv->channels) {
430
snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
431
if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
432
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
433
clt->channels > slv->channels) {
434
int srcs = clt->channels / slv->channels;
435
if (s < clt->channels % slv->channels)
438
} else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
439
slv->channels > clt->channels) {
440
int srcs = slv->channels / clt->channels;
441
if (s < slv->channels % clt->channels)
446
ttable[c * tt_ssize + s] = v;
447
if (++c == clt->channels)
449
if (++s == slv->channels)
453
case PLUG_ROUTE_POLICY_COPY:
454
if (clt->channels < slv->channels) {
459
for (c = 0; (int)c < n; c++)
460
ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
463
SNDERR("Invalid route policy");
467
err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->gen.slave, plug->gen.slave != plug->req_slave);
470
slv->channels = clt->channels;
471
slv->access = clt->access;
472
if (snd_pcm_format_linear(clt->format))
473
slv->format = clt->format;
478
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
480
snd_pcm_plug_t *plug = pcm->private_data;
482
snd_pcm_format_t cfmt;
483
int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
485
/* No conversion is needed */
486
if (clt->format == slv->format &&
487
clt->rate == slv->rate &&
488
clt->channels == clt->channels)
491
if (snd_pcm_format_linear(slv->format)) {
492
/* Conversion is done in another plugin */
493
if (clt->rate != slv->rate ||
494
clt->channels != slv->channels)
497
switch (clt->format) {
498
#ifdef BUILD_PCM_PLUGIN_MULAW
499
case SND_PCM_FORMAT_MU_LAW:
500
f = snd_pcm_mulaw_open;
503
#ifdef BUILD_PCM_PLUGIN_ALAW
504
case SND_PCM_FORMAT_A_LAW:
505
f = snd_pcm_alaw_open;
508
#ifdef BUILD_PCM_PLUGIN_ADPCM
509
case SND_PCM_FORMAT_IMA_ADPCM:
510
f = snd_pcm_adpcm_open;
514
#ifdef BUILD_PCM_PLUGIN_LFLOAT
515
if (snd_pcm_format_float(clt->format))
516
f = snd_pcm_lfloat_open;
520
f = snd_pcm_linear_open;
523
#ifdef BUILD_PCM_PLUGIN_LFLOAT
524
} else if (snd_pcm_format_float(slv->format)) {
525
/* Conversion is done in another plugin */
526
if (clt->format == slv->format &&
527
clt->rate == slv->rate &&
528
clt->channels == slv->channels)
531
if (snd_pcm_format_linear(clt->format))
532
f = snd_pcm_lfloat_open;
536
#ifdef BUILD_PCM_NONLINEAR
538
switch (slv->format) {
539
#ifdef BUILD_PCM_PLUGIN_MULAW
540
case SND_PCM_FORMAT_MU_LAW:
541
f = snd_pcm_mulaw_open;
544
#ifdef BUILD_PCM_PLUGIN_ALAW
545
case SND_PCM_FORMAT_A_LAW:
546
f = snd_pcm_alaw_open;
549
#ifdef BUILD_PCM_PLUGIN_ADPCM
550
case SND_PCM_FORMAT_IMA_ADPCM:
551
f = snd_pcm_adpcm_open;
557
if (snd_pcm_format_linear(clt->format))
560
cfmt = SND_PCM_FORMAT_S16;
561
#endif /* NONLINEAR */
563
err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
567
slv->access = clt->access;
571
static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
573
snd_pcm_plug_t *plug = pcm->private_data;
575
if (clt->access == slv->access)
577
err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
580
slv->access = clt->access;
584
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
585
static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
586
snd_pcm_plug_params_t *clt,
587
snd_pcm_plug_params_t *slv)
589
snd_pcm_plug_t *plug = pcm->private_data;
592
if (clt->access == slv->access)
595
switch (slv->access) {
596
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
597
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
598
case SND_PCM_ACCESS_MMAP_COMPLEX:
604
err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
605
plug->gen.slave != plug->req_slave);
608
switch (slv->access) {
609
case SND_PCM_ACCESS_RW_INTERLEAVED:
610
slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
612
case SND_PCM_ACCESS_RW_NONINTERLEAVED:
613
slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
622
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
623
snd_pcm_plug_params_t *client,
624
snd_pcm_plug_params_t *slave)
626
snd_pcm_plug_t *plug = pcm->private_data;
627
static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
628
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
629
snd_pcm_plug_change_mmap,
631
snd_pcm_plug_change_format,
632
#ifdef BUILD_PCM_PLUGIN_ROUTE
633
snd_pcm_plug_change_channels,
635
#ifdef BUILD_PCM_PLUGIN_RATE
636
snd_pcm_plug_change_rate,
638
#ifdef BUILD_PCM_PLUGIN_ROUTE
639
snd_pcm_plug_change_channels,
641
snd_pcm_plug_change_format,
642
snd_pcm_plug_change_access
644
snd_pcm_plug_params_t p = *slave;
646
plug->ttable_ok = plug->ttable_last = 0;
647
while (client->format != p.format ||
648
client->channels != p.channels ||
649
client->rate != p.rate ||
650
client->access != p.access) {
653
if (k >= sizeof(funcs)/sizeof(*funcs))
655
err = funcs[k](pcm, &new, client, &p);
657
snd_pcm_plug_clear(pcm);
661
plug->gen.slave = new;
662
pcm->fast_ops = new->fast_ops;
663
pcm->fast_op_arg = new->fast_op_arg;
667
#ifdef BUILD_PCM_PLUGIN_ROUTE
668
/* it's exception, user specified ttable, but no reduction/expand */
669
if (plug->ttable && !plug->ttable_ok) {
672
plug->ttable_last = 1;
673
err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
675
snd_pcm_plug_clear(pcm);
679
assert(plug->ttable_ok);
680
plug->gen.slave = new;
681
pcm->fast_ops = new->fast_ops;
682
pcm->fast_op_arg = new->fast_op_arg;
688
static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
690
unsigned int rate_min, channels_max;
693
/* HACK: to avoid overflow in PARTBIT_RATE code */
694
err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
697
if (rate_min < 4000) {
698
_snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
699
if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
702
/* HACK: to avoid overflow in PERIOD_SIZE code */
703
err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
706
if (channels_max > 10000) {
707
_snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
708
if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
714
static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
716
snd_pcm_plug_t *plug = pcm->private_data;
719
_snd_pcm_hw_params_any(sparams);
720
if (plug->sformat >= 0) {
721
_snd_pcm_hw_params_set_format(sparams, plug->sformat);
722
_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
724
if (plug->schannels > 0)
725
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
728
_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
729
plug->srate, 0, plug->srate + 1, -1);
730
/* reduce the available configurations */
731
err = snd_pcm_hw_refine(plug->req_slave, sparams);
737
static int check_access_change(snd_pcm_hw_params_t *cparams,
738
snd_pcm_hw_params_t *sparams)
740
snd_pcm_access_mask_t *smask;
741
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
742
const snd_pcm_access_mask_t *cmask;
743
snd_pcm_access_mask_t mask;
746
smask = (snd_pcm_access_mask_t *)
747
snd_pcm_hw_param_get_mask(sparams,
748
SND_PCM_HW_PARAM_ACCESS);
749
if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
750
snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
751
snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
752
return 0; /* OK, we have mmap support */
753
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
754
/* no mmap support - we need mmap emulation */
756
if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
757
!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
758
return -EINVAL; /* even no RW access? no way! */
760
cmask = (const snd_pcm_access_mask_t *)
761
snd_pcm_hw_param_get_mask(cparams,
762
SND_PCM_HW_PARAM_ACCESS);
763
snd_mask_none(&mask);
764
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
765
snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
766
if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
767
snd_pcm_access_mask_set(&mask,
768
SND_PCM_ACCESS_RW_INTERLEAVED);
770
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
771
snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
772
if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
773
snd_pcm_access_mask_set(&mask,
774
SND_PCM_ACCESS_RW_NONINTERLEAVED);
776
if (!snd_mask_empty(&mask))
777
*smask = mask; /* prefer the straight conversion */
784
static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
785
snd_pcm_hw_params_t *sparams)
787
snd_pcm_plug_t *plug = pcm->private_data;
788
snd_pcm_t *slave = plug->req_slave;
789
unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
790
SND_PCM_HW_PARBIT_TICK_TIME);
791
const snd_pcm_format_mask_t *format_mask, *sformat_mask;
792
snd_pcm_format_mask_t sfmt_mask;
794
snd_pcm_format_t format;
795
snd_interval_t t, buffer_size;
796
const snd_interval_t *srate, *crate;
798
if (plug->srate == -2 ||
799
(pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
800
(params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
801
links |= SND_PCM_HW_PARBIT_RATE;
803
err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
808
if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
809
links |= SND_PCM_HW_PARBIT_CHANNELS;
811
err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
815
if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
816
links |= SND_PCM_HW_PARBIT_FORMAT;
818
format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
819
sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
820
snd_mask_none(&sfmt_mask);
821
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
823
if (!snd_pcm_format_mask_test(format_mask, format))
825
if (snd_pcm_format_mask_test(sformat_mask, format))
828
f = snd_pcm_plug_slave_format(format, sformat_mask);
829
if (f == SND_PCM_FORMAT_UNKNOWN)
832
snd_pcm_format_mask_set(&sfmt_mask, f);
835
if (snd_pcm_format_mask_empty(&sfmt_mask)) {
836
SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
837
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
838
if (!snd_pcm_format_mask_test(format_mask, format))
840
SNDERR("Format: %s", snd_pcm_format_name(format));
842
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
843
if (!snd_pcm_format_mask_test(sformat_mask, format))
845
SNDERR("Slave format: %s", snd_pcm_format_name(format));
849
err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
850
SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
855
if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
856
err = check_access_change(params, sparams);
858
SNDERR("Unable to find an usable access for '%s'",
864
if ((links & SND_PCM_HW_PARBIT_RATE) ||
865
snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
866
links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
867
SND_PCM_HW_PARBIT_BUFFER_SIZE);
869
snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
870
snd_interval_unfloor(&buffer_size);
871
crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
872
srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
873
snd_interval_muldiv(&buffer_size, srate, crate, &t);
874
err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
878
err = _snd_pcm_hw_params_refine(sparams, links, params);
884
static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
885
snd_pcm_hw_params_t *params,
886
snd_pcm_hw_params_t *sparams)
888
snd_pcm_plug_t *plug = pcm->private_data;
889
unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
890
SND_PCM_HW_PARBIT_TICK_TIME);
891
const snd_pcm_format_mask_t *format_mask, *sformat_mask;
892
snd_pcm_format_mask_t fmt_mask;
894
snd_pcm_format_t format;
896
const snd_interval_t *sbuffer_size;
897
const snd_interval_t *srate, *crate;
899
if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
900
links |= SND_PCM_HW_PARBIT_CHANNELS;
902
if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
903
links |= SND_PCM_HW_PARBIT_FORMAT;
905
format_mask = snd_pcm_hw_param_get_mask(params,
906
SND_PCM_HW_PARAM_FORMAT);
907
sformat_mask = snd_pcm_hw_param_get_mask(sparams,
908
SND_PCM_HW_PARAM_FORMAT);
909
snd_mask_none(&fmt_mask);
910
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
912
if (!snd_pcm_format_mask_test(format_mask, format))
914
if (snd_pcm_format_mask_test(sformat_mask, format))
917
f = snd_pcm_plug_slave_format(format, sformat_mask);
918
if (f == SND_PCM_FORMAT_UNKNOWN)
921
snd_pcm_format_mask_set(&fmt_mask, format);
924
if (snd_pcm_format_mask_empty(&fmt_mask)) {
925
SNDERR("Unable to find an usable client format");
926
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
927
if (!snd_pcm_format_mask_test(format_mask, format))
929
SNDERR("Format: %s", snd_pcm_format_name(format));
931
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
932
if (!snd_pcm_format_mask_test(sformat_mask, format))
934
SNDERR("Slave format: %s", snd_pcm_format_name(format));
939
err = _snd_pcm_hw_param_set_mask(params,
940
SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
945
if (plug->srate == -2 ||
946
(pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
947
(params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
948
links |= SND_PCM_HW_PARBIT_RATE;
950
unsigned int rate_min, srate_min;
951
int rate_mindir, srate_mindir;
953
/* This is a temporary hack, waiting for a better solution */
954
err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
957
err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
960
if (rate_min == srate_min && srate_mindir > rate_mindir) {
961
err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
966
if ((links & SND_PCM_HW_PARBIT_RATE) ||
967
snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
968
links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
969
SND_PCM_HW_PARBIT_BUFFER_SIZE);
971
sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
972
crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
973
srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
974
snd_interval_muldiv(sbuffer_size, crate, srate, &t);
975
snd_interval_floor(&t);
976
if (snd_interval_empty(&t))
978
err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
982
err = _snd_pcm_hw_params_refine(params, links, sparams);
986
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
990
static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
992
snd_pcm_plug_t *plug = pcm->private_data;
993
return snd_pcm_hw_refine(plug->req_slave, params);
996
static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
998
return snd_pcm_hw_refine_slave(pcm, params,
999
snd_pcm_plug_hw_refine_cprepare,
1000
snd_pcm_plug_hw_refine_cchange,
1001
snd_pcm_plug_hw_refine_sprepare,
1002
snd_pcm_plug_hw_refine_schange,
1003
snd_pcm_plug_hw_refine_slave);
1006
static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1008
snd_pcm_plug_t *plug = pcm->private_data;
1009
snd_pcm_t *slave = plug->req_slave;
1010
snd_pcm_plug_params_t clt_params, slv_params;
1011
snd_pcm_hw_params_t sparams;
1014
err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
1017
err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1020
err = snd_pcm_hw_refine_soft(slave, &sparams);
1024
INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1025
INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1026
INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1027
INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1029
INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1030
INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1031
INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1032
snd_pcm_plug_clear(pcm);
1033
if (!(clt_params.format == slv_params.format &&
1034
clt_params.channels == slv_params.channels &&
1035
clt_params.rate == slv_params.rate &&
1037
snd_pcm_hw_params_test_access(slave, &sparams,
1038
clt_params.access) >= 0)) {
1039
INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1040
err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1044
slave = plug->gen.slave;
1045
err = _snd_pcm_hw_params(slave, params);
1047
snd_pcm_plug_clear(pcm);
1050
snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1051
snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1052
snd_pcm_link_hw_ptr(pcm, slave);
1053
snd_pcm_link_appl_ptr(pcm, slave);
1057
static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1059
snd_pcm_plug_t *plug = pcm->private_data;
1060
snd_pcm_t *slave = plug->gen.slave;
1061
int err = snd_pcm_hw_free(slave);
1062
snd_pcm_plug_clear(pcm);
1066
static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1068
snd_pcm_plug_t *plug = pcm->private_data;
1069
snd_output_printf(out, "Plug PCM: ");
1070
snd_pcm_dump(plug->gen.slave, out);
1073
static const snd_pcm_ops_t snd_pcm_plug_ops = {
1074
.close = snd_pcm_plug_close,
1075
.info = snd_pcm_plug_info,
1076
.hw_refine = snd_pcm_plug_hw_refine,
1077
.hw_params = snd_pcm_plug_hw_params,
1078
.hw_free = snd_pcm_plug_hw_free,
1079
.sw_params = snd_pcm_generic_sw_params,
1080
.channel_info = snd_pcm_generic_channel_info,
1081
.dump = snd_pcm_plug_dump,
1082
.nonblock = snd_pcm_generic_nonblock,
1083
.async = snd_pcm_generic_async,
1084
.mmap = snd_pcm_generic_mmap,
1085
.munmap = snd_pcm_generic_munmap,
1089
* \brief Creates a new Plug PCM
1090
* \param pcmp Returns created PCM handle
1091
* \param name Name of PCM
1092
* \param sformat Slave (destination) format
1093
* \param slave Slave PCM handle
1094
* \param close_slave When set, the slave PCM handle is closed with copy PCM
1095
* \retval zero on success otherwise a negative error code
1096
* \warning Using of this function might be dangerous in the sense
1097
* of compatibility reasons. The prototype might be freely
1098
* changed in future.
1100
int snd_pcm_plug_open(snd_pcm_t **pcmp,
1102
snd_pcm_format_t sformat, int schannels, int srate,
1103
const snd_config_t *rate_converter,
1104
enum snd_pcm_plug_route_policy route_policy,
1105
snd_pcm_route_ttable_entry_t *ttable,
1106
unsigned int tt_ssize,
1107
unsigned int tt_cused, unsigned int tt_sused,
1108
snd_pcm_t *slave, int close_slave)
1111
snd_pcm_plug_t *plug;
1113
assert(pcmp && slave);
1115
plug = calloc(1, sizeof(snd_pcm_plug_t));
1118
plug->sformat = sformat;
1119
plug->schannels = schannels;
1120
plug->srate = srate;
1121
plug->rate_converter = rate_converter;
1122
plug->gen.slave = plug->req_slave = slave;
1123
plug->gen.close_slave = close_slave;
1124
plug->route_policy = route_policy;
1125
plug->ttable = ttable;
1126
plug->tt_ssize = tt_ssize;
1127
plug->tt_cused = tt_cused;
1128
plug->tt_sused = tt_sused;
1130
err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1135
pcm->ops = &snd_pcm_plug_ops;
1136
pcm->fast_ops = slave->fast_ops;
1137
pcm->fast_op_arg = slave->fast_op_arg;
1138
pcm->private_data = plug;
1139
pcm->poll_fd = slave->poll_fd;
1140
pcm->poll_events = slave->poll_events;
1141
pcm->mmap_shadow = 1;
1142
pcm->monotonic = slave->monotonic;
1143
snd_pcm_link_hw_ptr(pcm, slave);
1144
snd_pcm_link_appl_ptr(pcm, slave);
1150
/*! \page pcm_plugins
1152
\section pcm_plugins_plug Automatic conversion plugin
1154
This plugin converts channels, rate and format on request.
1158
type plug # Automatic conversion PCM
1159
slave STR # Slave name
1161
slave { # Slave definition
1162
pcm STR # Slave PCM name
1164
pcm { } # Slave PCM definition
1165
[format STR] # Slave format (default nearest) or "unchanged"
1166
[channels INT] # Slave channels (default nearest) or "unchanged"
1167
[rate INT] # Slave rate (default nearest) or "unchanged"
1169
route_policy STR # route policy for automatic ttable generation
1170
# STR can be 'default', 'average', 'copy', 'duplicate'
1171
# average: result is average of input channels
1172
# copy: only first channels are copied to destination
1173
# duplicate: duplicate first set of channels
1174
# default: copy policy, except for mono capture - sum
1175
ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1177
SCHANNEL REAL # route value (0.0 - 1.0)
1180
rate_converter STR # type of rate converter
1182
rate_converter [ STR1 STR2 ... ]
1183
# type of rate converter
1184
# default value is taken from defaults.pcm.rate_converter
1188
\subsection pcm_plugins_plug_funcref Function reference
1191
<LI>snd_pcm_plug_open()
1192
<LI>_snd_pcm_plug_open()
1198
* \brief Creates a new Plug PCM
1199
* \param pcmp Returns created PCM handle
1200
* \param name Name of PCM
1201
* \param root Root configuration node
1202
* \param conf Configuration node with Plug PCM description
1203
* \param stream Stream type
1204
* \param mode Stream mode
1205
* \retval zero on success otherwise a negative error code
1206
* \warning Using of this function might be dangerous in the sense
1207
* of compatibility reasons. The prototype might be freely
1208
* changed in future.
1210
int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1211
snd_config_t *root, snd_config_t *conf,
1212
snd_pcm_stream_t stream, int mode)
1214
snd_config_iterator_t i, next;
1217
snd_config_t *slave = NULL, *sconf;
1218
snd_config_t *tt = NULL;
1219
enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1220
snd_pcm_route_ttable_entry_t *ttable = NULL;
1221
unsigned int csize, ssize;
1222
unsigned int cused, sused;
1223
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1224
int schannels = -1, srate = -1;
1225
const snd_config_t *rate_converter = NULL;
1227
snd_config_for_each(i, next, conf) {
1228
snd_config_t *n = snd_config_iterator_entry(i);
1230
if (snd_config_get_id(n, &id) < 0)
1232
if (snd_pcm_conf_generic_id(id))
1234
if (strcmp(id, "slave") == 0) {
1238
#ifdef BUILD_PCM_PLUGIN_ROUTE
1239
if (strcmp(id, "ttable") == 0) {
1240
route_policy = PLUG_ROUTE_POLICY_NONE;
1241
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1242
SNDERR("Invalid type for %s", id);
1248
if (strcmp(id, "route_policy") == 0) {
1250
if ((err = snd_config_get_string(n, &str)) < 0) {
1251
SNDERR("Invalid type for %s", id);
1255
SNDERR("Table is defined, route policy is ignored");
1256
if (!strcmp(str, "default"))
1257
route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1258
else if (!strcmp(str, "average"))
1259
route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1260
else if (!strcmp(str, "copy"))
1261
route_policy = PLUG_ROUTE_POLICY_COPY;
1262
else if (!strcmp(str, "duplicate"))
1263
route_policy = PLUG_ROUTE_POLICY_DUP;
1267
#ifdef BUILD_PCM_PLUGIN_RATE
1268
if (strcmp(id, "rate_converter") == 0) {
1273
SNDERR("Unknown field %s", id);
1277
SNDERR("slave is not defined");
1280
err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1281
SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1282
SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1283
SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1286
#ifdef BUILD_PCM_PLUGIN_ROUTE
1288
err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1290
snd_config_delete(sconf);
1293
ttable = malloc(csize * ssize * sizeof(*ttable));
1294
if (ttable == NULL) {
1295
snd_config_delete(sconf);
1298
err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1300
snd_config_delete(sconf);
1306
#ifdef BUILD_PCM_PLUGIN_RATE
1307
if (! rate_converter)
1308
rate_converter = snd_pcm_rate_get_default_converter(root);
1311
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1312
snd_config_delete(sconf);
1315
err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1316
route_policy, ttable, ssize, cused, sused, spcm, 1);
1318
snd_pcm_close(spcm);
1322
SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);