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