181 lines · c
1/*2 * Linear conversion Plug-In3 * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>,4 * 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#include <linux/time.h>24#include <sound/core.h>25#include <sound/pcm.h>26#include "pcm_plugin.h"27 28/*29 * Basic linear conversion plugin30 */31 32struct linear_priv {33 int cvt_endian; /* need endian conversion? */34 unsigned int src_ofs; /* byte offset in source format */35 unsigned int dst_ofs; /* byte soffset in destination format */36 unsigned int copy_ofs; /* byte offset in temporary u32 data */37 unsigned int dst_bytes; /* byte size of destination format */38 unsigned int copy_bytes; /* bytes to copy per conversion */39 unsigned int flip; /* MSB flip for signeness, done after endian conv */40};41 42static inline void do_convert(struct linear_priv *data,43 unsigned char *dst, unsigned char *src)44{45 unsigned int tmp = 0;46 unsigned char *p = (unsigned char *)&tmp;47 48 memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);49 if (data->cvt_endian)50 tmp = swab32(tmp);51 tmp ^= data->flip;52 memcpy(dst, p + data->dst_ofs, data->dst_bytes);53}54 55static void convert(struct snd_pcm_plugin *plugin,56 const struct snd_pcm_plugin_channel *src_channels,57 struct snd_pcm_plugin_channel *dst_channels,58 snd_pcm_uframes_t frames)59{60 struct linear_priv *data = (struct linear_priv *)plugin->extra_data;61 int channel;62 int nchannels = plugin->src_format.channels;63 for (channel = 0; channel < nchannels; ++channel) {64 char *src;65 char *dst;66 int src_step, dst_step;67 snd_pcm_uframes_t frames1;68 if (!src_channels[channel].enabled) {69 if (dst_channels[channel].wanted)70 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);71 dst_channels[channel].enabled = 0;72 continue;73 }74 dst_channels[channel].enabled = 1;75 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;76 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;77 src_step = src_channels[channel].area.step / 8;78 dst_step = dst_channels[channel].area.step / 8;79 frames1 = frames;80 while (frames1-- > 0) {81 do_convert(data, dst, src);82 src += src_step;83 dst += dst_step;84 }85 }86}87 88static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,89 const struct snd_pcm_plugin_channel *src_channels,90 struct snd_pcm_plugin_channel *dst_channels,91 snd_pcm_uframes_t frames)92{93 if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))94 return -ENXIO;95 if (frames == 0)96 return 0;97#ifdef CONFIG_SND_DEBUG98 {99 unsigned int channel;100 for (channel = 0; channel < plugin->src_format.channels; channel++) {101 if (snd_BUG_ON(src_channels[channel].area.first % 8 ||102 src_channels[channel].area.step % 8))103 return -ENXIO;104 if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||105 dst_channels[channel].area.step % 8))106 return -ENXIO;107 }108 }109#endif110 if (frames > dst_channels[0].frames)111 frames = dst_channels[0].frames;112 convert(plugin, src_channels, dst_channels, frames);113 return frames;114}115 116static void init_data(struct linear_priv *data,117 snd_pcm_format_t src_format, snd_pcm_format_t dst_format)118{119 int src_le, dst_le, src_bytes, dst_bytes;120 121 src_bytes = snd_pcm_format_width(src_format) / 8;122 dst_bytes = snd_pcm_format_width(dst_format) / 8;123 src_le = snd_pcm_format_little_endian(src_format) > 0;124 dst_le = snd_pcm_format_little_endian(dst_format) > 0;125 126 data->dst_bytes = dst_bytes;127 data->cvt_endian = src_le != dst_le;128 data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;129 if (src_le) {130 data->copy_ofs = 4 - data->copy_bytes;131 data->src_ofs = src_bytes - data->copy_bytes;132 } else133 data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -134 src_bytes;135 if (dst_le)136 data->dst_ofs = 4 - data->dst_bytes;137 else138 data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -139 dst_bytes;140 if (snd_pcm_format_signed(src_format) !=141 snd_pcm_format_signed(dst_format)) {142 if (dst_le)143 data->flip = (__force u32)cpu_to_le32(0x80000000);144 else145 data->flip = (__force u32)cpu_to_be32(0x80000000);146 }147}148 149int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,150 struct snd_pcm_plugin_format *src_format,151 struct snd_pcm_plugin_format *dst_format,152 struct snd_pcm_plugin **r_plugin)153{154 int err;155 struct linear_priv *data;156 struct snd_pcm_plugin *plugin;157 158 if (snd_BUG_ON(!r_plugin))159 return -ENXIO;160 *r_plugin = NULL;161 162 if (snd_BUG_ON(src_format->rate != dst_format->rate))163 return -ENXIO;164 if (snd_BUG_ON(src_format->channels != dst_format->channels))165 return -ENXIO;166 if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||167 !snd_pcm_format_linear(dst_format->format)))168 return -ENXIO;169 170 err = snd_pcm_plugin_build(plug, "linear format conversion",171 src_format, dst_format,172 sizeof(struct linear_priv), &plugin);173 if (err < 0)174 return err;175 data = (struct linear_priv *)plugin->extra_data;176 init_data(data, src_format->format, dst_format->format);177 plugin->transfer = linear_transfer;178 *r_plugin = plugin;179 return 0;180}181