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