1/* 2 * PCM Plug-In shared (kernel/library) code 3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.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 <sound/driver.h> 25 26#ifdef CONFIG_SND_PCM_OSS_PLUGINS 27 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 */ 43static 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 50static 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 103int 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 134snd_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 142int 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 181int 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 193snd_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 222snd_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 259static 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_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | 268 SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); 269 snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); 270 271 if (formats.bits[0] & (u32)linfmts) 272 formats.bits[0] |= (u32)linfmts; 273 if (formats.bits[1] & (u32)(linfmts >> 32)) 274 formats.bits[1] |= (u32)(linfmts >> 32); 275 return snd_mask_test(&formats, format); 276} 277 278static int preferred_formats[] = { 279 SNDRV_PCM_FORMAT_S16_LE, 280 SNDRV_PCM_FORMAT_S16_BE, 281 SNDRV_PCM_FORMAT_U16_LE, 282 SNDRV_PCM_FORMAT_U16_BE, 283 SNDRV_PCM_FORMAT_S24_LE, 284 SNDRV_PCM_FORMAT_S24_BE, 285 SNDRV_PCM_FORMAT_U24_LE, 286 SNDRV_PCM_FORMAT_U24_BE, 287 SNDRV_PCM_FORMAT_S32_LE, 288 SNDRV_PCM_FORMAT_S32_BE, 289 SNDRV_PCM_FORMAT_U32_LE, 290 SNDRV_PCM_FORMAT_U32_BE, 291 SNDRV_PCM_FORMAT_S8, 292 SNDRV_PCM_FORMAT_U8 293}; 294 295int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) 296{ 297 if (snd_mask_test(format_mask, format)) 298 return format; 299 if (! snd_pcm_plug_formats(format_mask, format)) 300 return -EINVAL; 301 if (snd_pcm_format_linear(format)) { 302 int width = snd_pcm_format_width(format); 303 int unsignd = snd_pcm_format_unsigned(format); 304 int big = snd_pcm_format_big_endian(format); 305 int format1; 306 int wid, width1=width; 307 int dwidth1 = 8; 308 for (wid = 0; wid < 4; ++wid) { 309 int end, big1 = big; 310 for (end = 0; end < 2; ++end) { 311 int sgn, unsignd1 = unsignd; 312 for (sgn = 0; sgn < 2; ++sgn) { 313 format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); 314 if (format1 >= 0 && 315 snd_mask_test(format_mask, format1)) 316 goto _found; 317 unsignd1 = !unsignd1; 318 } 319 big1 = !big1; 320 } 321 if (width1 == 32) { 322 dwidth1 = -dwidth1; 323 width1 = width; 324 } 325 width1 += dwidth1; 326 } 327 return -EINVAL; 328 _found: 329 return format1; 330 } else { 331 unsigned int i; 332 switch (format) { 333 case SNDRV_PCM_FORMAT_MU_LAW: 334 for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { 335 int format1 = preferred_formats[i]; 336 if (snd_mask_test(format_mask, format1)) 337 return format1; 338 } 339 default: 340 return -EINVAL; 341 } 342 } 343} 344 345int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, 346 struct snd_pcm_hw_params *params, 347 struct snd_pcm_hw_params *slave_params) 348{ 349 struct snd_pcm_plugin_format tmpformat; 350 struct snd_pcm_plugin_format dstformat; 351 struct snd_pcm_plugin_format srcformat; 352 int src_access, dst_access; 353 struct snd_pcm_plugin *plugin = NULL; 354 int err; 355 int stream = snd_pcm_plug_stream(plug); 356 int slave_interleaved = (params_channels(slave_params) == 1 || 357 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); 358 359 switch (stream) { 360 case SNDRV_PCM_STREAM_PLAYBACK: 361 dstformat.format = params_format(slave_params); 362 dstformat.rate = params_rate(slave_params); 363 dstformat.channels = params_channels(slave_params); 364 srcformat.format = params_format(params); 365 srcformat.rate = params_rate(params); 366 srcformat.channels = params_channels(params); 367 src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 368 dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : 369 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); 370 break; 371 case SNDRV_PCM_STREAM_CAPTURE: 372 dstformat.format = params_format(params); 373 dstformat.rate = params_rate(params); 374 dstformat.channels = params_channels(params); 375 srcformat.format = params_format(slave_params); 376 srcformat.rate = params_rate(slave_params); 377 srcformat.channels = params_channels(slave_params); 378 src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : 379 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); 380 dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 381 break; 382 default: 383 snd_BUG(); 384 return -EINVAL; 385 } 386 tmpformat = srcformat; 387 388 pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 389 srcformat.format, 390 srcformat.rate, 391 srcformat.channels); 392 pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 393 dstformat.format, 394 dstformat.rate, 395 dstformat.channels); 396 397 /* Format change (linearization) */ 398 if (! rate_match(srcformat.rate, dstformat.rate) && 399 ! snd_pcm_format_linear(srcformat.format)) { 400 if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW) 401 return -EINVAL; 402 tmpformat.format = SNDRV_PCM_FORMAT_S16; 403 err = snd_pcm_plugin_build_mulaw(plug, 404 &srcformat, &tmpformat, 405 &plugin); 406 if (err < 0) 407 return err; 408 err = snd_pcm_plugin_append(plugin); 409 if (err < 0) { 410 snd_pcm_plugin_free(plugin); 411 return err; 412 } 413 srcformat = tmpformat; 414 src_access = dst_access; 415 } 416 417 /* channels reduction */ 418 if (srcformat.channels > dstformat.channels) { 419 tmpformat.channels = dstformat.channels; 420 err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); 421 pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); 422 if (err < 0) 423 return err; 424 err = snd_pcm_plugin_append(plugin); 425 if (err < 0) { 426 snd_pcm_plugin_free(plugin); 427 return err; 428 } 429 srcformat = tmpformat; 430 src_access = dst_access; 431 } 432 433 /* rate resampling */ 434 if (!rate_match(srcformat.rate, dstformat.rate)) { 435 if (srcformat.format != SNDRV_PCM_FORMAT_S16) { 436 /* convert to S16 for resampling */ 437 tmpformat.format = SNDRV_PCM_FORMAT_S16; 438 err = snd_pcm_plugin_build_linear(plug, 439 &srcformat, &tmpformat, 440 &plugin); 441 if (err < 0) 442 return err; 443 err = snd_pcm_plugin_append(plugin); 444 if (err < 0) { 445 snd_pcm_plugin_free(plugin); 446 return err; 447 } 448 srcformat = tmpformat; 449 src_access = dst_access; 450 } 451 tmpformat.rate = dstformat.rate; 452 err = snd_pcm_plugin_build_rate(plug, 453 &srcformat, &tmpformat, 454 &plugin); 455 pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); 456 if (err < 0) 457 return err; 458 err = snd_pcm_plugin_append(plugin); 459 if (err < 0) { 460 snd_pcm_plugin_free(plugin); 461 return err; 462 } 463 srcformat = tmpformat; 464 src_access = dst_access; 465 } 466 467 /* format change */ 468 if (srcformat.format != dstformat.format) { 469 tmpformat.format = dstformat.format; 470 if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW || 471 tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { 472 err = snd_pcm_plugin_build_mulaw(plug, 473 &srcformat, &tmpformat, 474 &plugin); 475 } 476 else if (snd_pcm_format_linear(srcformat.format) && 477 snd_pcm_format_linear(tmpformat.format)) { 478 err = snd_pcm_plugin_build_linear(plug, 479 &srcformat, &tmpformat, 480 &plugin); 481 } 482 else 483 return -EINVAL; 484 pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); 485 if (err < 0) 486 return err; 487 err = snd_pcm_plugin_append(plugin); 488 if (err < 0) { 489 snd_pcm_plugin_free(plugin); 490 return err; 491 } 492 srcformat = tmpformat; 493 src_access = dst_access; 494 } 495 496 /* channels extension */ 497 if (srcformat.channels < dstformat.channels) { 498 tmpformat.channels = dstformat.channels; 499 err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); 500 pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); 501 if (err < 0) 502 return err; 503 err = snd_pcm_plugin_append(plugin); 504 if (err < 0) { 505 snd_pcm_plugin_free(plugin); 506 return err; 507 } 508 srcformat = tmpformat; 509 src_access = dst_access; 510 } 511 512 /* de-interleave */ 513 if (src_access != dst_access) { 514 err = snd_pcm_plugin_build_copy(plug, 515 &srcformat, 516 &tmpformat, 517 &plugin); 518 pdprintf("interleave change (copy: returns %i)\n", err); 519 if (err < 0) 520 return err; 521 err = snd_pcm_plugin_append(plugin); 522 if (err < 0) { 523 snd_pcm_plugin_free(plugin); 524 return err; 525 } 526 } 527 528 return 0; 529} 530 531snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug, 532 char *buf, 533 snd_pcm_uframes_t count, 534 struct snd_pcm_plugin_channel **channels) 535{ 536 struct snd_pcm_plugin *plugin; 537 struct snd_pcm_plugin_channel *v; 538 struct snd_pcm_plugin_format *format; 539 int width, nchannels, channel; 540 int stream = snd_pcm_plug_stream(plug); 541 542 snd_assert(buf != NULL, return -ENXIO); 543 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 544 plugin = snd_pcm_plug_first(plug); 545 format = &plugin->src_format; 546 } else { 547 plugin = snd_pcm_plug_last(plug); 548 format = &plugin->dst_format; 549 } 550 v = plugin->buf_channels; 551 *channels = v; 552 if ((width = snd_pcm_format_physical_width(format->format)) < 0) 553 return width; 554 nchannels = format->channels; 555 snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); 556 for (channel = 0; channel < nchannels; channel++, v++) { 557 v->frames = count; 558 v->enabled = 1; 559 v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); 560 v->area.addr = buf; 561 v->area.first = channel * width; 562 v->area.step = nchannels * width; 563 } 564 return count; 565} 566 567snd_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) 568{ 569 struct snd_pcm_plugin *plugin, *next; 570 struct snd_pcm_plugin_channel *dst_channels; 571 int err; 572 snd_pcm_sframes_t frames = size; 573 574 plugin = snd_pcm_plug_first(plug); 575 while (plugin && frames > 0) { 576 if ((next = plugin->next) != NULL) { 577 snd_pcm_sframes_t frames1 = frames; 578 if (plugin->dst_frames) 579 frames1 = plugin->dst_frames(plugin, frames); 580 if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { 581 return err; 582 } 583 if (err != frames1) { 584 frames = err; 585 if (plugin->src_frames) 586 frames = plugin->src_frames(plugin, frames1); 587 } 588 } else 589 dst_channels = NULL; 590 pdprintf("write plugin: %s, %li\n", plugin->name, frames); 591 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) 592 return frames; 593 src_channels = dst_channels; 594 plugin = next; 595 } 596 return snd_pcm_plug_client_size(plug, frames); 597} 598 599snd_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) 600{ 601 struct snd_pcm_plugin *plugin, *next; 602 struct snd_pcm_plugin_channel *src_channels, *dst_channels; 603 snd_pcm_sframes_t frames = size; 604 int err; 605 606 frames = snd_pcm_plug_slave_size(plug, frames); 607 if (frames < 0) 608 return frames; 609 610 src_channels = NULL; 611 plugin = snd_pcm_plug_first(plug); 612 while (plugin && frames > 0) { 613 if ((next = plugin->next) != NULL) { 614 if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { 615 return err; 616 } 617 frames = err; 618 } else { 619 dst_channels = dst_channels_final; 620 } 621 pdprintf("read plugin: %s, %li\n", plugin->name, frames); 622 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) 623 return frames; 624 plugin = next; 625 src_channels = dst_channels; 626 } 627 return frames; 628} 629 630int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, 631 size_t samples, int format) 632{ 633 unsigned char *dst; 634 unsigned int dst_step; 635 int width; 636 const unsigned char *silence; 637 if (!dst_area->addr) 638 return 0; 639 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; 640 width = snd_pcm_format_physical_width(format); 641 if (width <= 0) 642 return -EINVAL; 643 if (dst_area->step == (unsigned int) width && width >= 8) 644 return snd_pcm_format_set_silence(format, dst, samples); 645 silence = snd_pcm_format_silence_64(format); 646 if (! silence) 647 return -EINVAL; 648 dst_step = dst_area->step / 8; 649 if (width == 4) { 650 /* Ima ADPCM */ 651 int dstbit = dst_area->first % 8; 652 int dstbit_step = dst_area->step % 8; 653 while (samples-- > 0) { 654 if (dstbit) 655 *dst &= 0xf0; 656 else 657 *dst &= 0x0f; 658 dst += dst_step; 659 dstbit += dstbit_step; 660 if (dstbit == 8) { 661 dst++; 662 dstbit = 0; 663 } 664 } 665 } else { 666 width /= 8; 667 while (samples-- > 0) { 668 memcpy(dst, silence, width); 669 dst += dst_step; 670 } 671 } 672 return 0; 673} 674 675int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, 676 const struct snd_pcm_channel_area *dst_area, size_t dst_offset, 677 size_t samples, int format) 678{ 679 char *src, *dst; 680 int width; 681 int src_step, dst_step; 682 src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; 683 if (!src_area->addr) 684 return snd_pcm_area_silence(dst_area, dst_offset, samples, format); 685 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; 686 if (!dst_area->addr) 687 return 0; 688 width = snd_pcm_format_physical_width(format); 689 if (width <= 0) 690 return -EINVAL; 691 if (src_area->step == (unsigned int) width && 692 dst_area->step == (unsigned int) width && width >= 8) { 693 size_t bytes = samples * width / 8; 694 memcpy(dst, src, bytes); 695 return 0; 696 } 697 src_step = src_area->step / 8; 698 dst_step = dst_area->step / 8; 699 if (width == 4) { 700 /* Ima ADPCM */ 701 int srcbit = src_area->first % 8; 702 int srcbit_step = src_area->step % 8; 703 int dstbit = dst_area->first % 8; 704 int dstbit_step = dst_area->step % 8; 705 while (samples-- > 0) { 706 unsigned char srcval; 707 if (srcbit) 708 srcval = *src & 0x0f; 709 else 710 srcval = (*src & 0xf0) >> 4; 711 if (dstbit) 712 *dst = (*dst & 0xf0) | srcval; 713 else 714 *dst = (*dst & 0x0f) | (srcval << 4); 715 src += src_step; 716 srcbit += srcbit_step; 717 if (srcbit == 8) { 718 src++; 719 srcbit = 0; 720 } 721 dst += dst_step; 722 dstbit += dstbit_step; 723 if (dstbit == 8) { 724 dst++; 725 dstbit = 0; 726 } 727 } 728 } else { 729 width /= 8; 730 while (samples-- > 0) { 731 memcpy(dst, src, width); 732 src += src_step; 733 dst += dst_step; 734 } 735 } 736 return 0; 737} 738 739#endif 740