0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include <linux/time.h>
0025 #include <sound/core.h>
0026 #include <sound/pcm.h>
0027 #include "pcm_plugin.h"
0028
0029 #define SIGN_BIT (0x80)
0030 #define QUANT_MASK (0xf)
0031 #define NSEGS (8)
0032 #define SEG_SHIFT (4)
0033 #define SEG_MASK (0x70)
0034
0035 static inline int val_seg(int val)
0036 {
0037 int r = 0;
0038 val >>= 7;
0039 if (val & 0xf0) {
0040 val >>= 4;
0041 r += 4;
0042 }
0043 if (val & 0x0c) {
0044 val >>= 2;
0045 r += 2;
0046 }
0047 if (val & 0x02)
0048 r += 1;
0049 return r;
0050 }
0051
0052 #define BIAS (0x84)
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083 static unsigned char linear2ulaw(int pcm_val)
0084 {
0085 int mask;
0086 int seg;
0087 unsigned char uval;
0088
0089
0090 if (pcm_val < 0) {
0091 pcm_val = BIAS - pcm_val;
0092 mask = 0x7F;
0093 } else {
0094 pcm_val += BIAS;
0095 mask = 0xFF;
0096 }
0097 if (pcm_val > 0x7FFF)
0098 pcm_val = 0x7FFF;
0099
0100
0101 seg = val_seg(pcm_val);
0102
0103
0104
0105
0106
0107 uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
0108 return uval ^ mask;
0109 }
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120 static int ulaw2linear(unsigned char u_val)
0121 {
0122 int t;
0123
0124
0125 u_val = ~u_val;
0126
0127
0128
0129
0130
0131 t = ((u_val & QUANT_MASK) << 3) + BIAS;
0132 t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
0133
0134 return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
0135 }
0136
0137
0138
0139
0140
0141 typedef void (*mulaw_f)(struct snd_pcm_plugin *plugin,
0142 const struct snd_pcm_plugin_channel *src_channels,
0143 struct snd_pcm_plugin_channel *dst_channels,
0144 snd_pcm_uframes_t frames);
0145
0146 struct mulaw_priv {
0147 mulaw_f func;
0148 int cvt_endian;
0149 unsigned int native_ofs;
0150 unsigned int copy_ofs;
0151 unsigned int native_bytes;
0152 unsigned int copy_bytes;
0153 u16 flip;
0154 };
0155
0156 static inline void cvt_s16_to_native(struct mulaw_priv *data,
0157 unsigned char *dst, u16 sample)
0158 {
0159 sample ^= data->flip;
0160 if (data->cvt_endian)
0161 sample = swab16(sample);
0162 if (data->native_bytes > data->copy_bytes)
0163 memset(dst, 0, data->native_bytes);
0164 memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs,
0165 data->copy_bytes);
0166 }
0167
0168 static void mulaw_decode(struct snd_pcm_plugin *plugin,
0169 const struct snd_pcm_plugin_channel *src_channels,
0170 struct snd_pcm_plugin_channel *dst_channels,
0171 snd_pcm_uframes_t frames)
0172 {
0173 struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
0174 int channel;
0175 int nchannels = plugin->src_format.channels;
0176 for (channel = 0; channel < nchannels; ++channel) {
0177 char *src;
0178 char *dst;
0179 int src_step, dst_step;
0180 snd_pcm_uframes_t frames1;
0181 if (!src_channels[channel].enabled) {
0182 if (dst_channels[channel].wanted)
0183 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
0184 dst_channels[channel].enabled = 0;
0185 continue;
0186 }
0187 dst_channels[channel].enabled = 1;
0188 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
0189 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
0190 src_step = src_channels[channel].area.step / 8;
0191 dst_step = dst_channels[channel].area.step / 8;
0192 frames1 = frames;
0193 while (frames1-- > 0) {
0194 signed short sample = ulaw2linear(*src);
0195 cvt_s16_to_native(data, dst, sample);
0196 src += src_step;
0197 dst += dst_step;
0198 }
0199 }
0200 }
0201
0202 static inline signed short cvt_native_to_s16(struct mulaw_priv *data,
0203 unsigned char *src)
0204 {
0205 u16 sample = 0;
0206 memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs,
0207 data->copy_bytes);
0208 if (data->cvt_endian)
0209 sample = swab16(sample);
0210 sample ^= data->flip;
0211 return (signed short)sample;
0212 }
0213
0214 static void mulaw_encode(struct snd_pcm_plugin *plugin,
0215 const struct snd_pcm_plugin_channel *src_channels,
0216 struct snd_pcm_plugin_channel *dst_channels,
0217 snd_pcm_uframes_t frames)
0218 {
0219 struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
0220 int channel;
0221 int nchannels = plugin->src_format.channels;
0222 for (channel = 0; channel < nchannels; ++channel) {
0223 char *src;
0224 char *dst;
0225 int src_step, dst_step;
0226 snd_pcm_uframes_t frames1;
0227 if (!src_channels[channel].enabled) {
0228 if (dst_channels[channel].wanted)
0229 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
0230 dst_channels[channel].enabled = 0;
0231 continue;
0232 }
0233 dst_channels[channel].enabled = 1;
0234 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
0235 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
0236 src_step = src_channels[channel].area.step / 8;
0237 dst_step = dst_channels[channel].area.step / 8;
0238 frames1 = frames;
0239 while (frames1-- > 0) {
0240 signed short sample = cvt_native_to_s16(data, src);
0241 *dst = linear2ulaw(sample);
0242 src += src_step;
0243 dst += dst_step;
0244 }
0245 }
0246 }
0247
0248 static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
0249 const struct snd_pcm_plugin_channel *src_channels,
0250 struct snd_pcm_plugin_channel *dst_channels,
0251 snd_pcm_uframes_t frames)
0252 {
0253 struct mulaw_priv *data;
0254
0255 if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
0256 return -ENXIO;
0257 if (frames == 0)
0258 return 0;
0259 #ifdef CONFIG_SND_DEBUG
0260 {
0261 unsigned int channel;
0262 for (channel = 0; channel < plugin->src_format.channels; channel++) {
0263 if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
0264 src_channels[channel].area.step % 8))
0265 return -ENXIO;
0266 if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
0267 dst_channels[channel].area.step % 8))
0268 return -ENXIO;
0269 }
0270 }
0271 #endif
0272 if (frames > dst_channels[0].frames)
0273 frames = dst_channels[0].frames;
0274 data = (struct mulaw_priv *)plugin->extra_data;
0275 data->func(plugin, src_channels, dst_channels, frames);
0276 return frames;
0277 }
0278
0279 static void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
0280 {
0281 #ifdef SNDRV_LITTLE_ENDIAN
0282 data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
0283 #else
0284 data->cvt_endian = snd_pcm_format_little_endian(format) > 0;
0285 #endif
0286 if (!snd_pcm_format_signed(format))
0287 data->flip = 0x8000;
0288 data->native_bytes = snd_pcm_format_physical_width(format) / 8;
0289 data->copy_bytes = data->native_bytes < 2 ? 1 : 2;
0290 if (snd_pcm_format_little_endian(format)) {
0291 data->native_ofs = data->native_bytes - data->copy_bytes;
0292 data->copy_ofs = 2 - data->copy_bytes;
0293 } else {
0294
0295 data->native_ofs = data->native_bytes -
0296 snd_pcm_format_width(format) / 8;
0297 }
0298 }
0299
0300 int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
0301 struct snd_pcm_plugin_format *src_format,
0302 struct snd_pcm_plugin_format *dst_format,
0303 struct snd_pcm_plugin **r_plugin)
0304 {
0305 int err;
0306 struct mulaw_priv *data;
0307 struct snd_pcm_plugin *plugin;
0308 struct snd_pcm_plugin_format *format;
0309 mulaw_f func;
0310
0311 if (snd_BUG_ON(!r_plugin))
0312 return -ENXIO;
0313 *r_plugin = NULL;
0314
0315 if (snd_BUG_ON(src_format->rate != dst_format->rate))
0316 return -ENXIO;
0317 if (snd_BUG_ON(src_format->channels != dst_format->channels))
0318 return -ENXIO;
0319
0320 if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
0321 format = src_format;
0322 func = mulaw_encode;
0323 }
0324 else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
0325 format = dst_format;
0326 func = mulaw_decode;
0327 }
0328 else {
0329 snd_BUG();
0330 return -EINVAL;
0331 }
0332 if (!snd_pcm_format_linear(format->format))
0333 return -EINVAL;
0334
0335 err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
0336 src_format, dst_format,
0337 sizeof(struct mulaw_priv), &plugin);
0338 if (err < 0)
0339 return err;
0340 data = (struct mulaw_priv *)plugin->extra_data;
0341 data->func = func;
0342 init_data(data, format->format);
0343 plugin->transfer = mulaw_transfer;
0344 *r_plugin = plugin;
0345 return 0;
0346 }