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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 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;
57 unsigned int tt_ssize, tt_cused, tt_sused;
58 } snd_pcm_plug_t;
59
60 #endif
61
snd_pcm_plug_close(snd_pcm_t *pcm)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 if (plug->rate_converter) {
68 snd_config_delete(plug->rate_converter);
69 plug->rate_converter = NULL;
70 }
71 assert(plug->gen.slave == plug->req_slave);
72 if (plug->gen.close_slave) {
73 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
74 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
75 err = snd_pcm_close(plug->req_slave);
76 if (err < 0)
77 result = err;
78 }
79 free(plug);
80 return result;
81 }
82
snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)83 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
84 {
85 snd_pcm_plug_t *plug = pcm->private_data;
86 snd_pcm_t *slave = plug->req_slave;
87 int err;
88
89 if ((err = snd_pcm_info(slave, info)) < 0)
90 return err;
91 return 0;
92 }
93
94 static const snd_pcm_format_t linear_preferred_formats[] = {
95 #ifdef SND_LITTLE_ENDIAN
96 SND_PCM_FORMAT_S16_LE,
97 SND_PCM_FORMAT_U16_LE,
98 SND_PCM_FORMAT_S16_BE,
99 SND_PCM_FORMAT_U16_BE,
100 #else
101 SND_PCM_FORMAT_S16_BE,
102 SND_PCM_FORMAT_U16_BE,
103 SND_PCM_FORMAT_S16_LE,
104 SND_PCM_FORMAT_U16_LE,
105 #endif
106 #ifdef SND_LITTLE_ENDIAN
107 SND_PCM_FORMAT_S32_LE,
108 SND_PCM_FORMAT_U32_LE,
109 SND_PCM_FORMAT_S32_BE,
110 SND_PCM_FORMAT_U32_BE,
111 #else
112 SND_PCM_FORMAT_S32_BE,
113 SND_PCM_FORMAT_U32_BE,
114 SND_PCM_FORMAT_S32_LE,
115 SND_PCM_FORMAT_U32_LE,
116 #endif
117 SND_PCM_FORMAT_S8,
118 SND_PCM_FORMAT_U8,
119 #ifdef SND_LITTLE_ENDIAN
120 SND_PCM_FORMAT_FLOAT_LE,
121 SND_PCM_FORMAT_FLOAT64_LE,
122 SND_PCM_FORMAT_FLOAT_BE,
123 SND_PCM_FORMAT_FLOAT64_BE,
124 #else
125 SND_PCM_FORMAT_FLOAT_BE,
126 SND_PCM_FORMAT_FLOAT64_BE,
127 SND_PCM_FORMAT_FLOAT_LE,
128 SND_PCM_FORMAT_FLOAT64_LE,
129 #endif
130 #ifdef SND_LITTLE_ENDIAN
131 SND_PCM_FORMAT_S24_LE,
132 SND_PCM_FORMAT_U24_LE,
133 SND_PCM_FORMAT_S24_BE,
134 SND_PCM_FORMAT_U24_BE,
135 #else
136 SND_PCM_FORMAT_S24_BE,
137 SND_PCM_FORMAT_U24_BE,
138 SND_PCM_FORMAT_S24_LE,
139 SND_PCM_FORMAT_U24_LE,
140 #endif
141 #ifdef SND_LITTLE_ENDIAN
142 SND_PCM_FORMAT_S20_LE,
143 SND_PCM_FORMAT_U20_LE,
144 SND_PCM_FORMAT_S20_BE,
145 SND_PCM_FORMAT_U20_BE,
146 #else
147 SND_PCM_FORMAT_S20_BE,
148 SND_PCM_FORMAT_U20_BE,
149 SND_PCM_FORMAT_S20_LE,
150 SND_PCM_FORMAT_U20_LE,
151 #endif
152 #ifdef SND_LITTLE_ENDIAN
153 SND_PCM_FORMAT_S24_3LE,
154 SND_PCM_FORMAT_U24_3LE,
155 SND_PCM_FORMAT_S24_3BE,
156 SND_PCM_FORMAT_U24_3BE,
157 #else
158 SND_PCM_FORMAT_S24_3BE,
159 SND_PCM_FORMAT_U24_3BE,
160 SND_PCM_FORMAT_S24_3LE,
161 SND_PCM_FORMAT_U24_3LE,
162 #endif
163 #ifdef SND_LITTLE_ENDIAN
164 SND_PCM_FORMAT_S20_3LE,
165 SND_PCM_FORMAT_U20_3LE,
166 SND_PCM_FORMAT_S20_3BE,
167 SND_PCM_FORMAT_U20_3BE,
168 #else
169 SND_PCM_FORMAT_S20_3BE,
170 SND_PCM_FORMAT_U20_3BE,
171 SND_PCM_FORMAT_S20_3LE,
172 SND_PCM_FORMAT_U20_3LE,
173 #endif
174 #ifdef SND_LITTLE_ENDIAN
175 SND_PCM_FORMAT_S18_3LE,
176 SND_PCM_FORMAT_U18_3LE,
177 SND_PCM_FORMAT_S18_3BE,
178 SND_PCM_FORMAT_U18_3BE,
179 #else
180 SND_PCM_FORMAT_S18_3BE,
181 SND_PCM_FORMAT_U18_3BE,
182 SND_PCM_FORMAT_S18_3LE,
183 SND_PCM_FORMAT_U18_3LE,
184 #endif
185 };
186
187 #if defined(BUILD_PCM_PLUGIN_MULAW) || \
188 defined(BUILD_PCM_PLUGIN_ALAW) || \
189 defined(BUILD_PCM_PLUGIN_ADPCM) || \
190 defined(BUILD_PCM_PLUGIN_IEC958)
191 #define BUILD_PCM_NONLINEAR
192 #endif
193
194 #ifdef BUILD_PCM_NONLINEAR
195 static const snd_pcm_format_t nonlinear_preferred_formats[] = {
196 #ifdef BUILD_PCM_PLUGIN_MULAW
197 SND_PCM_FORMAT_MU_LAW,
198 #endif
199 #ifdef BUILD_PCM_PLUGIN_ALAW
200 SND_PCM_FORMAT_A_LAW,
201 #endif
202 #ifdef BUILD_PCM_PLUGIN_ADPCM
203 SND_PCM_FORMAT_IMA_ADPCM,
204 #endif
205 #ifdef BUILD_PCM_PLUGIN_IEC958
206 SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
207 SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
208 #endif
209 };
210 #endif
211
212 #ifdef BUILD_PCM_PLUGIN_LFLOAT
213 static const snd_pcm_format_t float_preferred_formats[] = {
214 #ifdef SND_LITTLE_ENDIAN
215 SND_PCM_FORMAT_FLOAT_LE,
216 SND_PCM_FORMAT_FLOAT64_LE,
217 SND_PCM_FORMAT_FLOAT_BE,
218 SND_PCM_FORMAT_FLOAT64_BE,
219 #else
220 SND_PCM_FORMAT_FLOAT_BE,
221 SND_PCM_FORMAT_FLOAT64_BE,
222 SND_PCM_FORMAT_FLOAT_LE,
223 SND_PCM_FORMAT_FLOAT64_LE,
224 #endif
225 };
226 #endif
227
228 static const char linear_format_widths[32] = {
229 0, 0, 0, 0, 0, 0, 0, 1,
230 0, 0, 0, 0, 0, 0, 0, 1,
231 0, 1, 0, 1, 0, 0, 0, 1,
232 0, 0, 0, 0, 0, 0, 0, 1,
233 };
234
check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)235 static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
236 {
237 int e, s;
238 if (! linear_format_widths[wid - 1])
239 return SND_PCM_FORMAT_UNKNOWN;
240 for (e = 0; e < 2; e++) {
241 for (s = 0; s < 2; s++) {
242 int pw = ((wid + 7) / 8) * 8;
243 for (; pw <= 32; pw += 8) {
244 snd_pcm_format_t f;
245 f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
246 if (f != SND_PCM_FORMAT_UNKNOWN &&
247 snd_pcm_format_mask_test(format_mask, f))
248 return f;
249 }
250 sgn = !sgn;
251 }
252 ed = !ed;
253 }
254 return SND_PCM_FORMAT_UNKNOWN;
255 }
256
snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)257 static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
258 {
259 int w, w1, u, e;
260 snd_pcm_format_t f;
261 snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
262 snd_pcm_format_mask_t fl = {
263 #ifdef BUILD_PCM_PLUGIN_LFLOAT
264 SND_PCM_FMTBIT_FLOAT
265 #else
266 { 0 }
267 #endif
268 };
269 if (snd_pcm_format_mask_test(format_mask, format))
270 return format;
271 if (!snd_pcm_format_mask_test(&lin, format) &&
272 !snd_pcm_format_mask_test(&fl, format)) {
273 unsigned int i;
274 switch (format) {
275 #ifdef BUILD_PCM_PLUGIN_MULAW
276 case SND_PCM_FORMAT_MU_LAW:
277 #endif
278 #ifdef BUILD_PCM_PLUGIN_ALAW
279 case SND_PCM_FORMAT_A_LAW:
280 #endif
281 #ifdef BUILD_PCM_PLUGIN_ADPCM
282 case SND_PCM_FORMAT_IMA_ADPCM:
283 #endif
284 for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
285 snd_pcm_format_t f = linear_preferred_formats[i];
286 if (snd_pcm_format_mask_test(format_mask, f))
287 return f;
288 }
289 /* Fall through */
290 default:
291 return SND_PCM_FORMAT_UNKNOWN;
292 }
293
294 }
295 snd_mask_intersect(&lin, format_mask);
296 snd_mask_intersect(&fl, format_mask);
297 if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
298 #ifdef BUILD_PCM_NONLINEAR
299 unsigned int i;
300 for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
301 snd_pcm_format_t f = nonlinear_preferred_formats[i];
302 if (snd_pcm_format_mask_test(format_mask, f))
303 return f;
304 }
305 #endif
306 return SND_PCM_FORMAT_UNKNOWN;
307 }
308 #ifdef BUILD_PCM_PLUGIN_LFLOAT
309 if (snd_pcm_format_float(format)) {
310 if (snd_pcm_format_mask_test(&fl, format)) {
311 unsigned int i;
312 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
313 snd_pcm_format_t f = float_preferred_formats[i];
314 if (snd_pcm_format_mask_test(format_mask, f))
315 return f;
316 }
317 }
318 w = 32;
319 u = 0;
320 e = snd_pcm_format_big_endian(format);
321 } else
322 #endif
323 if (snd_mask_empty(&lin)) {
324 #ifdef BUILD_PCM_PLUGIN_LFLOAT
325 unsigned int i;
326 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
327 snd_pcm_format_t f = float_preferred_formats[i];
328 if (snd_pcm_format_mask_test(format_mask, f))
329 return f;
330 }
331 #endif
332 return SND_PCM_FORMAT_UNKNOWN;
333 } else {
334 w = snd_pcm_format_width(format);
335 u = snd_pcm_format_unsigned(format);
336 e = snd_pcm_format_big_endian(format);
337 }
338 for (w1 = w; w1 <= 32; w1++) {
339 f = check_linear_format(format_mask, w1, u, e);
340 if (f != SND_PCM_FORMAT_UNKNOWN)
341 return f;
342 }
343 for (w1 = w - 1; w1 > 0; w1--) {
344 f = check_linear_format(format_mask, w1, u, e);
345 if (f != SND_PCM_FORMAT_UNKNOWN)
346 return f;
347 }
348 return SND_PCM_FORMAT_UNKNOWN;
349 }
350
snd_pcm_plug_clear(snd_pcm_t *pcm)351 static void snd_pcm_plug_clear(snd_pcm_t *pcm)
352 {
353 snd_pcm_plug_t *plug = pcm->private_data;
354 snd_pcm_t *slave = plug->req_slave;
355 /* Clear old plugins */
356 if (plug->gen.slave != slave) {
357 snd_pcm_unlink_hw_ptr(pcm, plug->gen.slave);
358 snd_pcm_unlink_appl_ptr(pcm, plug->gen.slave);
359 snd_pcm_close(plug->gen.slave);
360 plug->gen.slave = slave;
361 pcm->fast_ops = slave->fast_ops;
362 pcm->fast_op_arg = slave->fast_op_arg;
363 }
364 }
365
366 #ifndef DOC_HIDDEN
367 typedef struct {
368 snd_pcm_access_t access;
369 snd_pcm_format_t format;
370 unsigned int channels;
371 unsigned int rate;
372 } snd_pcm_plug_params_t;
373 #endif
374
375 #ifdef BUILD_PCM_PLUGIN_RATE
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)376 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)
377 {
378 snd_pcm_plug_t *plug = pcm->private_data;
379 int err;
380 if (clt->rate == slv->rate)
381 return 0;
382 assert(snd_pcm_format_linear(slv->format));
383 err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
384 plug->gen.slave, plug->gen.slave != plug->req_slave);
385 if (err < 0)
386 return err;
387 slv->access = clt->access;
388 slv->rate = clt->rate;
389 if (snd_pcm_format_linear(clt->format))
390 slv->format = clt->format;
391 return 1;
392 }
393 #endif
394
395 #ifdef BUILD_PCM_PLUGIN_ROUTE
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)396 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)
397 {
398 snd_pcm_plug_t *plug = pcm->private_data;
399 unsigned int tt_ssize, tt_cused, tt_sused;
400 snd_pcm_route_ttable_entry_t *ttable;
401 int err;
402 if (clt->channels == slv->channels &&
403 (!plug->ttable || plug->ttable_ok))
404 return 0;
405 if (clt->rate != slv->rate &&
406 clt->channels > slv->channels)
407 return 0;
408 assert(snd_pcm_format_linear(slv->format));
409 tt_ssize = slv->channels;
410 tt_cused = clt->channels;
411 tt_sused = slv->channels;
412 ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
413 if (plug->ttable) { /* expand or shrink table */
414 unsigned int c = 0, s = 0;
415 for (c = 0; c < tt_cused; c++) {
416 for (s = 0; s < tt_sused; s++) {
417 snd_pcm_route_ttable_entry_t v;
418 if (c >= plug->tt_cused)
419 v = 0;
420 else if (s >= plug->tt_sused)
421 v = 0;
422 else
423 v = plug->ttable[c * plug->tt_ssize + s];
424 ttable[c * tt_ssize + s] = v;
425 }
426 }
427 plug->ttable_ok = 1;
428 } else {
429 unsigned int k;
430 unsigned int c = 0, s = 0;
431 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
432 int n;
433 for (k = 0; k < tt_cused * tt_sused; ++k)
434 ttable[k] = 0;
435 if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
436 rpolicy = PLUG_ROUTE_POLICY_COPY;
437 /* it's hack for mono conversion */
438 if (clt->channels == 1 || slv->channels == 1)
439 rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
440 }
441 switch (rpolicy) {
442 case PLUG_ROUTE_POLICY_AVERAGE:
443 case PLUG_ROUTE_POLICY_DUP:
444 if (clt->channels > slv->channels) {
445 n = clt->channels;
446 } else {
447 n = slv->channels;
448 }
449 while (n-- > 0) {
450 snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
451 if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
452 if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
453 clt->channels > slv->channels) {
454 int srcs = clt->channels / slv->channels;
455 if (s < clt->channels % slv->channels)
456 srcs++;
457 v /= srcs;
458 } else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
459 slv->channels > clt->channels) {
460 int srcs = slv->channels / clt->channels;
461 if (s < slv->channels % clt->channels)
462 srcs++;
463 v /= srcs;
464 }
465 }
466 ttable[c * tt_ssize + s] = v;
467 if (++c == clt->channels)
468 c = 0;
469 if (++s == slv->channels)
470 s = 0;
471 }
472 break;
473 case PLUG_ROUTE_POLICY_COPY:
474 if (clt->channels < slv->channels) {
475 n = clt->channels;
476 } else {
477 n = slv->channels;
478 }
479 for (c = 0; (int)c < n; c++)
480 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
481 break;
482 default:
483 SNDERR("Invalid route policy");
484 break;
485 }
486 }
487 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);
488 if (err < 0)
489 return err;
490 slv->channels = clt->channels;
491 slv->access = clt->access;
492 if (snd_pcm_format_linear(clt->format))
493 slv->format = clt->format;
494 return 1;
495 }
496 #endif
497
498 #ifdef BUILD_PCM_PLUGIN_IEC958
iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)499 static int iec958_open(snd_pcm_t **pcmp, const char *name,
500 snd_pcm_format_t sformat, snd_pcm_t *slave,
501 int close_slave)
502 {
503 unsigned char preamble_vals[3] = {
504 0x08, 0x02, 0x04 /* Z, X, Y */
505 };
506 return snd_pcm_iec958_open(pcmp, name, sformat, slave, close_slave, NULL, preamble_vals, 0);
507 }
508 #endif
509
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)510 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)
511 {
512 snd_pcm_plug_t *plug = pcm->private_data;
513 int err;
514 snd_pcm_format_t cfmt;
515 int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
516
517 /* No conversion is needed */
518 if (clt->format == slv->format &&
519 clt->rate == slv->rate &&
520 clt->channels == slv->channels &&
521 (!plug->ttable || plug->ttable_ok))
522 return 0;
523
524 if (snd_pcm_format_linear(slv->format)) {
525 /* Conversion is done in another plugin */
526 if (clt->rate != slv->rate ||
527 clt->channels != slv->channels ||
528 (plug->ttable && !plug->ttable_ok))
529 return 0;
530 cfmt = clt->format;
531 switch (clt->format) {
532 #ifdef BUILD_PCM_PLUGIN_MULAW
533 case SND_PCM_FORMAT_MU_LAW:
534 f = snd_pcm_mulaw_open;
535 break;
536 #endif
537 #ifdef BUILD_PCM_PLUGIN_ALAW
538 case SND_PCM_FORMAT_A_LAW:
539 f = snd_pcm_alaw_open;
540 break;
541 #endif
542 #ifdef BUILD_PCM_PLUGIN_ADPCM
543 case SND_PCM_FORMAT_IMA_ADPCM:
544 f = snd_pcm_adpcm_open;
545 break;
546 #endif
547 #ifdef BUILD_PCM_PLUGIN_IEC958
548 case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
549 case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
550 f = iec958_open;
551 break;
552 #endif
553 default:
554 #ifdef BUILD_PCM_PLUGIN_LFLOAT
555 if (snd_pcm_format_float(clt->format))
556 f = snd_pcm_lfloat_open;
557
558 else
559 #endif
560 f = snd_pcm_linear_open;
561 break;
562 }
563 #ifdef BUILD_PCM_PLUGIN_LFLOAT
564 } else if (snd_pcm_format_float(slv->format)) {
565 if (snd_pcm_format_linear(clt->format)) {
566 cfmt = clt->format;
567 f = snd_pcm_lfloat_open;
568 } else if (clt->rate != slv->rate || clt->channels != slv->channels ||
569 (plug->ttable && !plug->ttable_ok)) {
570 cfmt = SND_PCM_FORMAT_S16;
571 f = snd_pcm_lfloat_open;
572 } else
573 return -EINVAL;
574 #endif
575 #ifdef BUILD_PCM_NONLINEAR
576 } else {
577 switch (slv->format) {
578 #ifdef BUILD_PCM_PLUGIN_MULAW
579 case SND_PCM_FORMAT_MU_LAW:
580 f = snd_pcm_mulaw_open;
581 break;
582 #endif
583 #ifdef BUILD_PCM_PLUGIN_ALAW
584 case SND_PCM_FORMAT_A_LAW:
585 f = snd_pcm_alaw_open;
586 break;
587 #endif
588 #ifdef BUILD_PCM_PLUGIN_ADPCM
589 case SND_PCM_FORMAT_IMA_ADPCM:
590 f = snd_pcm_adpcm_open;
591 break;
592 #endif
593 #ifdef BUILD_PCM_PLUGIN_IEC958
594 case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
595 case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
596 f = iec958_open;
597 break;
598 #endif
599 default:
600 return -EINVAL;
601 }
602 if (snd_pcm_format_linear(clt->format))
603 cfmt = clt->format;
604 else
605 cfmt = SND_PCM_FORMAT_S16;
606 #endif /* NONLINEAR */
607 }
608 err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
609 if (err < 0)
610 return err;
611 slv->format = cfmt;
612 slv->access = clt->access;
613 return 1;
614 }
615
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)616 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)
617 {
618 snd_pcm_plug_t *plug = pcm->private_data;
619 int err;
620 if (clt->access == slv->access)
621 return 0;
622 err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
623 if (err < 0)
624 return err;
625 slv->access = clt->access;
626 return 1;
627 }
628
629 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)630 static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
631 snd_pcm_plug_params_t *clt,
632 snd_pcm_plug_params_t *slv)
633 {
634 snd_pcm_plug_t *plug = pcm->private_data;
635 int err;
636
637 if (clt->access == slv->access)
638 return 0;
639
640 switch (slv->access) {
641 case SND_PCM_ACCESS_MMAP_INTERLEAVED:
642 case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
643 case SND_PCM_ACCESS_MMAP_COMPLEX:
644 return 0;
645 default:
646 break;
647 }
648
649 err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
650 plug->gen.slave != plug->req_slave);
651 if (err < 0)
652 return err;
653 switch (slv->access) {
654 case SND_PCM_ACCESS_RW_INTERLEAVED:
655 slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
656 break;
657 case SND_PCM_ACCESS_RW_NONINTERLEAVED:
658 slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
659 break;
660 default:
661 break;
662 }
663 return 1;
664 }
665 #endif
666
snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, snd_pcm_plug_params_t *client, snd_pcm_plug_params_t *slave)667 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
668 snd_pcm_plug_params_t *client,
669 snd_pcm_plug_params_t *slave)
670 {
671 snd_pcm_plug_t *plug = pcm->private_data;
672 static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
673 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
674 snd_pcm_plug_change_mmap,
675 #endif
676 snd_pcm_plug_change_format,
677 #ifdef BUILD_PCM_PLUGIN_ROUTE
678 snd_pcm_plug_change_channels,
679 #endif
680 #ifdef BUILD_PCM_PLUGIN_RATE
681 snd_pcm_plug_change_rate,
682 #endif
683 #ifdef BUILD_PCM_PLUGIN_ROUTE
684 snd_pcm_plug_change_channels,
685 #endif
686 snd_pcm_plug_change_format,
687 snd_pcm_plug_change_access
688 };
689 snd_pcm_plug_params_t p = *slave;
690 unsigned int k = 0;
691 plug->ttable_ok = 0;
692 while (client->format != p.format ||
693 client->channels != p.channels ||
694 client->rate != p.rate ||
695 client->access != p.access ||
696 (plug->ttable && !plug->ttable_ok)) {
697 snd_pcm_t *new;
698 int err;
699 if (k >= sizeof(funcs)/sizeof(*funcs)) {
700 snd_pcm_plug_clear(pcm);
701 return -EINVAL;
702 }
703 err = funcs[k](pcm, &new, client, &p);
704 if (err < 0) {
705 snd_pcm_plug_clear(pcm);
706 return err;
707 }
708 if (err) {
709 plug->gen.slave = new;
710 }
711 k++;
712 }
713 return 0;
714 }
715
snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)716 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
717 {
718 unsigned int rate_min, channels_max;
719 int err;
720
721 /* HACK: to avoid overflow in PARTBIT_RATE code */
722 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
723 if (err < 0)
724 return err;
725 if (rate_min < 4000) {
726 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
727 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
728 return -EINVAL;
729 }
730 /* HACK: to avoid overflow in PERIOD_SIZE code */
731 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
732 if (err < 0)
733 return err;
734 if (channels_max > 10000) {
735 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
736 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
737 return -EINVAL;
738 }
739 return 0;
740 }
741
snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)742 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
743 {
744 snd_pcm_plug_t *plug = pcm->private_data;
745 int err;
746
747 _snd_pcm_hw_params_any(sparams);
748 if (plug->sformat >= 0) {
749 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
750 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
751 }
752 if (plug->schannels > 0)
753 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
754 plug->schannels, 0);
755 if (plug->srate > 0)
756 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
757 plug->srate, 0, plug->srate + 1, -1);
758 /* reduce the available configurations */
759 err = snd_pcm_hw_refine(plug->req_slave, sparams);
760 if (err < 0)
761 return err;
762 return 0;
763 }
764
check_access_change(snd_pcm_hw_params_t *cparams, snd_pcm_hw_params_t *sparams)765 static int check_access_change(snd_pcm_hw_params_t *cparams,
766 snd_pcm_hw_params_t *sparams)
767 {
768 snd_pcm_access_mask_t *smask;
769 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
770 const snd_pcm_access_mask_t *cmask;
771 snd_pcm_access_mask_t mask;
772 #endif
773
774 smask = (snd_pcm_access_mask_t *)
775 snd_pcm_hw_param_get_mask(sparams,
776 SND_PCM_HW_PARAM_ACCESS);
777 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
778 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
779 snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
780 return 0; /* OK, we have mmap support */
781 #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
782 /* no mmap support - we need mmap emulation */
783
784 if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
785 !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
786 return -EINVAL; /* even no RW access? no way! */
787
788 cmask = (const snd_pcm_access_mask_t *)
789 snd_pcm_hw_param_get_mask(cparams,
790 SND_PCM_HW_PARAM_ACCESS);
791 snd_mask_none(&mask);
792 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
793 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
794 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
795 snd_pcm_access_mask_set(&mask,
796 SND_PCM_ACCESS_RW_INTERLEAVED);
797 }
798 if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
799 snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
800 if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
801 snd_pcm_access_mask_set(&mask,
802 SND_PCM_ACCESS_RW_NONINTERLEAVED);
803 }
804 if (!snd_mask_empty(&mask))
805 *smask = mask; /* prefer the straight conversion */
806 return 0;
807 #else
808 return -EINVAL;
809 #endif
810 }
811
snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)812 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
813 snd_pcm_hw_params_t *sparams)
814 {
815 snd_pcm_plug_t *plug = pcm->private_data;
816 snd_pcm_t *slave = plug->req_slave;
817 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
818 SND_PCM_HW_PARBIT_TICK_TIME);
819 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
820 snd_pcm_format_mask_t sfmt_mask;
821 int err;
822 snd_pcm_format_t format;
823 snd_interval_t t, buffer_size;
824 const snd_interval_t *srate, *crate;
825
826 if (plug->srate == -2 ||
827 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
828 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
829 links |= SND_PCM_HW_PARBIT_RATE;
830 else {
831 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
832 if (err < 0)
833 return err;
834 }
835
836 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
837 links |= SND_PCM_HW_PARBIT_CHANNELS;
838 else {
839 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
840 if (err < 0)
841 return err;
842 }
843 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
844 links |= SND_PCM_HW_PARBIT_FORMAT;
845 else {
846 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
847 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
848 snd_mask_none(&sfmt_mask);
849 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
850 snd_pcm_format_t f;
851 if (!snd_pcm_format_mask_test(format_mask, format))
852 continue;
853 if (snd_pcm_format_mask_test(sformat_mask, format))
854 f = format;
855 else {
856 f = snd_pcm_plug_slave_format(format, sformat_mask);
857 if (f == SND_PCM_FORMAT_UNKNOWN)
858 continue;
859 }
860 snd_pcm_format_mask_set(&sfmt_mask, f);
861 }
862
863 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
864 SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
865 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
866 if (!snd_pcm_format_mask_test(format_mask, format))
867 continue;
868 SNDERR("Format: %s", snd_pcm_format_name(format));
869 }
870 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
871 if (!snd_pcm_format_mask_test(sformat_mask, format))
872 continue;
873 SNDERR("Slave format: %s", snd_pcm_format_name(format));
874 }
875 return -EINVAL;
876 }
877 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
878 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
879 if (err < 0)
880 return -EINVAL;
881 }
882
883 if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
884 err = check_access_change(params, sparams);
885 if (err < 0) {
886 SNDERR("Unable to find an usable access for '%s'",
887 pcm->name);
888 return err;
889 }
890 }
891
892 if ((links & SND_PCM_HW_PARBIT_RATE) ||
893 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
894 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
895 SND_PCM_HW_PARBIT_BUFFER_SIZE);
896 else {
897 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
898 snd_interval_unfloor(&buffer_size);
899 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
900 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
901 snd_interval_muldiv(&buffer_size, srate, crate, &t);
902 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
903 if (err < 0)
904 return err;
905 }
906 err = _snd_pcm_hw_params_refine(sparams, links, params);
907 if (err < 0)
908 return err;
909 return 0;
910 }
911
snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)912 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
913 snd_pcm_hw_params_t *params,
914 snd_pcm_hw_params_t *sparams)
915 {
916 snd_pcm_plug_t *plug = pcm->private_data;
917 unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
918 SND_PCM_HW_PARBIT_TICK_TIME);
919 const snd_pcm_format_mask_t *format_mask, *sformat_mask;
920 snd_pcm_format_mask_t fmt_mask;
921 int err;
922 snd_pcm_format_t format;
923 snd_interval_t t;
924 const snd_interval_t *sbuffer_size;
925 const snd_interval_t *srate, *crate;
926
927 if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
928 links |= SND_PCM_HW_PARBIT_CHANNELS;
929
930 if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
931 links |= SND_PCM_HW_PARBIT_FORMAT;
932 else {
933 format_mask = snd_pcm_hw_param_get_mask(params,
934 SND_PCM_HW_PARAM_FORMAT);
935 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
936 SND_PCM_HW_PARAM_FORMAT);
937 snd_mask_none(&fmt_mask);
938 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
939 snd_pcm_format_t f;
940 if (!snd_pcm_format_mask_test(format_mask, format))
941 continue;
942 if (snd_pcm_format_mask_test(sformat_mask, format))
943 f = format;
944 else {
945 f = snd_pcm_plug_slave_format(format, sformat_mask);
946 if (f == SND_PCM_FORMAT_UNKNOWN)
947 continue;
948 }
949 snd_pcm_format_mask_set(&fmt_mask, format);
950 }
951
952 if (snd_pcm_format_mask_empty(&fmt_mask)) {
953 SNDERR("Unable to find an usable client format");
954 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
955 if (!snd_pcm_format_mask_test(format_mask, format))
956 continue;
957 SNDERR("Format: %s", snd_pcm_format_name(format));
958 }
959 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
960 if (!snd_pcm_format_mask_test(sformat_mask, format))
961 continue;
962 SNDERR("Slave format: %s", snd_pcm_format_name(format));
963 }
964 return -EINVAL;
965 }
966
967 err = _snd_pcm_hw_param_set_mask(params,
968 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
969 if (err < 0)
970 return err;
971 }
972
973 if (plug->srate == -2 ||
974 (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
975 (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
976 links |= SND_PCM_HW_PARBIT_RATE;
977 else {
978 unsigned int rate_min, srate_min;
979 int rate_mindir, srate_mindir;
980
981 /* This is a temporary hack, waiting for a better solution */
982 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
983 if (err < 0)
984 return err;
985 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
986 if (err < 0)
987 return err;
988 if (rate_min == srate_min && srate_mindir > rate_mindir) {
989 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
990 if (err < 0)
991 return err;
992 }
993 }
994 if ((links & SND_PCM_HW_PARBIT_RATE) ||
995 snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
996 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
997 SND_PCM_HW_PARBIT_BUFFER_SIZE);
998 else {
999 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
1000 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
1001 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
1002 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
1003 snd_interval_floor(&t);
1004 if (snd_interval_empty(&t))
1005 return -EINVAL;
1006 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
1007 if (err < 0)
1008 return err;
1009 }
1010 err = _snd_pcm_hw_params_refine(params, links, sparams);
1011 if (err < 0)
1012 return err;
1013 /* FIXME */
1014 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
1015 return 0;
1016 }
1017
snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1018 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1019 {
1020 snd_pcm_plug_t *plug = pcm->private_data;
1021 return snd_pcm_hw_refine(plug->req_slave, params);
1022 }
1023
snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1024 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1025 {
1026 return snd_pcm_hw_refine_slave(pcm, params,
1027 snd_pcm_plug_hw_refine_cprepare,
1028 snd_pcm_plug_hw_refine_cchange,
1029 snd_pcm_plug_hw_refine_sprepare,
1030 snd_pcm_plug_hw_refine_schange,
1031 snd_pcm_plug_hw_refine_slave);
1032 }
1033
snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1034 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1035 {
1036 snd_pcm_plug_t *plug = pcm->private_data;
1037 snd_pcm_t *slave = plug->req_slave;
1038 snd_pcm_plug_params_t clt_params, slv_params;
1039 snd_pcm_hw_params_t sparams;
1040 int err;
1041
1042 err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
1043 if (err < 0)
1044 return err;
1045 err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
1046 if (err < 0)
1047 return err;
1048 err = snd_pcm_hw_refine_soft(slave, &sparams);
1049 if (err < 0)
1050 return err;
1051
1052 INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
1053 INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
1054 INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
1055 INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
1056
1057 INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
1058 INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
1059 INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
1060 snd_pcm_plug_clear(pcm);
1061 if (!(clt_params.format == slv_params.format &&
1062 clt_params.channels == slv_params.channels &&
1063 clt_params.rate == slv_params.rate &&
1064 !plug->ttable &&
1065 snd_pcm_hw_params_test_access(slave, &sparams,
1066 clt_params.access) >= 0)) {
1067 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
1068 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
1069 if (err < 0)
1070 return err;
1071 }
1072 slave = plug->gen.slave;
1073 err = _snd_pcm_hw_params_internal(slave, params);
1074 if (err < 0) {
1075 snd_pcm_plug_clear(pcm);
1076 return err;
1077 }
1078 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
1079 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
1080
1081 pcm->fast_ops = slave->fast_ops;
1082 pcm->fast_op_arg = slave->fast_op_arg;
1083 snd_pcm_link_hw_ptr(pcm, slave);
1084 snd_pcm_link_appl_ptr(pcm, slave);
1085 return 0;
1086 }
1087
snd_pcm_plug_hw_free(snd_pcm_t *pcm)1088 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
1089 {
1090 snd_pcm_plug_t *plug = pcm->private_data;
1091 snd_pcm_t *slave = plug->gen.slave;
1092 int err = snd_pcm_hw_free(slave);
1093 snd_pcm_plug_clear(pcm);
1094 return err;
1095 }
1096
snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)1097 static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
1098 {
1099 snd_pcm_plug_t *plug = pcm->private_data;
1100 snd_pcm_t *slave = plug->gen.slave;
1101 int err = snd_pcm_sw_params(slave, params);
1102 if (err >= 0) {
1103 pcm->fast_ops = slave->fast_ops;
1104 pcm->fast_op_arg = slave->fast_op_arg;
1105 }
1106 return err;
1107 }
1108
snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)1109 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
1110 {
1111 snd_pcm_plug_t *plug = pcm->private_data;
1112 snd_output_printf(out, "Plug PCM: ");
1113 snd_pcm_dump(plug->gen.slave, out);
1114 }
1115
1116 static const snd_pcm_ops_t snd_pcm_plug_ops = {
1117 .close = snd_pcm_plug_close,
1118 .info = snd_pcm_plug_info,
1119 .hw_refine = snd_pcm_plug_hw_refine,
1120 .hw_params = snd_pcm_plug_hw_params,
1121 .hw_free = snd_pcm_plug_hw_free,
1122 .sw_params = snd_pcm_plug_sw_params,
1123 .channel_info = snd_pcm_generic_channel_info,
1124 .dump = snd_pcm_plug_dump,
1125 .nonblock = snd_pcm_generic_nonblock,
1126 .async = snd_pcm_generic_async,
1127 .mmap = snd_pcm_generic_mmap,
1128 .munmap = snd_pcm_generic_munmap,
1129 .query_chmaps = snd_pcm_generic_query_chmaps,
1130 .get_chmap = snd_pcm_generic_get_chmap,
1131 .set_chmap = snd_pcm_generic_set_chmap,
1132 };
1133
1134 /**
1135 * \brief Creates a new Plug PCM
1136 * \param pcmp Returns created PCM handle
1137 * \param name Name of PCM
1138 * \param sformat Slave (destination) format
1139 * \param slave Slave PCM handle
1140 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1141 * \retval zero on success otherwise a negative error code
1142 * \warning Using of this function might be dangerous in the sense
1143 * of compatibility reasons. The prototype might be freely
1144 * changed in future.
1145 */
snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, int schannels, int srate, const snd_config_t *rate_converter, enum snd_pcm_plug_route_policy route_policy, snd_pcm_route_ttable_entry_t *ttable, unsigned int tt_ssize, unsigned int tt_cused, unsigned int tt_sused, snd_pcm_t *slave, int close_slave)1146 int snd_pcm_plug_open(snd_pcm_t **pcmp,
1147 const char *name,
1148 snd_pcm_format_t sformat, int schannels, int srate,
1149 const snd_config_t *rate_converter,
1150 enum snd_pcm_plug_route_policy route_policy,
1151 snd_pcm_route_ttable_entry_t *ttable,
1152 unsigned int tt_ssize,
1153 unsigned int tt_cused, unsigned int tt_sused,
1154 snd_pcm_t *slave, int close_slave)
1155 {
1156 snd_pcm_t *pcm;
1157 snd_pcm_plug_t *plug;
1158 int err;
1159 assert(pcmp && slave);
1160
1161 plug = calloc(1, sizeof(snd_pcm_plug_t));
1162 if (!plug)
1163 return -ENOMEM;
1164 plug->sformat = sformat;
1165 plug->schannels = schannels;
1166 plug->srate = srate;
1167 plug->gen.slave = plug->req_slave = slave;
1168 plug->gen.close_slave = close_slave;
1169 plug->route_policy = route_policy;
1170 plug->ttable = ttable;
1171 plug->tt_ssize = tt_ssize;
1172 plug->tt_cused = tt_cused;
1173 plug->tt_sused = tt_sused;
1174
1175 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1176 if (err < 0) {
1177 free(plug);
1178 return err;
1179 }
1180 pcm->ops = &snd_pcm_plug_ops;
1181 pcm->fast_ops = slave->fast_ops;
1182 pcm->fast_op_arg = slave->fast_op_arg;
1183 if (rate_converter) {
1184 err = snd_config_copy(&plug->rate_converter,
1185 (snd_config_t *)rate_converter);
1186 if (err < 0) {
1187 snd_pcm_free(pcm);
1188 free(plug);
1189 return err;
1190 }
1191 }
1192 pcm->private_data = plug;
1193 pcm->poll_fd = slave->poll_fd;
1194 pcm->poll_events = slave->poll_events;
1195 pcm->mmap_shadow = 1;
1196 pcm->tstamp_type = slave->tstamp_type;
1197 snd_pcm_link_hw_ptr(pcm, slave);
1198 snd_pcm_link_appl_ptr(pcm, slave);
1199 *pcmp = pcm;
1200
1201 return 0;
1202 }
1203
1204 /*! \page pcm_plugins
1205
1206 \section pcm_plugins_plug Automatic conversion plugin
1207
1208 This plugin converts channels, rate and format on request.
1209
1210 \code
1211 pcm.name {
1212 type plug # Automatic conversion PCM
1213 slave STR # Slave name
1214 # or
1215 slave { # Slave definition
1216 pcm STR # Slave PCM name
1217 # or
1218 pcm { } # Slave PCM definition
1219 [format STR] # Slave format (default nearest) or "unchanged"
1220 [channels INT] # Slave channels (default nearest) or "unchanged"
1221 [rate INT] # Slave rate (default nearest) or "unchanged"
1222 }
1223 route_policy STR # route policy for automatic ttable generation
1224 # STR can be 'default', 'average', 'copy', 'duplicate'
1225 # average: result is average of input channels
1226 # copy: only first channels are copied to destination
1227 # duplicate: duplicate first set of channels
1228 # default: copy policy, except for mono capture - sum
1229 ttable { # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1230 CCHANNEL {
1231 SCHANNEL REAL # route value (0.0 - 1.0)
1232 }
1233 }
1234 rate_converter STR # type of rate converter
1235 # or
1236 rate_converter [ STR1 STR2 ... ]
1237 # type of rate converter
1238 # default value is taken from defaults.pcm.rate_converter
1239 }
1240 \endcode
1241
1242 \subsection pcm_plugins_plug_funcref Function reference
1243
1244 <UL>
1245 <LI>snd_pcm_plug_open()
1246 <LI>_snd_pcm_plug_open()
1247 </UL>
1248
1249 */
1250
1251 /**
1252 * \brief Creates a new Plug PCM
1253 * \param pcmp Returns created PCM handle
1254 * \param name Name of PCM
1255 * \param root Root configuration node
1256 * \param conf Configuration node with Plug PCM description
1257 * \param stream Stream type
1258 * \param mode Stream mode
1259 * \retval zero on success otherwise a negative error code
1260 * \warning Using of this function might be dangerous in the sense
1261 * of compatibility reasons. The prototype might be freely
1262 * changed in future.
1263 */
_snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode)1264 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1265 snd_config_t *root, snd_config_t *conf,
1266 snd_pcm_stream_t stream, int mode)
1267 {
1268 snd_config_iterator_t i, next;
1269 int err;
1270 snd_pcm_t *spcm;
1271 snd_config_t *slave = NULL, *sconf;
1272 snd_config_t *tt = NULL;
1273 enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1274 snd_pcm_route_ttable_entry_t *ttable = NULL;
1275 unsigned int csize, ssize;
1276 unsigned int cused, sused;
1277 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1278 int schannels = -1, srate = -1;
1279 const snd_config_t *rate_converter = NULL;
1280
1281 snd_config_for_each(i, next, conf) {
1282 snd_config_t *n = snd_config_iterator_entry(i);
1283 const char *id;
1284 if (snd_config_get_id(n, &id) < 0)
1285 continue;
1286 if (snd_pcm_conf_generic_id(id))
1287 continue;
1288 if (strcmp(id, "slave") == 0) {
1289 slave = n;
1290 continue;
1291 }
1292 #ifdef BUILD_PCM_PLUGIN_ROUTE
1293 if (strcmp(id, "ttable") == 0) {
1294 route_policy = PLUG_ROUTE_POLICY_NONE;
1295 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1296 SNDERR("Invalid type for %s", id);
1297 return -EINVAL;
1298 }
1299 tt = n;
1300 continue;
1301 }
1302 if (strcmp(id, "route_policy") == 0) {
1303 const char *str;
1304 if ((err = snd_config_get_string(n, &str)) < 0) {
1305 SNDERR("Invalid type for %s", id);
1306 return -EINVAL;
1307 }
1308 if (tt != NULL)
1309 SNDERR("Table is defined, route policy is ignored");
1310 if (!strcmp(str, "default"))
1311 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1312 else if (!strcmp(str, "average"))
1313 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1314 else if (!strcmp(str, "copy"))
1315 route_policy = PLUG_ROUTE_POLICY_COPY;
1316 else if (!strcmp(str, "duplicate"))
1317 route_policy = PLUG_ROUTE_POLICY_DUP;
1318 continue;
1319 }
1320 #endif
1321 #ifdef BUILD_PCM_PLUGIN_RATE
1322 if (strcmp(id, "rate_converter") == 0) {
1323 rate_converter = n;
1324 continue;
1325 }
1326 #endif
1327 SNDERR("Unknown field %s", id);
1328 return -EINVAL;
1329 }
1330 if (!slave) {
1331 SNDERR("slave is not defined");
1332 return -EINVAL;
1333 }
1334 err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1335 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1336 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1337 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1338 if (err < 0)
1339 return err;
1340 #ifdef BUILD_PCM_PLUGIN_ROUTE
1341 if (tt) {
1342 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1343 if (err < 0) {
1344 snd_config_delete(sconf);
1345 return err;
1346 }
1347 ttable = malloc(csize * ssize * sizeof(*ttable));
1348 if (ttable == NULL) {
1349 snd_config_delete(sconf);
1350 return err;
1351 }
1352 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1353 if (err < 0) {
1354 snd_config_delete(sconf);
1355 return err;
1356 }
1357 }
1358 #endif
1359
1360 #ifdef BUILD_PCM_PLUGIN_RATE
1361 if (! rate_converter)
1362 rate_converter = snd_pcm_rate_get_default_converter(root);
1363 #endif
1364
1365 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1366 snd_config_delete(sconf);
1367 if (err < 0)
1368 return err;
1369 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
1370 route_policy, ttable, ssize, cused, sused, spcm, 1);
1371 if (err < 0)
1372 snd_pcm_close(spcm);
1373 return err;
1374 }
1375 #ifndef DOC_HIDDEN
1376 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1377 #endif
1378