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