2 * PCM - A-Law conversion
3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "pcm_local.h"
24 #include "pcm_plugin.h"
26 typedef void (*alaw_f)(snd_pcm_channel_area_t *src_areas,
28 snd_pcm_channel_area_t *dst_areas,
30 size_t frames, size_t channels, int getputidx);
33 /* This field need to be the first */
34 snd_pcm_plugin_t plug;
39 int cxfer_mode, cmmap_shape;
42 static inline int val_seg(int val)
60 * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
62 * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
64 * Linear Input Code Compressed Code
65 * ------------------------ ---------------
66 * 0000000wxyza 000wxyz
67 * 0000001wxyza 001wxyz
68 * 000001wxyzab 010wxyz
69 * 00001wxyzabc 011wxyz
70 * 0001wxyzabcd 100wxyz
71 * 001wxyzabcde 101wxyz
72 * 01wxyzabcdef 110wxyz
73 * 1wxyzabcdefg 111wxyz
75 * For further information see John C. Bellamy's Digital Telephony, 1982,
76 * John Wiley & Sons, pps 98-111 and 472-476.
79 static unsigned char s16_to_alaw(int pcm_val)
97 /* Convert the scaled magnitude to segment number. */
98 seg = val_seg(pcm_val);
99 aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
105 * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
108 static int alaw_to_s16(unsigned char a_val)
118 seg = (t >> 4) & 0x07;
119 t = ((t & 0x0f) << 4) + 0x108;
122 return ((a_val & 0x80) ? t : -t);
125 static void alaw_decode(snd_pcm_channel_area_t *src_areas,
127 snd_pcm_channel_area_t *dst_areas,
129 size_t frames, size_t channels, int putidx)
131 #define PUT_S16_LABELS
132 #include "plugin_ops.h"
133 #undef PUT_S16_LABELS
134 void *put = put_s16_labels[putidx];
136 for (channel = 0; channel < channels; ++channel) {
139 int src_step, dst_step;
141 snd_pcm_channel_area_t *src_area = &src_areas[channel];
142 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
144 if (!src_area->enabled) {
145 if (dst_area->wanted)
146 snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt);
147 dst_area->enabled = 0;
150 dst_area->enabled = 1;
152 src = snd_pcm_channel_area_addr(src_area, src_offset);
153 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
154 src_step = snd_pcm_channel_area_step(src_area);
155 dst_step = snd_pcm_channel_area_step(dst_area);
157 while (frames1-- > 0) {
158 int16_t sample = alaw_to_s16(*src);
160 #define PUT_S16_END after
161 #include "plugin_ops.h"
170 static void alaw_encode(snd_pcm_channel_area_t *src_areas,
172 snd_pcm_channel_area_t *dst_areas,
174 size_t frames, size_t channels, int getidx)
176 #define GET_S16_LABELS
177 #include "plugin_ops.h"
178 #undef GET_S16_LABELS
179 void *get = get_s16_labels[getidx];
182 for (channel = 0; channel < channels; ++channel) {
185 int src_step, dst_step;
187 snd_pcm_channel_area_t *src_area = &src_areas[channel];
188 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
190 if (!src_area->enabled) {
191 if (dst_area->wanted)
192 snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt);
193 dst_area->enabled = 0;
196 dst_area->enabled = 1;
198 src = snd_pcm_channel_area_addr(src_area, src_offset);
199 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
200 src_step = snd_pcm_channel_area_step(src_area);
201 dst_step = snd_pcm_channel_area_step(dst_area);
203 while (frames1-- > 0) {
205 #define GET_S16_END after
206 #include "plugin_ops.h"
209 *dst = s16_to_alaw(sample);
216 static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
218 snd_pcm_alaw_t *alaw = pcm->private;
219 unsigned int req_mask = info->req_mask;
220 unsigned int sfmt = info->req.format.sfmt;
222 if (req_mask & SND_PCM_PARAMS_SFMT) {
223 if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
224 !snd_pcm_format_linear(sfmt) :
225 sfmt != SND_PCM_SFMT_A_LAW) {
226 info->req.fail_mask = SND_PCM_PARAMS_SFMT;
227 info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
231 info->req_mask |= SND_PCM_PARAMS_SFMT;
232 info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
233 SND_PCM_PARAMS_XFER_MODE);
234 info->req.format.sfmt = alaw->sformat;
235 err = snd_pcm_params_info(alaw->plug.slave, info);
236 info->req_mask = req_mask;
237 info->req.format.sfmt = sfmt;
240 if (req_mask & SND_PCM_PARAMS_SFMT)
241 info->formats = 1 << sfmt;
243 info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ?
244 SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW;
245 info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
246 info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
250 static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
252 snd_pcm_alaw_t *alaw = pcm->private;
253 snd_pcm_t *slave = alaw->plug.slave;
255 if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
256 !snd_pcm_format_linear(params->format.sfmt) :
257 params->format.sfmt != SND_PCM_SFMT_A_LAW) {
258 params->fail_mask = SND_PCM_PARAMS_SFMT;
259 params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
262 if (slave->mmap_data) {
263 err = snd_pcm_munmap_data(slave);
267 alaw->cformat = params->format.sfmt;
268 alaw->cxfer_mode = params->xfer_mode;
269 alaw->cmmap_shape = params->mmap_shape;
270 params->format.sfmt = alaw->sformat;
271 params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
272 params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
273 err = snd_pcm_params(slave, params);
274 params->format.sfmt = alaw->cformat;
275 params->xfer_mode = alaw->cxfer_mode;
276 params->mmap_shape = alaw->cmmap_shape;
277 if (slave->valid_setup) {
278 int r = snd_pcm_mmap_data(slave, NULL);
284 static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
286 snd_pcm_alaw_t *alaw = pcm->private;
287 int err = snd_pcm_setup(alaw->plug.slave, setup);
290 assert(alaw->sformat == setup->format.sfmt);
291 if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
292 setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
294 setup->xfer_mode = alaw->cxfer_mode;
295 if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
296 setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
298 setup->mmap_shape = alaw->cmmap_shape;
299 setup->format.sfmt = alaw->cformat;
300 setup->mmap_bytes = 0;
301 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
302 if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
303 alaw->getput_idx = getput_index(alaw->cformat);
304 alaw->func = alaw_encode;
306 alaw->getput_idx = getput_index(alaw->sformat);
307 alaw->func = alaw_decode;
310 if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
311 alaw->getput_idx = getput_index(alaw->cformat);
312 alaw->func = alaw_decode;
314 alaw->getput_idx = getput_index(alaw->sformat);
315 alaw->func = alaw_encode;
321 static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
322 snd_pcm_channel_area_t *areas,
327 snd_pcm_alaw_t *alaw = pcm->private;
328 snd_pcm_t *slave = alaw->plug.slave;
331 if (slave_sizep && *slave_sizep < size)
334 while (xfer < size) {
335 size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
336 alaw->func(areas, offset,
337 snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
338 frames, pcm->setup.format.channels,
340 err = snd_pcm_mmap_forward(slave, frames);
343 assert((size_t)err == frames);
346 snd_pcm_mmap_hw_forward(pcm, err);
356 static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
357 snd_pcm_channel_area_t *areas,
362 snd_pcm_alaw_t *alaw = pcm->private;
363 snd_pcm_t *slave = alaw->plug.slave;
366 if (slave_sizep && *slave_sizep < size)
369 while (xfer < size) {
370 size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
371 alaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
373 frames, pcm->setup.format.channels,
375 err = snd_pcm_mmap_forward(slave, frames);
378 assert((size_t)err == frames);
381 snd_pcm_mmap_hw_forward(pcm, err);
391 static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
393 snd_pcm_alaw_t *alaw = pcm->private;
394 fprintf(fp, "A-Law conversion PCM (%s)\n",
395 snd_pcm_format_name(alaw->sformat));
396 if (pcm->valid_setup) {
397 fprintf(fp, "Its setup is:\n");
398 snd_pcm_dump_setup(pcm, fp);
400 fprintf(fp, "Slave: ");
401 snd_pcm_dump(alaw->plug.slave, fp);
404 struct snd_pcm_ops snd_pcm_alaw_ops = {
405 close: snd_pcm_plugin_close,
406 info: snd_pcm_plugin_info,
407 params_info: snd_pcm_alaw_params_info,
408 params: snd_pcm_alaw_params,
409 setup: snd_pcm_alaw_setup,
410 channel_info: snd_pcm_plugin_channel_info,
411 channel_params: snd_pcm_plugin_channel_params,
412 channel_setup: snd_pcm_plugin_channel_setup,
413 dump: snd_pcm_alaw_dump,
414 nonblock: snd_pcm_plugin_nonblock,
415 mmap_status: snd_pcm_plugin_mmap_status,
416 mmap_control: snd_pcm_plugin_mmap_control,
417 mmap_data: snd_pcm_plugin_mmap_data,
418 munmap_status: snd_pcm_plugin_munmap_status,
419 munmap_control: snd_pcm_plugin_munmap_control,
420 munmap_data: snd_pcm_plugin_munmap_data,
423 int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
426 snd_pcm_alaw_t *alaw;
428 assert(handlep && slave);
429 if (snd_pcm_format_linear(sformat) != 1 &&
430 sformat != SND_PCM_SFMT_A_LAW)
432 alaw = calloc(1, sizeof(snd_pcm_alaw_t));
436 alaw->sformat = sformat;
437 alaw->plug.read = snd_pcm_alaw_read_areas;
438 alaw->plug.write = snd_pcm_alaw_write_areas;
439 alaw->plug.slave = slave;
440 alaw->plug.close_slave = close_slave;
442 handle = calloc(1, sizeof(snd_pcm_t));
447 handle->type = SND_PCM_TYPE_ALAW;
448 handle->stream = slave->stream;
449 handle->ops = &snd_pcm_alaw_ops;
450 handle->op_arg = handle;
451 handle->fast_ops = &snd_pcm_plugin_fast_ops;
452 handle->fast_op_arg = handle;
453 handle->mode = slave->mode;
454 handle->private = alaw;
455 err = snd_pcm_init(handle);
457 snd_pcm_close(handle);
465 int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
467 int stream, int mode)
469 snd_config_iterator_t i;
474 snd_config_foreach(i, conf) {
475 snd_config_t *n = snd_config_entry(i);
476 if (strcmp(n->id, "comment") == 0)
478 if (strcmp(n->id, "type") == 0)
480 if (strcmp(n->id, "stream") == 0)
482 if (strcmp(n->id, "sname") == 0) {
483 err = snd_config_string_get(n, &sname);
488 if (strcmp(n->id, "sformat") == 0) {
490 err = snd_config_string_get(n, &f);
493 sformat = snd_pcm_format_value(f);
496 if (snd_pcm_format_linear(sformat) != 1 &&
497 sformat != SND_PCM_SFMT_A_LAW)
503 if (!sname || !sformat)
505 /* This is needed cause snd_config_update may destroy config */
506 sname = strdup(sname);
509 err = snd_pcm_open(&spcm, sname, stream, mode);
513 err = snd_pcm_alaw_open(pcmp, sformat, spcm, 1);