xref: /linux/sound/core/oss/linear.c (revision d3603f4c30c24b43f49bf9c1dbc93b06f7a7775d)
1  /*
2   *  Linear conversion Plug-In
3   *  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 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  #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 plugin
30   */
31  
32  struct 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  
42  static 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  
55  static 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  
88  static 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_DEBUG
98  	{
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  #endif
110  	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  
116  static 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  	} else
133  		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  	else
138  		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  		else
145  			data->flip = (__force u32)cpu_to_be32(0x80000000);
146  	}
147  }
148  
149  int 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