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