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