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