162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Mu-Law conversion Plug-In Interface 362306a36Sopenharmony_ci * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Uros Bizjak <uros@kss-loka.si> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on reference implementation by Sun Microsystems, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 962306a36Sopenharmony_ci * it under the terms of the GNU Library General Public License as 1062306a36Sopenharmony_ci * published by the Free Software Foundation; either version 2 of 1162306a36Sopenharmony_ci * the License, or (at your option) any later version. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1462306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1562306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1662306a36Sopenharmony_ci * GNU Library General Public License for more details. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * You should have received a copy of the GNU Library General Public 1962306a36Sopenharmony_ci * License along with this library; if not, write to the Free Software 2062306a36Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/time.h> 2562306a36Sopenharmony_ci#include <sound/core.h> 2662306a36Sopenharmony_ci#include <sound/pcm.h> 2762306a36Sopenharmony_ci#include "pcm_plugin.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ 3062306a36Sopenharmony_ci#define QUANT_MASK (0xf) /* Quantization field mask. */ 3162306a36Sopenharmony_ci#define NSEGS (8) /* Number of u-law segments. */ 3262306a36Sopenharmony_ci#define SEG_SHIFT (4) /* Left shift for segment number. */ 3362306a36Sopenharmony_ci#define SEG_MASK (0x70) /* Segment field mask. */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline int val_seg(int val) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int r = 0; 3862306a36Sopenharmony_ci val >>= 7; 3962306a36Sopenharmony_ci if (val & 0xf0) { 4062306a36Sopenharmony_ci val >>= 4; 4162306a36Sopenharmony_ci r += 4; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci if (val & 0x0c) { 4462306a36Sopenharmony_ci val >>= 2; 4562306a36Sopenharmony_ci r += 2; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci if (val & 0x02) 4862306a36Sopenharmony_ci r += 1; 4962306a36Sopenharmony_ci return r; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define BIAS (0x84) /* Bias for linear code. */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * linear2ulaw() - Convert a linear PCM value to u-law 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * In order to simplify the encoding process, the original linear magnitude 5862306a36Sopenharmony_ci * is biased by adding 33 which shifts the encoding range from (0 - 8158) to 5962306a36Sopenharmony_ci * (33 - 8191). The result can be seen in the following encoding table: 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Biased Linear Input Code Compressed Code 6262306a36Sopenharmony_ci * ------------------------ --------------- 6362306a36Sopenharmony_ci * 00000001wxyza 000wxyz 6462306a36Sopenharmony_ci * 0000001wxyzab 001wxyz 6562306a36Sopenharmony_ci * 000001wxyzabc 010wxyz 6662306a36Sopenharmony_ci * 00001wxyzabcd 011wxyz 6762306a36Sopenharmony_ci * 0001wxyzabcde 100wxyz 6862306a36Sopenharmony_ci * 001wxyzabcdef 101wxyz 6962306a36Sopenharmony_ci * 01wxyzabcdefg 110wxyz 7062306a36Sopenharmony_ci * 1wxyzabcdefgh 111wxyz 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Each biased linear code has a leading 1 which identifies the segment 7362306a36Sopenharmony_ci * number. The value of the segment number is equal to 7 minus the number 7462306a36Sopenharmony_ci * of leading 0's. The quantization interval is directly available as the 7562306a36Sopenharmony_ci * four bits wxyz. * The trailing bits (a - h) are ignored. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Ordinarily the complement of the resulting code word is used for 7862306a36Sopenharmony_ci * transmission, and so the code word is complemented before it is returned. 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * For further information see John C. Bellamy's Digital Telephony, 1982, 8162306a36Sopenharmony_ci * John Wiley & Sons, pps 98-111 and 472-476. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_cistatic unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int mask; 8662306a36Sopenharmony_ci int seg; 8762306a36Sopenharmony_ci unsigned char uval; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Get the sign and the magnitude of the value. */ 9062306a36Sopenharmony_ci if (pcm_val < 0) { 9162306a36Sopenharmony_ci pcm_val = BIAS - pcm_val; 9262306a36Sopenharmony_ci mask = 0x7F; 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci pcm_val += BIAS; 9562306a36Sopenharmony_ci mask = 0xFF; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if (pcm_val > 0x7FFF) 9862306a36Sopenharmony_ci pcm_val = 0x7FFF; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Convert the scaled magnitude to segment number. */ 10162306a36Sopenharmony_ci seg = val_seg(pcm_val); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Combine the sign, segment, quantization bits; 10562306a36Sopenharmony_ci * and complement the code word. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); 10862306a36Sopenharmony_ci return uval ^ mask; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * ulaw2linear() - Convert a u-law value to 16-bit linear PCM 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * First, a biased linear code is derived from the code word. An unbiased 11562306a36Sopenharmony_ci * output can then be obtained by subtracting 33 from the biased code. 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * Note that this function expects to be passed the complement of the 11862306a36Sopenharmony_ci * original code word. This is in keeping with ISDN conventions. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic int ulaw2linear(unsigned char u_val) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int t; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Complement to obtain normal u-law value. */ 12562306a36Sopenharmony_ci u_val = ~u_val; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * Extract and bias the quantization bits. Then 12962306a36Sopenharmony_ci * shift up by the segment number and subtract out the bias. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci t = ((u_val & QUANT_MASK) << 3) + BIAS; 13262306a36Sopenharmony_ci t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * Basic Mu-Law plugin 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_citypedef void (*mulaw_f)(struct snd_pcm_plugin *plugin, 14262306a36Sopenharmony_ci const struct snd_pcm_plugin_channel *src_channels, 14362306a36Sopenharmony_ci struct snd_pcm_plugin_channel *dst_channels, 14462306a36Sopenharmony_ci snd_pcm_uframes_t frames); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct mulaw_priv { 14762306a36Sopenharmony_ci mulaw_f func; 14862306a36Sopenharmony_ci int cvt_endian; /* need endian conversion? */ 14962306a36Sopenharmony_ci unsigned int native_ofs; /* byte offset in native format */ 15062306a36Sopenharmony_ci unsigned int copy_ofs; /* byte offset in s16 format */ 15162306a36Sopenharmony_ci unsigned int native_bytes; /* byte size of the native format */ 15262306a36Sopenharmony_ci unsigned int copy_bytes; /* bytes to copy per conversion */ 15362306a36Sopenharmony_ci u16 flip; /* MSB flip for signedness, done after endian conversion */ 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic inline void cvt_s16_to_native(struct mulaw_priv *data, 15762306a36Sopenharmony_ci unsigned char *dst, u16 sample) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci sample ^= data->flip; 16062306a36Sopenharmony_ci if (data->cvt_endian) 16162306a36Sopenharmony_ci sample = swab16(sample); 16262306a36Sopenharmony_ci if (data->native_bytes > data->copy_bytes) 16362306a36Sopenharmony_ci memset(dst, 0, data->native_bytes); 16462306a36Sopenharmony_ci memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs, 16562306a36Sopenharmony_ci data->copy_bytes); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void mulaw_decode(struct snd_pcm_plugin *plugin, 16962306a36Sopenharmony_ci const struct snd_pcm_plugin_channel *src_channels, 17062306a36Sopenharmony_ci struct snd_pcm_plugin_channel *dst_channels, 17162306a36Sopenharmony_ci snd_pcm_uframes_t frames) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data; 17462306a36Sopenharmony_ci int channel; 17562306a36Sopenharmony_ci int nchannels = plugin->src_format.channels; 17662306a36Sopenharmony_ci for (channel = 0; channel < nchannels; ++channel) { 17762306a36Sopenharmony_ci char *src; 17862306a36Sopenharmony_ci char *dst; 17962306a36Sopenharmony_ci int src_step, dst_step; 18062306a36Sopenharmony_ci snd_pcm_uframes_t frames1; 18162306a36Sopenharmony_ci if (!src_channels[channel].enabled) { 18262306a36Sopenharmony_ci if (dst_channels[channel].wanted) 18362306a36Sopenharmony_ci snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); 18462306a36Sopenharmony_ci dst_channels[channel].enabled = 0; 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci dst_channels[channel].enabled = 1; 18862306a36Sopenharmony_ci src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; 18962306a36Sopenharmony_ci dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; 19062306a36Sopenharmony_ci src_step = src_channels[channel].area.step / 8; 19162306a36Sopenharmony_ci dst_step = dst_channels[channel].area.step / 8; 19262306a36Sopenharmony_ci frames1 = frames; 19362306a36Sopenharmony_ci while (frames1-- > 0) { 19462306a36Sopenharmony_ci signed short sample = ulaw2linear(*src); 19562306a36Sopenharmony_ci cvt_s16_to_native(data, dst, sample); 19662306a36Sopenharmony_ci src += src_step; 19762306a36Sopenharmony_ci dst += dst_step; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic inline signed short cvt_native_to_s16(struct mulaw_priv *data, 20362306a36Sopenharmony_ci unsigned char *src) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u16 sample = 0; 20662306a36Sopenharmony_ci memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs, 20762306a36Sopenharmony_ci data->copy_bytes); 20862306a36Sopenharmony_ci if (data->cvt_endian) 20962306a36Sopenharmony_ci sample = swab16(sample); 21062306a36Sopenharmony_ci sample ^= data->flip; 21162306a36Sopenharmony_ci return (signed short)sample; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void mulaw_encode(struct snd_pcm_plugin *plugin, 21562306a36Sopenharmony_ci const struct snd_pcm_plugin_channel *src_channels, 21662306a36Sopenharmony_ci struct snd_pcm_plugin_channel *dst_channels, 21762306a36Sopenharmony_ci snd_pcm_uframes_t frames) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data; 22062306a36Sopenharmony_ci int channel; 22162306a36Sopenharmony_ci int nchannels = plugin->src_format.channels; 22262306a36Sopenharmony_ci for (channel = 0; channel < nchannels; ++channel) { 22362306a36Sopenharmony_ci char *src; 22462306a36Sopenharmony_ci char *dst; 22562306a36Sopenharmony_ci int src_step, dst_step; 22662306a36Sopenharmony_ci snd_pcm_uframes_t frames1; 22762306a36Sopenharmony_ci if (!src_channels[channel].enabled) { 22862306a36Sopenharmony_ci if (dst_channels[channel].wanted) 22962306a36Sopenharmony_ci snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); 23062306a36Sopenharmony_ci dst_channels[channel].enabled = 0; 23162306a36Sopenharmony_ci continue; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci dst_channels[channel].enabled = 1; 23462306a36Sopenharmony_ci src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; 23562306a36Sopenharmony_ci dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; 23662306a36Sopenharmony_ci src_step = src_channels[channel].area.step / 8; 23762306a36Sopenharmony_ci dst_step = dst_channels[channel].area.step / 8; 23862306a36Sopenharmony_ci frames1 = frames; 23962306a36Sopenharmony_ci while (frames1-- > 0) { 24062306a36Sopenharmony_ci signed short sample = cvt_native_to_s16(data, src); 24162306a36Sopenharmony_ci *dst = linear2ulaw(sample); 24262306a36Sopenharmony_ci src += src_step; 24362306a36Sopenharmony_ci dst += dst_step; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, 24962306a36Sopenharmony_ci const struct snd_pcm_plugin_channel *src_channels, 25062306a36Sopenharmony_ci struct snd_pcm_plugin_channel *dst_channels, 25162306a36Sopenharmony_ci snd_pcm_uframes_t frames) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct mulaw_priv *data; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) 25662306a36Sopenharmony_ci return -ENXIO; 25762306a36Sopenharmony_ci if (frames == 0) 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 26062306a36Sopenharmony_ci { 26162306a36Sopenharmony_ci unsigned int channel; 26262306a36Sopenharmony_ci for (channel = 0; channel < plugin->src_format.channels; channel++) { 26362306a36Sopenharmony_ci if (snd_BUG_ON(src_channels[channel].area.first % 8 || 26462306a36Sopenharmony_ci src_channels[channel].area.step % 8)) 26562306a36Sopenharmony_ci return -ENXIO; 26662306a36Sopenharmony_ci if (snd_BUG_ON(dst_channels[channel].area.first % 8 || 26762306a36Sopenharmony_ci dst_channels[channel].area.step % 8)) 26862306a36Sopenharmony_ci return -ENXIO; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci#endif 27262306a36Sopenharmony_ci if (frames > dst_channels[0].frames) 27362306a36Sopenharmony_ci frames = dst_channels[0].frames; 27462306a36Sopenharmony_ci data = (struct mulaw_priv *)plugin->extra_data; 27562306a36Sopenharmony_ci data->func(plugin, src_channels, dst_channels, frames); 27662306a36Sopenharmony_ci return frames; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void init_data(struct mulaw_priv *data, snd_pcm_format_t format) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN 28262306a36Sopenharmony_ci data->cvt_endian = snd_pcm_format_big_endian(format) > 0; 28362306a36Sopenharmony_ci#else 28462306a36Sopenharmony_ci data->cvt_endian = snd_pcm_format_little_endian(format) > 0; 28562306a36Sopenharmony_ci#endif 28662306a36Sopenharmony_ci if (!snd_pcm_format_signed(format)) 28762306a36Sopenharmony_ci data->flip = 0x8000; 28862306a36Sopenharmony_ci data->native_bytes = snd_pcm_format_physical_width(format) / 8; 28962306a36Sopenharmony_ci data->copy_bytes = data->native_bytes < 2 ? 1 : 2; 29062306a36Sopenharmony_ci if (snd_pcm_format_little_endian(format)) { 29162306a36Sopenharmony_ci data->native_ofs = data->native_bytes - data->copy_bytes; 29262306a36Sopenharmony_ci data->copy_ofs = 2 - data->copy_bytes; 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci /* S24 in 4bytes need an 1 byte offset */ 29562306a36Sopenharmony_ci data->native_ofs = data->native_bytes - 29662306a36Sopenharmony_ci snd_pcm_format_width(format) / 8; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug, 30162306a36Sopenharmony_ci struct snd_pcm_plugin_format *src_format, 30262306a36Sopenharmony_ci struct snd_pcm_plugin_format *dst_format, 30362306a36Sopenharmony_ci struct snd_pcm_plugin **r_plugin) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int err; 30662306a36Sopenharmony_ci struct mulaw_priv *data; 30762306a36Sopenharmony_ci struct snd_pcm_plugin *plugin; 30862306a36Sopenharmony_ci struct snd_pcm_plugin_format *format; 30962306a36Sopenharmony_ci mulaw_f func; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (snd_BUG_ON(!r_plugin)) 31262306a36Sopenharmony_ci return -ENXIO; 31362306a36Sopenharmony_ci *r_plugin = NULL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (snd_BUG_ON(src_format->rate != dst_format->rate)) 31662306a36Sopenharmony_ci return -ENXIO; 31762306a36Sopenharmony_ci if (snd_BUG_ON(src_format->channels != dst_format->channels)) 31862306a36Sopenharmony_ci return -ENXIO; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { 32162306a36Sopenharmony_ci format = src_format; 32262306a36Sopenharmony_ci func = mulaw_encode; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) { 32562306a36Sopenharmony_ci format = dst_format; 32662306a36Sopenharmony_ci func = mulaw_decode; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci else { 32962306a36Sopenharmony_ci snd_BUG(); 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if (!snd_pcm_format_linear(format->format)) 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", 33662306a36Sopenharmony_ci src_format, dst_format, 33762306a36Sopenharmony_ci sizeof(struct mulaw_priv), &plugin); 33862306a36Sopenharmony_ci if (err < 0) 33962306a36Sopenharmony_ci return err; 34062306a36Sopenharmony_ci data = (struct mulaw_priv *)plugin->extra_data; 34162306a36Sopenharmony_ci data->func = func; 34262306a36Sopenharmony_ci init_data(data, format->format); 34362306a36Sopenharmony_ci plugin->transfer = mulaw_transfer; 34462306a36Sopenharmony_ci *r_plugin = plugin; 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 347