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