📄 pcm_plugin.c
字号:
/* * PCM Plug-In shared (kernel/library) code * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> * * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #if 0#define PLUGIN_DEBUG#endif#include <sound/driver.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/vmalloc.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include "pcm_plugin.h"#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, bitset_t *dst_vmask, bitset_t **src_vmask){ bitset_t *vmask = plugin->src_vmask; bitset_copy(vmask, dst_vmask, plugin->src_format.channels); *src_vmask = vmask; return 0;}static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, bitset_t *src_vmask, bitset_t **dst_vmask){ bitset_t *vmask = plugin->dst_vmask; bitset_copy(vmask, src_vmask, plugin->dst_format.channels); *dst_vmask = vmask; return 0;}/* * because some cards might have rates "very close", we ignore * all "resampling" requests within +-5% */static int rate_match(unsigned int src_rate, unsigned int dst_rate){ unsigned int low = (src_rate * 95) / 100; unsigned int high = (src_rate * 105) / 100; return dst_rate >= low && dst_rate <= high;}static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames){ snd_pcm_plugin_format_t *format; ssize_t width; size_t size; unsigned int channel; snd_pcm_plugin_channel_t *c; if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { format = &plugin->src_format; } else { format = &plugin->dst_format; } if ((width = snd_pcm_format_physical_width(format->format)) < 0) return width; size = frames * format->channels * width; snd_assert((size % 8) == 0, return -ENXIO); size /= 8; if (plugin->buf_frames < frames) { if (plugin->buf) vfree(plugin->buf); plugin->buf = vmalloc(size); plugin->buf_frames = frames; } if (!plugin->buf) { plugin->buf_frames = 0; return -ENOMEM; } c = plugin->buf_channels; if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { for (channel = 0; channel < format->channels; channel++, c++) { c->frames = frames; c->enabled = 1; c->wanted = 0; c->area.addr = plugin->buf; c->area.first = channel * width; c->area.step = format->channels * width; } } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { snd_assert((size % format->channels) == 0,); size /= format->channels; for (channel = 0; channel < format->channels; channel++, c++) { c->frames = frames; c->enabled = 1; c->wanted = 0; c->area.addr = plugin->buf + (channel * size); c->area.first = 0; c->area.step = width; } } else return -EINVAL; return 0;}int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames){ int err; snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); while (plugin->next) { if (plugin->dst_frames) frames = plugin->dst_frames(plugin, frames); snd_assert(frames > 0, return -ENXIO); plugin = plugin->next; err = snd_pcm_plugin_alloc(plugin, frames); if (err < 0) return err; } } else { snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); while (plugin->prev) { if (plugin->src_frames) frames = plugin->src_frames(plugin, frames); snd_assert(frames > 0, return -ENXIO); plugin = plugin->prev; err = snd_pcm_plugin_alloc(plugin, frames); if (err < 0) return err; } } return 0;}snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames, snd_pcm_plugin_channel_t **channels){ *channels = plugin->buf_channels; return frames;}int snd_pcm_plugin_build(snd_pcm_plug_t *plug, const char *name, snd_pcm_plugin_format_t *src_format, snd_pcm_plugin_format_t *dst_format, size_t extra, snd_pcm_plugin_t **ret){ snd_pcm_plugin_t *plugin; unsigned int channels; snd_assert(plug != NULL, return -ENXIO); snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL); if (plugin == NULL) return -ENOMEM; plugin->name = name; plugin->plug = plug; plugin->stream = snd_pcm_plug_stream(plug); plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; plugin->src_format = *src_format; plugin->src_width = snd_pcm_format_physical_width(src_format->format); snd_assert(plugin->src_width > 0, ); plugin->dst_format = *dst_format; plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); snd_assert(plugin->dst_width > 0, ); if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) channels = src_format->channels; else channels = dst_format->channels; plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL); if (plugin->buf_channels == NULL) { snd_pcm_plugin_free(plugin); return -ENOMEM; } plugin->src_vmask = bitset_alloc(src_format->channels); if (plugin->src_vmask == NULL) { snd_pcm_plugin_free(plugin); return -ENOMEM; } plugin->dst_vmask = bitset_alloc(dst_format->channels); if (plugin->dst_vmask == NULL) { snd_pcm_plugin_free(plugin); return -ENOMEM; } plugin->client_channels = snd_pcm_plugin_client_channels; plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; *ret = plugin; return 0;}int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin){ if (! plugin) return 0; if (plugin->private_free) plugin->private_free(plugin); if (plugin->buf_channels) kfree(plugin->buf_channels); if (plugin->buf) vfree(plugin->buf); if (plugin->src_vmask) kfree(plugin->src_vmask); if (plugin->dst_vmask) kfree(plugin->dst_vmask); kfree(plugin); return 0;}snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames){ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; int stream = snd_pcm_plug_stream(plug); snd_assert(plug != NULL, return -ENXIO); if (drv_frames == 0) return 0; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { plugin = snd_pcm_plug_last(plug); while (plugin && drv_frames > 0) { plugin_prev = plugin->prev; if (plugin->src_frames) drv_frames = plugin->src_frames(plugin, drv_frames); plugin = plugin_prev; } } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { plugin = snd_pcm_plug_first(plug); while (plugin && drv_frames > 0) { plugin_next = plugin->next; if (plugin->dst_frames) drv_frames = plugin->dst_frames(plugin, drv_frames); plugin = plugin_next; } } else snd_BUG(); return drv_frames;}snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames){ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; snd_pcm_sframes_t frames; int stream = snd_pcm_plug_stream(plug); snd_assert(plug != NULL, return -ENXIO); if (clt_frames == 0) return 0; frames = clt_frames; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { plugin = snd_pcm_plug_first(plug); while (plugin && frames > 0) { plugin_next = plugin->next; if (plugin->dst_frames) { frames = plugin->dst_frames(plugin, frames); if (frames < 0) return frames; } plugin = plugin_next; } } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { plugin = snd_pcm_plug_last(plug); while (plugin) { plugin_prev = plugin->prev; if (plugin->src_frames) { frames = plugin->src_frames(plugin, frames); if (frames < 0) return frames; } plugin = plugin_prev; } } else snd_BUG(); return frames;}static int snd_pcm_plug_formats(snd_mask_t *mask, int format){ snd_mask_t formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & (u32)linfmts) formats.bits[0] |= (u32)linfmts; if (formats.bits[1] & (u32)(linfmts >> 32)) formats.bits[1] |= (u32)(linfmts >> 32); return snd_mask_test(&formats, format);}static int preferred_formats[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE, SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE, SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE, SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE, SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE, SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_U8};int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask){ if (snd_mask_test(format_mask, format)) return format; if (! snd_pcm_plug_formats(format_mask, format)) return -EINVAL; if (snd_pcm_format_linear(format)) { int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format); int big = snd_pcm_format_big_endian(format); int format1; int wid, width1=width; int dwidth1 = 8; for (wid = 0; wid < 4; ++wid) { int end, big1 = big; for (end = 0; end < 2; ++end) { int sgn, unsignd1 = unsignd; for (sgn = 0; sgn < 2; ++sgn) { format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); if (format1 >= 0 && snd_mask_test(format_mask, format1)) goto _found; unsignd1 = !unsignd1; } big1 = !big1; } if (width1 == 32) { dwidth1 = -dwidth1; width1 = width; } width1 += dwidth1; } return -EINVAL; _found: return format1; } else { unsigned int i; switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { int format1 = preferred_formats[i]; if (snd_mask_test(format_mask, format1)) return format1; } default: return -EINVAL; } }}int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *slave_params){ snd_pcm_plugin_format_t tmpformat; snd_pcm_plugin_format_t dstformat; snd_pcm_plugin_format_t srcformat; int src_access, dst_access; snd_pcm_plugin_t *plugin = NULL; int err, first; int stream = snd_pcm_plug_stream(plug); int slave_interleaved = (params_channels(slave_params) == 1 || params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); switch (stream) { case SNDRV_PCM_STREAM_PLAYBACK: dstformat.format = params_format(slave_params); dstformat.rate = params_rate(slave_params); dstformat.channels = params_channels(slave_params); srcformat.format = params_format(params); srcformat.rate = params_rate(params); srcformat.channels = params_channels(params); src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); break; case SNDRV_PCM_STREAM_CAPTURE: dstformat.format = params_format(params); dstformat.rate = params_rate(params); dstformat.channels = params_channels(params); srcformat.format = params_format(slave_params); srcformat.rate = params_rate(slave_params); srcformat.channels = params_channels(slave_params); src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; break; default: snd_BUG(); return -EINVAL; } tmpformat = srcformat; pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", srcformat.format, srcformat.rate, srcformat.channels); pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", dstformat.format, dstformat.rate, dstformat.channels); /* Format change (linearization) */ if ((srcformat.format != dstformat.format || !rate_match(srcformat.rate, dstformat.rate) || srcformat.channels != dstformat.channels) && !snd_pcm_format_linear(srcformat.format)) { if (snd_pcm_format_linear(dstformat.format)) tmpformat.format = dstformat.format; else tmpformat.format = SNDRV_PCM_FORMAT_S16; first = plugin == NULL; switch (srcformat.format) { case SNDRV_PCM_FORMAT_MU_LAW: err = snd_pcm_plugin_build_mulaw(plug, &srcformat, &tmpformat, &plugin); break; default: return -EINVAL; } pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); if (err < 0) return err; err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; } srcformat = tmpformat; src_access = dst_access; } /* channels reduction */ if (srcformat.channels > dstformat.channels) { int sv = srcformat.channels; int dv = dstformat.channels; route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL); if (ttable == NULL) return -ENOMEM;#if 1 if (sv == 2 && dv == 1) { ttable[0] = HALF; ttable[1] = HALF; } else#endif { int v; for (v = 0; v < dv; ++v) ttable[v * sv + v] = FULL; } tmpformat.channels = dstformat.channels; if (rate_match(srcformat.rate, dstformat.rate) && snd_pcm_format_linear(dstformat.format)) tmpformat.format = dstformat.format; err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, ttable, &plugin); kfree(ttable); pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); if (err < 0) { snd_pcm_plugin_free(plugin); return err; } err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; } srcformat = tmpformat; src_access = dst_access; } /* rate resampling */ if (!rate_match(srcformat.rate, dstformat.rate)) { tmpformat.rate = dstformat.rate; if (srcformat.channels == dstformat.channels && snd_pcm_format_linear(dstformat.format)) tmpformat.format = dstformat.format;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -