1 /* 2 * PCM Plug-In shared (kernel/library) code 3 * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> 4 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 5 * 6 * 7 * This library is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU Library General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23 #if 0 24 #define PLUGIN_DEBUG 25 #endif 26 27 #include <linux/slab.h> 28 #include <linux/time.h> 29 #include <linux/vmalloc.h> 30 #include <sound/core.h> 31 #include <sound/pcm.h> 32 #include <sound/pcm_params.h> 33 #include "pcm_plugin.h" 34 35 #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) 36 #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) 37 38 /* 39 * because some cards might have rates "very close", we ignore 40 * all "resampling" requests within +-5% 41 */ 42 static int rate_match(unsigned int src_rate, unsigned int dst_rate) 43 { 44 unsigned int low = (src_rate * 95) / 100; 45 unsigned int high = (src_rate * 105) / 100; 46 return dst_rate >= low && dst_rate <= high; 47 } 48 49 static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames) 50 { 51 struct snd_pcm_plugin_format *format; 52 ssize_t width; 53 size_t size; 54 unsigned int channel; 55 struct snd_pcm_plugin_channel *c; 56 57 if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { 58 format = &plugin->src_format; 59 } else { 60 format = &plugin->dst_format; 61 } 62 if ((width = snd_pcm_format_physical_width(format->format)) < 0) 63 return width; 64 size = frames * format->channels * width; 65 snd_assert((size % 8) == 0, return -ENXIO); 66 size /= 8; 67 if (plugin->buf_frames < frames) { 68 vfree(plugin->buf); 69 plugin->buf = vmalloc(size); 70 plugin->buf_frames = frames; 71 } 72 if (!plugin->buf) { 73 plugin->buf_frames = 0; 74 return -ENOMEM; 75 } 76 c = plugin->buf_channels; 77 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { 78 for (channel = 0; channel < format->channels; channel++, c++) { 79 c->frames = frames; 80 c->enabled = 1; 81 c->wanted = 0; 82 c->area.addr = plugin->buf; 83 c->area.first = channel * width; 84 c->area.step = format->channels * width; 85 } 86 } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { 87 snd_assert((size % format->channels) == 0,); 88 size /= format->channels; 89 for (channel = 0; channel < format->channels; channel++, c++) { 90 c->frames = frames; 91 c->enabled = 1; 92 c->wanted = 0; 93 c->area.addr = plugin->buf + (channel * size); 94 c->area.first = 0; 95 c->area.step = width; 96 } 97 } else 98 return -EINVAL; 99 return 0; 100 } 101 102 int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) 103 { 104 int err; 105 snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); 106 if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { 107 struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug); 108 while (plugin->next) { 109 if (plugin->dst_frames) 110 frames = plugin->dst_frames(plugin, frames); 111 snd_assert(frames > 0, return -ENXIO); 112 plugin = plugin->next; 113 err = snd_pcm_plugin_alloc(plugin, frames); 114 if (err < 0) 115 return err; 116 } 117 } else { 118 struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug); 119 while (plugin->prev) { 120 if (plugin->src_frames) 121 frames = plugin->src_frames(plugin, frames); 122 snd_assert(frames > 0, return -ENXIO); 123 plugin = plugin->prev; 124 err = snd_pcm_plugin_alloc(plugin, frames); 125 if (err < 0) 126 return err; 127 } 128 } 129 return 0; 130 } 131 132 133 snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, 134 snd_pcm_uframes_t frames, 135 struct snd_pcm_plugin_channel **channels) 136 { 137 *channels = plugin->buf_channels; 138 return frames; 139 } 140 141 int snd_pcm_plugin_build(struct snd_pcm_substream *plug, 142 const char *name, 143 struct snd_pcm_plugin_format *src_format, 144 struct snd_pcm_plugin_format *dst_format, 145 size_t extra, 146 struct snd_pcm_plugin **ret) 147 { 148 struct snd_pcm_plugin *plugin; 149 unsigned int channels; 150 151 snd_assert(plug != NULL, return -ENXIO); 152 snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); 153 plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL); 154 if (plugin == NULL) 155 return -ENOMEM; 156 plugin->name = name; 157 plugin->plug = plug; 158 plugin->stream = snd_pcm_plug_stream(plug); 159 plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 160 plugin->src_format = *src_format; 161 plugin->src_width = snd_pcm_format_physical_width(src_format->format); 162 snd_assert(plugin->src_width > 0, ); 163 plugin->dst_format = *dst_format; 164 plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); 165 snd_assert(plugin->dst_width > 0, ); 166 if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) 167 channels = src_format->channels; 168 else 169 channels = dst_format->channels; 170 plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); 171 if (plugin->buf_channels == NULL) { 172 snd_pcm_plugin_free(plugin); 173 return -ENOMEM; 174 } 175 plugin->client_channels = snd_pcm_plugin_client_channels; 176 *ret = plugin; 177 return 0; 178 } 179 180 int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin) 181 { 182 if (! plugin) 183 return 0; 184 if (plugin->private_free) 185 plugin->private_free(plugin); 186 kfree(plugin->buf_channels); 187 vfree(plugin->buf); 188 kfree(plugin); 189 return 0; 190 } 191 192 snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) 193 { 194 struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; 195 int stream = snd_pcm_plug_stream(plug); 196 197 snd_assert(plug != NULL, return -ENXIO); 198 if (drv_frames == 0) 199 return 0; 200 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 201 plugin = snd_pcm_plug_last(plug); 202 while (plugin && drv_frames > 0) { 203 plugin_prev = plugin->prev; 204 if (plugin->src_frames) 205 drv_frames = plugin->src_frames(plugin, drv_frames); 206 plugin = plugin_prev; 207 } 208 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { 209 plugin = snd_pcm_plug_first(plug); 210 while (plugin && drv_frames > 0) { 211 plugin_next = plugin->next; 212 if (plugin->dst_frames) 213 drv_frames = plugin->dst_frames(plugin, drv_frames); 214 plugin = plugin_next; 215 } 216 } else 217 snd_BUG(); 218 return drv_frames; 219 } 220 221 snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames) 222 { 223 struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; 224 snd_pcm_sframes_t frames; 225 int stream = snd_pcm_plug_stream(plug); 226 227 snd_assert(plug != NULL, return -ENXIO); 228 if (clt_frames == 0) 229 return 0; 230 frames = clt_frames; 231 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 232 plugin = snd_pcm_plug_first(plug); 233 while (plugin && frames > 0) { 234 plugin_next = plugin->next; 235 if (plugin->dst_frames) { 236 frames = plugin->dst_frames(plugin, frames); 237 if (frames < 0) 238 return frames; 239 } 240 plugin = plugin_next; 241 } 242 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { 243 plugin = snd_pcm_plug_last(plug); 244 while (plugin) { 245 plugin_prev = plugin->prev; 246 if (plugin->src_frames) { 247 frames = plugin->src_frames(plugin, frames); 248 if (frames < 0) 249 return frames; 250 } 251 plugin = plugin_prev; 252 } 253 } else 254 snd_BUG(); 255 return frames; 256 } 257 258 static int snd_pcm_plug_formats(struct snd_mask *mask, int format) 259 { 260 struct snd_mask formats = *mask; 261 u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 262 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | 263 SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | 264 SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | 265 SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | 266 SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE | 267 SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | 268 SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | 269 SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); 270 snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); 271 272 if (formats.bits[0] & (u32)linfmts) 273 formats.bits[0] |= (u32)linfmts; 274 if (formats.bits[1] & (u32)(linfmts >> 32)) 275 formats.bits[1] |= (u32)(linfmts >> 32); 276 return snd_mask_test(&formats, format); 277 } 278 279 static int preferred_formats[] = { 280 SNDRV_PCM_FORMAT_S16_LE, 281 SNDRV_PCM_FORMAT_S16_BE, 282 SNDRV_PCM_FORMAT_U16_LE, 283 SNDRV_PCM_FORMAT_U16_BE, 284 SNDRV_PCM_FORMAT_S24_3LE, 285 SNDRV_PCM_FORMAT_S24_3BE, 286 SNDRV_PCM_FORMAT_U24_3LE, 287 SNDRV_PCM_FORMAT_U24_3BE, 288 SNDRV_PCM_FORMAT_S24_LE, 289 SNDRV_PCM_FORMAT_S24_BE, 290 SNDRV_PCM_FORMAT_U24_LE, 291 SNDRV_PCM_FORMAT_U24_BE, 292 SNDRV_PCM_FORMAT_S32_LE, 293 SNDRV_PCM_FORMAT_S32_BE, 294 SNDRV_PCM_FORMAT_U32_LE, 295 SNDRV_PCM_FORMAT_U32_BE, 296 SNDRV_PCM_FORMAT_S8, 297 SNDRV_PCM_FORMAT_U8 298 }; 299 300 int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) 301 { 302 int i; 303 304 if (snd_mask_test(format_mask, format)) 305 return format; 306 if (! snd_pcm_plug_formats(format_mask, format)) 307 return -EINVAL; 308 if (snd_pcm_format_linear(format)) { 309 unsigned int width = snd_pcm_format_width(format); 310 int unsignd = snd_pcm_format_unsigned(format) > 0; 311 int big = snd_pcm_format_big_endian(format) > 0; 312 unsigned int badness, best = -1; 313 int best_format = -1; 314 for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { 315 int f = preferred_formats[i]; 316 unsigned int w; 317 if (!snd_mask_test(format_mask, f)) 318 continue; 319 w = snd_pcm_format_width(f); 320 if (w >= width) 321 badness = w - width; 322 else 323 badness = width - w + 32; 324 badness += snd_pcm_format_unsigned(f) != unsignd; 325 badness += snd_pcm_format_big_endian(f) != big; 326 if (badness < best) { 327 best_format = f; 328 best = badness; 329 } 330 } 331 return best_format >= 0 ? best_format : -EINVAL; 332 } else { 333 switch (format) { 334 case SNDRV_PCM_FORMAT_MU_LAW: 335 for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { 336 int format1 = preferred_formats[i]; 337 if (snd_mask_test(format_mask, format1)) 338 return format1; 339 } 340 default: 341 return -EINVAL; 342 } 343 } 344 } 345 346 int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, 347 struct snd_pcm_hw_params *params, 348 struct snd_pcm_hw_params *slave_params) 349 { 350 struct snd_pcm_plugin_format tmpformat; 351 struct snd_pcm_plugin_format dstformat; 352 struct snd_pcm_plugin_format srcformat; 353 int src_access, dst_access; 354 struct snd_pcm_plugin *plugin = NULL; 355 int err; 356 int stream = snd_pcm_plug_stream(plug); 357 int slave_interleaved = (params_channels(slave_params) == 1 || 358 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); 359 360 switch (stream) { 361 case SNDRV_PCM_STREAM_PLAYBACK: 362 dstformat.format = params_format(slave_params); 363 dstformat.rate = params_rate(slave_params); 364 dstformat.channels = params_channels(slave_params); 365 srcformat.format = params_format(params); 366 srcformat.rate = params_rate(params); 367 srcformat.channels = params_channels(params); 368 src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 369 dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : 370 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); 371 break; 372 case SNDRV_PCM_STREAM_CAPTURE: 373 dstformat.format = params_format(params); 374 dstformat.rate = params_rate(params); 375 dstformat.channels = params_channels(params); 376 srcformat.format = params_format(slave_params); 377 srcformat.rate = params_rate(slave_params); 378 srcformat.channels = params_channels(slave_params); 379 src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : 380 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); 381 dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 382 break; 383 default: 384 snd_BUG(); 385 return -EINVAL; 386 } 387 tmpformat = srcformat; 388 389 pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 390 srcformat.format, 391 srcformat.rate, 392 srcformat.channels); 393 pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 394 dstformat.format, 395 dstformat.rate, 396 dstformat.channels); 397 398 /* Format change (linearization) */ 399 if (! rate_match(srcformat.rate, dstformat.rate) && 400 ! snd_pcm_format_linear(srcformat.format)) { 401 if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW) 402 return -EINVAL; 403 tmpformat.format = SNDRV_PCM_FORMAT_S16; 404 err = snd_pcm_plugin_build_mulaw(plug, 405 &srcformat, &tmpformat, 406 &plugin); 407 if (err < 0) 408 return err; 409 err = snd_pcm_plugin_append(plugin); 410 if (err < 0) { 411 snd_pcm_plugin_free(plugin); 412 return err; 413 } 414 srcformat = tmpformat; 415 src_access = dst_access; 416 } 417 418 /* channels reduction */ 419 if (srcformat.channels > dstformat.channels) { 420 tmpformat.channels = dstformat.channels; 421 err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); 422 pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); 423 if (err < 0) 424 return err; 425 err = snd_pcm_plugin_append(plugin); 426 if (err < 0) { 427 snd_pcm_plugin_free(plugin); 428 return err; 429 } 430 srcformat = tmpformat; 431 src_access = dst_access; 432 } 433 434 /* rate resampling */ 435 if (!rate_match(srcformat.rate, dstformat.rate)) { 436 if (srcformat.format != SNDRV_PCM_FORMAT_S16) { 437 /* convert to S16 for resampling */ 438 tmpformat.format = SNDRV_PCM_FORMAT_S16; 439 err = snd_pcm_plugin_build_linear(plug, 440 &srcformat, &tmpformat, 441 &plugin); 442 if (err < 0) 443 return err; 444 err = snd_pcm_plugin_append(plugin); 445 if (err < 0) { 446 snd_pcm_plugin_free(plugin); 447 return err; 448 } 449 srcformat = tmpformat; 450 src_access = dst_access; 451 } 452 tmpformat.rate = dstformat.rate; 453 err = snd_pcm_plugin_build_rate(plug, 454 &srcformat, &tmpformat, 455 &plugin); 456 pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); 457 if (err < 0) 458 return err; 459 err = snd_pcm_plugin_append(plugin); 460 if (err < 0) { 461 snd_pcm_plugin_free(plugin); 462 return err; 463 } 464 srcformat = tmpformat; 465 src_access = dst_access; 466 } 467 468 /* format change */ 469 if (srcformat.format != dstformat.format) { 470 tmpformat.format = dstformat.format; 471 if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW || 472 tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { 473 err = snd_pcm_plugin_build_mulaw(plug, 474 &srcformat, &tmpformat, 475 &plugin); 476 } 477 else if (snd_pcm_format_linear(srcformat.format) && 478 snd_pcm_format_linear(tmpformat.format)) { 479 err = snd_pcm_plugin_build_linear(plug, 480 &srcformat, &tmpformat, 481 &plugin); 482 } 483 else 484 return -EINVAL; 485 pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); 486 if (err < 0) 487 return err; 488 err = snd_pcm_plugin_append(plugin); 489 if (err < 0) { 490 snd_pcm_plugin_free(plugin); 491 return err; 492 } 493 srcformat = tmpformat; 494 src_access = dst_access; 495 } 496 497 /* channels extension */ 498 if (srcformat.channels < dstformat.channels) { 499 tmpformat.channels = dstformat.channels; 500 err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); 501 pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); 502 if (err < 0) 503 return err; 504 err = snd_pcm_plugin_append(plugin); 505 if (err < 0) { 506 snd_pcm_plugin_free(plugin); 507 return err; 508 } 509 srcformat = tmpformat; 510 src_access = dst_access; 511 } 512 513 /* de-interleave */ 514 if (src_access != dst_access) { 515 err = snd_pcm_plugin_build_copy(plug, 516 &srcformat, 517 &tmpformat, 518 &plugin); 519 pdprintf("interleave change (copy: returns %i)\n", err); 520 if (err < 0) 521 return err; 522 err = snd_pcm_plugin_append(plugin); 523 if (err < 0) { 524 snd_pcm_plugin_free(plugin); 525 return err; 526 } 527 } 528 529 return 0; 530 } 531 532 snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug, 533 char *buf, 534 snd_pcm_uframes_t count, 535 struct snd_pcm_plugin_channel **channels) 536 { 537 struct snd_pcm_plugin *plugin; 538 struct snd_pcm_plugin_channel *v; 539 struct snd_pcm_plugin_format *format; 540 int width, nchannels, channel; 541 int stream = snd_pcm_plug_stream(plug); 542 543 snd_assert(buf != NULL, return -ENXIO); 544 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 545 plugin = snd_pcm_plug_first(plug); 546 format = &plugin->src_format; 547 } else { 548 plugin = snd_pcm_plug_last(plug); 549 format = &plugin->dst_format; 550 } 551 v = plugin->buf_channels; 552 *channels = v; 553 if ((width = snd_pcm_format_physical_width(format->format)) < 0) 554 return width; 555 nchannels = format->channels; 556 snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); 557 for (channel = 0; channel < nchannels; channel++, v++) { 558 v->frames = count; 559 v->enabled = 1; 560 v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); 561 v->area.addr = buf; 562 v->area.first = channel * width; 563 v->area.step = nchannels * width; 564 } 565 return count; 566 } 567 568 snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *src_channels, snd_pcm_uframes_t size) 569 { 570 struct snd_pcm_plugin *plugin, *next; 571 struct snd_pcm_plugin_channel *dst_channels; 572 int err; 573 snd_pcm_sframes_t frames = size; 574 575 plugin = snd_pcm_plug_first(plug); 576 while (plugin && frames > 0) { 577 if ((next = plugin->next) != NULL) { 578 snd_pcm_sframes_t frames1 = frames; 579 if (plugin->dst_frames) 580 frames1 = plugin->dst_frames(plugin, frames); 581 if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { 582 return err; 583 } 584 if (err != frames1) { 585 frames = err; 586 if (plugin->src_frames) 587 frames = plugin->src_frames(plugin, frames1); 588 } 589 } else 590 dst_channels = NULL; 591 pdprintf("write plugin: %s, %li\n", plugin->name, frames); 592 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) 593 return frames; 594 src_channels = dst_channels; 595 plugin = next; 596 } 597 return snd_pcm_plug_client_size(plug, frames); 598 } 599 600 snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size) 601 { 602 struct snd_pcm_plugin *plugin, *next; 603 struct snd_pcm_plugin_channel *src_channels, *dst_channels; 604 snd_pcm_sframes_t frames = size; 605 int err; 606 607 frames = snd_pcm_plug_slave_size(plug, frames); 608 if (frames < 0) 609 return frames; 610 611 src_channels = NULL; 612 plugin = snd_pcm_plug_first(plug); 613 while (plugin && frames > 0) { 614 if ((next = plugin->next) != NULL) { 615 if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { 616 return err; 617 } 618 frames = err; 619 } else { 620 dst_channels = dst_channels_final; 621 } 622 pdprintf("read plugin: %s, %li\n", plugin->name, frames); 623 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) 624 return frames; 625 plugin = next; 626 src_channels = dst_channels; 627 } 628 return frames; 629 } 630 631 int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, 632 size_t samples, int format) 633 { 634 /* FIXME: sub byte resolution and odd dst_offset */ 635 unsigned char *dst; 636 unsigned int dst_step; 637 int width; 638 const unsigned char *silence; 639 if (!dst_area->addr) 640 return 0; 641 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; 642 width = snd_pcm_format_physical_width(format); 643 if (width <= 0) 644 return -EINVAL; 645 if (dst_area->step == (unsigned int) width && width >= 8) 646 return snd_pcm_format_set_silence(format, dst, samples); 647 silence = snd_pcm_format_silence_64(format); 648 if (! silence) 649 return -EINVAL; 650 dst_step = dst_area->step / 8; 651 if (width == 4) { 652 /* Ima ADPCM */ 653 int dstbit = dst_area->first % 8; 654 int dstbit_step = dst_area->step % 8; 655 while (samples-- > 0) { 656 if (dstbit) 657 *dst &= 0xf0; 658 else 659 *dst &= 0x0f; 660 dst += dst_step; 661 dstbit += dstbit_step; 662 if (dstbit == 8) { 663 dst++; 664 dstbit = 0; 665 } 666 } 667 } else { 668 width /= 8; 669 while (samples-- > 0) { 670 memcpy(dst, silence, width); 671 dst += dst_step; 672 } 673 } 674 return 0; 675 } 676 677 int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, 678 const struct snd_pcm_channel_area *dst_area, size_t dst_offset, 679 size_t samples, int format) 680 { 681 /* FIXME: sub byte resolution and odd dst_offset */ 682 char *src, *dst; 683 int width; 684 int src_step, dst_step; 685 src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; 686 if (!src_area->addr) 687 return snd_pcm_area_silence(dst_area, dst_offset, samples, format); 688 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; 689 if (!dst_area->addr) 690 return 0; 691 width = snd_pcm_format_physical_width(format); 692 if (width <= 0) 693 return -EINVAL; 694 if (src_area->step == (unsigned int) width && 695 dst_area->step == (unsigned int) width && width >= 8) { 696 size_t bytes = samples * width / 8; 697 memcpy(dst, src, bytes); 698 return 0; 699 } 700 src_step = src_area->step / 8; 701 dst_step = dst_area->step / 8; 702 if (width == 4) { 703 /* Ima ADPCM */ 704 int srcbit = src_area->first % 8; 705 int srcbit_step = src_area->step % 8; 706 int dstbit = dst_area->first % 8; 707 int dstbit_step = dst_area->step % 8; 708 while (samples-- > 0) { 709 unsigned char srcval; 710 if (srcbit) 711 srcval = *src & 0x0f; 712 else 713 srcval = (*src & 0xf0) >> 4; 714 if (dstbit) 715 *dst = (*dst & 0xf0) | srcval; 716 else 717 *dst = (*dst & 0x0f) | (srcval << 4); 718 src += src_step; 719 srcbit += srcbit_step; 720 if (srcbit == 8) { 721 src++; 722 srcbit = 0; 723 } 724 dst += dst_step; 725 dstbit += dstbit_step; 726 if (dstbit == 8) { 727 dst++; 728 dstbit = 0; 729 } 730 } 731 } else { 732 width /= 8; 733 while (samples-- > 0) { 734 memcpy(dst, src, width); 735 src += src_step; 736 dst += dst_step; 737 } 738 } 739 return 0; 740 } 741