Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  Linear conversion Plug-In
0003  *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>,
0004  *            Abramo Bagnara <abramo@alsa-project.org>
0005  *
0006  *
0007  *   This library is free software; you can redistribute it and/or modify
0008  *   it under the terms of the GNU Library General Public License as
0009  *   published by the Free Software Foundation; either version 2 of
0010  *   the License, or (at your option) any later version.
0011  *
0012  *   This program is distributed in the hope that it will be useful,
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  *   GNU Library General Public License for more details.
0016  *
0017  *   You should have received a copy of the GNU Library General Public
0018  *   License along with this library; if not, write to the Free Software
0019  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
0020  *
0021  */
0022 
0023 #include <linux/time.h>
0024 #include <sound/core.h>
0025 #include <sound/pcm.h>
0026 #include "pcm_plugin.h"
0027 
0028 /*
0029  *  Basic linear conversion plugin
0030  */
0031  
0032 struct linear_priv {
0033     int cvt_endian;     /* need endian conversion? */
0034     unsigned int src_ofs;   /* byte offset in source format */
0035     unsigned int dst_ofs;   /* byte soffset in destination format */
0036     unsigned int copy_ofs;  /* byte offset in temporary u32 data */
0037     unsigned int dst_bytes;     /* byte size of destination format */
0038     unsigned int copy_bytes;    /* bytes to copy per conversion */
0039     unsigned int flip; /* MSB flip for signeness, done after endian conv */
0040 };
0041 
0042 static inline void do_convert(struct linear_priv *data,
0043                   unsigned char *dst, unsigned char *src)
0044 {
0045     unsigned int tmp = 0;
0046     unsigned char *p = (unsigned char *)&tmp;
0047 
0048     memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);
0049     if (data->cvt_endian)
0050         tmp = swab32(tmp);
0051     tmp ^= data->flip;
0052     memcpy(dst, p + data->dst_ofs, data->dst_bytes);
0053 }
0054 
0055 static void convert(struct snd_pcm_plugin *plugin,
0056             const struct snd_pcm_plugin_channel *src_channels,
0057             struct snd_pcm_plugin_channel *dst_channels,
0058             snd_pcm_uframes_t frames)
0059 {
0060     struct linear_priv *data = (struct linear_priv *)plugin->extra_data;
0061     int channel;
0062     int nchannels = plugin->src_format.channels;
0063     for (channel = 0; channel < nchannels; ++channel) {
0064         char *src;
0065         char *dst;
0066         int src_step, dst_step;
0067         snd_pcm_uframes_t frames1;
0068         if (!src_channels[channel].enabled) {
0069             if (dst_channels[channel].wanted)
0070                 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
0071             dst_channels[channel].enabled = 0;
0072             continue;
0073         }
0074         dst_channels[channel].enabled = 1;
0075         src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
0076         dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
0077         src_step = src_channels[channel].area.step / 8;
0078         dst_step = dst_channels[channel].area.step / 8;
0079         frames1 = frames;
0080         while (frames1-- > 0) {
0081             do_convert(data, dst, src);
0082             src += src_step;
0083             dst += dst_step;
0084         }
0085     }
0086 }
0087 
0088 static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
0089                    const struct snd_pcm_plugin_channel *src_channels,
0090                    struct snd_pcm_plugin_channel *dst_channels,
0091                    snd_pcm_uframes_t frames)
0092 {
0093     if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
0094         return -ENXIO;
0095     if (frames == 0)
0096         return 0;
0097 #ifdef CONFIG_SND_DEBUG
0098     {
0099         unsigned int channel;
0100         for (channel = 0; channel < plugin->src_format.channels; channel++) {
0101             if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
0102                        src_channels[channel].area.step % 8))
0103                 return -ENXIO;
0104             if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
0105                        dst_channels[channel].area.step % 8))
0106                 return -ENXIO;
0107         }
0108     }
0109 #endif
0110     if (frames > dst_channels[0].frames)
0111         frames = dst_channels[0].frames;
0112     convert(plugin, src_channels, dst_channels, frames);
0113     return frames;
0114 }
0115 
0116 static void init_data(struct linear_priv *data,
0117               snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
0118 {
0119     int src_le, dst_le, src_bytes, dst_bytes;
0120 
0121     src_bytes = snd_pcm_format_width(src_format) / 8;
0122     dst_bytes = snd_pcm_format_width(dst_format) / 8;
0123     src_le = snd_pcm_format_little_endian(src_format) > 0;
0124     dst_le = snd_pcm_format_little_endian(dst_format) > 0;
0125 
0126     data->dst_bytes = dst_bytes;
0127     data->cvt_endian = src_le != dst_le;
0128     data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;
0129     if (src_le) {
0130         data->copy_ofs = 4 - data->copy_bytes;
0131         data->src_ofs = src_bytes - data->copy_bytes;
0132     } else
0133         data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -
0134             src_bytes;
0135     if (dst_le)
0136         data->dst_ofs = 4 - data->dst_bytes;
0137     else
0138         data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -
0139             dst_bytes;
0140     if (snd_pcm_format_signed(src_format) !=
0141         snd_pcm_format_signed(dst_format)) {
0142         if (dst_le)
0143             data->flip = (__force u32)cpu_to_le32(0x80000000);
0144         else
0145             data->flip = (__force u32)cpu_to_be32(0x80000000);
0146     }
0147 }
0148 
0149 int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
0150                 struct snd_pcm_plugin_format *src_format,
0151                 struct snd_pcm_plugin_format *dst_format,
0152                 struct snd_pcm_plugin **r_plugin)
0153 {
0154     int err;
0155     struct linear_priv *data;
0156     struct snd_pcm_plugin *plugin;
0157 
0158     if (snd_BUG_ON(!r_plugin))
0159         return -ENXIO;
0160     *r_plugin = NULL;
0161 
0162     if (snd_BUG_ON(src_format->rate != dst_format->rate))
0163         return -ENXIO;
0164     if (snd_BUG_ON(src_format->channels != dst_format->channels))
0165         return -ENXIO;
0166     if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
0167                !snd_pcm_format_linear(dst_format->format)))
0168         return -ENXIO;
0169 
0170     err = snd_pcm_plugin_build(plug, "linear format conversion",
0171                    src_format, dst_format,
0172                    sizeof(struct linear_priv), &plugin);
0173     if (err < 0)
0174         return err;
0175     data = (struct linear_priv *)plugin->extra_data;
0176     init_data(data, src_format->format, dst_format->format);
0177     plugin->transfer = linear_transfer;
0178     *r_plugin = plugin;
0179     return 0;
0180 }