4 * \brief PCM A-Law Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
9 * PCM - A-Law conversion
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
33 #include "plugin_ops.h"
36 /* entry for static linking */
37 const char *_snd_module_pcm_alaw = "";
42 typedef void (*alaw_f)(const snd_pcm_channel_area_t *dst_areas,
43 snd_pcm_uframes_t dst_offset,
44 const snd_pcm_channel_area_t *src_areas,
45 snd_pcm_uframes_t src_offset,
46 unsigned int channels, snd_pcm_uframes_t frames,
47 unsigned int getputidx);
50 /* This field need to be the first */
51 snd_pcm_plugin_t plug;
52 unsigned int getput_idx;
54 snd_pcm_format_t sformat;
59 static inline int val_seg(int val)
77 * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
79 * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
81 * Linear Input Code Compressed Code
82 * ------------------------ ---------------
83 * 0000000wxyza 000wxyz
84 * 0000001wxyza 001wxyz
85 * 000001wxyzab 010wxyz
86 * 00001wxyzabc 011wxyz
87 * 0001wxyzabcd 100wxyz
88 * 001wxyzabcde 101wxyz
89 * 01wxyzabcdef 110wxyz
90 * 1wxyzabcdefg 111wxyz
92 * For further information see John C. Bellamy's Digital Telephony, 1982,
93 * John Wiley & Sons, pps 98-111 and 472-476.
96 static unsigned char s16_to_alaw(int pcm_val)
107 if (pcm_val > 0x7fff)
114 /* Convert the scaled magnitude to segment number. */
115 seg = val_seg(pcm_val);
116 aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
122 * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
125 static int alaw_to_s16(unsigned char a_val)
135 seg = (t >> 4) & 0x07;
136 t = ((t & 0x0f) << 4) + 0x108;
139 return ((a_val & 0x80) ? t : -t);
144 void snd_pcm_alaw_decode(const snd_pcm_channel_area_t *dst_areas,
145 snd_pcm_uframes_t dst_offset,
146 const snd_pcm_channel_area_t *src_areas,
147 snd_pcm_uframes_t src_offset,
148 unsigned int channels, snd_pcm_uframes_t frames,
152 #include "plugin_ops.h"
154 void *put = put16_labels[putidx];
155 unsigned int channel;
156 for (channel = 0; channel < channels; ++channel) {
157 const unsigned char *src;
159 int src_step, dst_step;
160 snd_pcm_uframes_t frames1;
161 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
162 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
163 src = snd_pcm_channel_area_addr(src_area, src_offset);
164 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
165 src_step = snd_pcm_channel_area_step(src_area);
166 dst_step = snd_pcm_channel_area_step(dst_area);
168 while (frames1-- > 0) {
169 int16_t sample = alaw_to_s16(*src);
171 #define PUT16_END after
172 #include "plugin_ops.h"
181 void snd_pcm_alaw_encode(const snd_pcm_channel_area_t *dst_areas,
182 snd_pcm_uframes_t dst_offset,
183 const snd_pcm_channel_area_t *src_areas,
184 snd_pcm_uframes_t src_offset,
185 unsigned int channels, snd_pcm_uframes_t frames,
189 #include "plugin_ops.h"
191 void *get = get16_labels[getidx];
192 unsigned int channel;
194 for (channel = 0; channel < channels; ++channel) {
197 int src_step, dst_step;
198 snd_pcm_uframes_t frames1;
199 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
200 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
201 src = snd_pcm_channel_area_addr(src_area, src_offset);
202 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
203 src_step = snd_pcm_channel_area_step(src_area);
204 dst_step = snd_pcm_channel_area_step(dst_area);
206 while (frames1-- > 0) {
208 #define GET16_END after
209 #include "plugin_ops.h"
212 *dst = s16_to_alaw(sample);
219 #endif /* DOC_HIDDEN */
221 static int snd_pcm_alaw_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
223 snd_pcm_alaw_t *alaw = pcm->private_data;
225 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
226 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
230 if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
231 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
232 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
235 err = _snd_pcm_hw_params_set_format(params,
236 SND_PCM_FORMAT_A_LAW);
240 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
243 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
247 static int snd_pcm_alaw_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
249 snd_pcm_alaw_t *alaw = pcm->private_data;
250 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
251 _snd_pcm_hw_params_any(sparams);
252 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
254 _snd_pcm_hw_params_set_format(sparams, alaw->sformat);
255 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
259 static int snd_pcm_alaw_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
260 snd_pcm_hw_params_t *sparams)
263 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
264 SND_PCM_HW_PARBIT_RATE |
265 SND_PCM_HW_PARBIT_PERIOD_SIZE |
266 SND_PCM_HW_PARBIT_BUFFER_SIZE |
267 SND_PCM_HW_PARBIT_PERIODS |
268 SND_PCM_HW_PARBIT_PERIOD_TIME |
269 SND_PCM_HW_PARBIT_BUFFER_TIME |
270 SND_PCM_HW_PARBIT_TICK_TIME);
271 err = _snd_pcm_hw_params_refine(sparams, links, params);
277 static int snd_pcm_alaw_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
278 snd_pcm_hw_params_t *sparams)
281 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
282 SND_PCM_HW_PARBIT_RATE |
283 SND_PCM_HW_PARBIT_PERIOD_SIZE |
284 SND_PCM_HW_PARBIT_BUFFER_SIZE |
285 SND_PCM_HW_PARBIT_PERIODS |
286 SND_PCM_HW_PARBIT_PERIOD_TIME |
287 SND_PCM_HW_PARBIT_BUFFER_TIME |
288 SND_PCM_HW_PARBIT_TICK_TIME);
289 err = _snd_pcm_hw_params_refine(params, links, sparams);
295 static int snd_pcm_alaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
297 return snd_pcm_hw_refine_slave(pcm, params,
298 snd_pcm_alaw_hw_refine_cprepare,
299 snd_pcm_alaw_hw_refine_cchange,
300 snd_pcm_alaw_hw_refine_sprepare,
301 snd_pcm_alaw_hw_refine_schange,
302 snd_pcm_generic_hw_refine);
305 static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
307 snd_pcm_alaw_t *alaw = pcm->private_data;
308 snd_pcm_format_t format;
309 int err = snd_pcm_hw_params_slave(pcm, params,
310 snd_pcm_alaw_hw_refine_cchange,
311 snd_pcm_alaw_hw_refine_sprepare,
312 snd_pcm_alaw_hw_refine_schange,
313 snd_pcm_generic_hw_params);
317 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
321 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
322 if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
323 alaw->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S16);
324 alaw->func = snd_pcm_alaw_encode;
326 alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, alaw->sformat);
327 alaw->func = snd_pcm_alaw_decode;
330 if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
331 alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, format);
332 alaw->func = snd_pcm_alaw_decode;
334 alaw->getput_idx = snd_pcm_linear_get_index(alaw->sformat, SND_PCM_FORMAT_S16);
335 alaw->func = snd_pcm_alaw_encode;
341 static snd_pcm_uframes_t
342 snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
343 const snd_pcm_channel_area_t *areas,
344 snd_pcm_uframes_t offset,
345 snd_pcm_uframes_t size,
346 const snd_pcm_channel_area_t *slave_areas,
347 snd_pcm_uframes_t slave_offset,
348 snd_pcm_uframes_t *slave_sizep)
350 snd_pcm_alaw_t *alaw = pcm->private_data;
351 if (size > *slave_sizep)
353 alaw->func(slave_areas, slave_offset,
361 static snd_pcm_uframes_t
362 snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
363 const snd_pcm_channel_area_t *areas,
364 snd_pcm_uframes_t offset,
365 snd_pcm_uframes_t size,
366 const snd_pcm_channel_area_t *slave_areas,
367 snd_pcm_uframes_t slave_offset,
368 snd_pcm_uframes_t *slave_sizep)
370 snd_pcm_alaw_t *alaw = pcm->private_data;
371 if (size > *slave_sizep)
373 alaw->func(areas, offset,
374 slave_areas, slave_offset,
381 static void snd_pcm_alaw_dump(snd_pcm_t *pcm, snd_output_t *out)
383 snd_pcm_alaw_t *alaw = pcm->private_data;
384 snd_output_printf(out, "A-Law conversion PCM (%s)\n",
385 snd_pcm_format_name(alaw->sformat));
387 snd_output_printf(out, "Its setup is:\n");
388 snd_pcm_dump_setup(pcm, out);
390 snd_output_printf(out, "Slave: ");
391 snd_pcm_dump(alaw->plug.gen.slave, out);
394 static const snd_pcm_ops_t snd_pcm_alaw_ops = {
395 .close = snd_pcm_generic_close,
396 .info = snd_pcm_generic_info,
397 .hw_refine = snd_pcm_alaw_hw_refine,
398 .hw_params = snd_pcm_alaw_hw_params,
399 .hw_free = snd_pcm_generic_hw_free,
400 .sw_params = snd_pcm_generic_sw_params,
401 .channel_info = snd_pcm_generic_channel_info,
402 .dump = snd_pcm_alaw_dump,
403 .nonblock = snd_pcm_generic_nonblock,
404 .async = snd_pcm_generic_async,
405 .mmap = snd_pcm_generic_mmap,
406 .munmap = snd_pcm_generic_munmap,
410 * \brief Creates a new A-Law conversion PCM
411 * \param pcmp Returns created PCM handle
412 * \param name Name of PCM
413 * \param sformat Slave (destination) format
414 * \param slave Slave PCM handle
415 * \param close_slave When set, the slave PCM handle is closed with copy PCM
416 * \retval zero on success otherwise a negative error code
417 * \warning Using of this function might be dangerous in the sense
418 * of compatibility reasons. The prototype might be freely
421 int snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
424 snd_pcm_alaw_t *alaw;
426 assert(pcmp && slave);
427 if (snd_pcm_format_linear(sformat) != 1 &&
428 sformat != SND_PCM_FORMAT_A_LAW)
430 alaw = calloc(1, sizeof(snd_pcm_alaw_t));
434 snd_pcm_plugin_init(&alaw->plug);
435 alaw->sformat = sformat;
436 alaw->plug.read = snd_pcm_alaw_read_areas;
437 alaw->plug.write = snd_pcm_alaw_write_areas;
438 alaw->plug.undo_read = snd_pcm_plugin_undo_read_generic;
439 alaw->plug.undo_write = snd_pcm_plugin_undo_write_generic;
440 alaw->plug.gen.slave = slave;
441 alaw->plug.gen.close_slave = close_slave;
443 err = snd_pcm_new(&pcm, SND_PCM_TYPE_ALAW, name, slave->stream, slave->mode);
448 pcm->ops = &snd_pcm_alaw_ops;
449 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
450 pcm->private_data = alaw;
451 pcm->poll_fd = slave->poll_fd;
452 pcm->poll_events = slave->poll_events;
453 pcm->monotonic = slave->monotonic;
454 snd_pcm_set_hw_ptr(pcm, &alaw->plug.hw_ptr, -1, 0);
455 snd_pcm_set_appl_ptr(pcm, &alaw->plug.appl_ptr, -1, 0);
461 /*! \page pcm_plugins
463 \section pcm_plugins_alaw Plugin: A-Law
465 This plugin converts A-Law samples to linear or linear to A-Law samples
466 from master A-Law conversion PCM to given slave PCM. The channel count,
467 format and rate must match for both of them.
471 type alaw # A-Law conversion PCM
472 slave STR # Slave name
474 slave { # Slave definition
475 pcm STR # Slave PCM name
477 pcm { } # Slave PCM definition
478 format STR # Slave format
483 \subsection pcm_plugins_alaw_funcref Function reference
486 <LI>snd_pcm_alaw_open()
487 <LI>_snd_pcm_alaw_open()
493 * \brief Creates a new A-Law conversion PCM
494 * \param pcmp Returns created PCM handle
495 * \param name Name of PCM
496 * \param root Root configuration node
497 * \param conf Configuration node with copy PCM description
498 * \param stream Stream type
499 * \param mode Stream mode
500 * \retval zero on success otherwise a negative error code
501 * \warning Using of this function might be dangerous in the sense
502 * of compatibility reasons. The prototype might be freely
505 int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
506 snd_config_t *root, snd_config_t *conf,
507 snd_pcm_stream_t stream, int mode)
509 snd_config_iterator_t i, next;
512 snd_config_t *slave = NULL, *sconf;
513 snd_pcm_format_t sformat;
514 snd_config_for_each(i, next, conf) {
515 snd_config_t *n = snd_config_iterator_entry(i);
517 if (snd_config_get_id(n, &id) < 0)
519 if (snd_pcm_conf_generic_id(id))
521 if (strcmp(id, "slave") == 0) {
525 SNDERR("Unknown field %s", id);
529 SNDERR("slave is not defined");
532 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
533 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
536 if (snd_pcm_format_linear(sformat) != 1 &&
537 sformat != SND_PCM_FORMAT_A_LAW) {
538 snd_config_delete(sconf);
539 SNDERR("invalid slave format");
542 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
543 snd_config_delete(sconf);
546 err = snd_pcm_alaw_open(pcmp, name, sformat, spcm, 1);
552 SND_DLSYM_BUILD_VERSION(_snd_pcm_alaw_open, SND_PCM_DLSYM_VERSION);