2 * \file pcm/pcm_linear.c
4 * \brief PCM Linear Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
9 * PCM - Linear 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_linear = "";
42 /* This field need to be the first */
43 snd_pcm_plugin_t plug;
44 unsigned int use_getput;
45 unsigned int conv_idx;
46 unsigned int get_idx, put_idx;
47 snd_pcm_format_t sformat;
53 int snd_pcm_linear_convert_index(snd_pcm_format_t src_format,
54 snd_pcm_format_t dst_format)
56 int src_endian, dst_endian, sign, src_width, dst_width;
58 sign = (snd_pcm_format_signed(src_format) !=
59 snd_pcm_format_signed(dst_format));
60 #ifdef SND_LITTLE_ENDIAN
61 src_endian = snd_pcm_format_big_endian(src_format);
62 dst_endian = snd_pcm_format_big_endian(dst_format);
64 src_endian = snd_pcm_format_little_endian(src_format);
65 dst_endian = snd_pcm_format_little_endian(dst_format);
73 src_width = snd_pcm_format_width(src_format) / 8 - 1;
74 dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
76 return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
79 int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
81 int sign, width, pwidth, endian;
82 sign = (snd_pcm_format_signed(src_format) !=
83 snd_pcm_format_signed(dst_format));
84 #ifdef SND_LITTLE_ENDIAN
85 endian = snd_pcm_format_big_endian(src_format);
87 endian = snd_pcm_format_little_endian(src_format);
91 pwidth = snd_pcm_format_physical_width(src_format);
92 width = snd_pcm_format_width(src_format);
103 return width * 4 + endian * 2 + sign + 16;
105 width = width / 8 - 1;
106 return width * 4 + endian * 2 + sign;
110 int snd_pcm_linear_get32_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
112 return snd_pcm_linear_get_index(src_format, dst_format);
115 int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
117 int sign, width, pwidth, endian;
118 sign = (snd_pcm_format_signed(src_format) !=
119 snd_pcm_format_signed(dst_format));
120 #ifdef SND_LITTLE_ENDIAN
121 endian = snd_pcm_format_big_endian(dst_format);
123 endian = snd_pcm_format_little_endian(dst_format);
127 pwidth = snd_pcm_format_physical_width(dst_format);
128 width = snd_pcm_format_width(dst_format);
139 return width * 4 + endian * 2 + sign + 16;
141 width = width / 8 - 1;
142 return width * 4 + endian * 2 + sign;
146 int snd_pcm_linear_put32_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
148 int sign, width, pwidth, endian;
149 sign = (snd_pcm_format_signed(src_format) !=
150 snd_pcm_format_signed(dst_format));
151 #ifdef SND_LITTLE_ENDIAN
152 endian = snd_pcm_format_big_endian(dst_format);
154 endian = snd_pcm_format_little_endian(dst_format);
158 pwidth = snd_pcm_format_physical_width(dst_format);
159 width = snd_pcm_format_width(dst_format);
170 return width * 4 + endian * 2 + sign + 16;
172 width = width / 8 - 1;
173 return width * 4 + endian * 2 + sign;
177 void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
178 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
179 unsigned int channels, snd_pcm_uframes_t frames,
180 unsigned int convidx)
183 #include "plugin_ops.h"
185 void *conv = conv_labels[convidx];
186 unsigned int channel;
187 for (channel = 0; channel < channels; ++channel) {
190 int src_step, dst_step;
191 snd_pcm_uframes_t frames1;
192 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
193 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
194 src = snd_pcm_channel_area_addr(src_area, src_offset);
195 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
196 src_step = snd_pcm_channel_area_step(src_area);
197 dst_step = snd_pcm_channel_area_step(dst_area);
199 while (frames1-- > 0) {
201 #define CONV_END after
202 #include "plugin_ops.h"
211 void snd_pcm_linear_getput(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
212 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
213 unsigned int channels, snd_pcm_uframes_t frames,
214 unsigned int get_idx, unsigned int put_idx)
216 #define CONV24_LABELS
217 #include "plugin_ops.h"
219 void *get = get32_labels[get_idx];
220 void *put = put32_labels[put_idx];
221 unsigned int channel;
222 u_int32_t sample = 0;
223 for (channel = 0; channel < channels; ++channel) {
226 int src_step, dst_step;
227 snd_pcm_uframes_t frames1;
228 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
229 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
230 src = snd_pcm_channel_area_addr(src_area, src_offset);
231 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
232 src_step = snd_pcm_channel_area_step(src_area);
233 dst_step = snd_pcm_channel_area_step(dst_area);
235 while (frames1-- > 0) {
237 #define CONV24_END after
238 #include "plugin_ops.h"
247 #endif /* DOC_HIDDEN */
249 static int snd_pcm_linear_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
252 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
253 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
254 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
258 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
262 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
265 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
269 static int snd_pcm_linear_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
271 snd_pcm_linear_t *linear = pcm->private_data;
272 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
273 _snd_pcm_hw_params_any(sparams);
274 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
276 _snd_pcm_hw_params_set_format(sparams, linear->sformat);
277 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
281 static int snd_pcm_linear_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
282 snd_pcm_hw_params_t *sparams)
285 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
286 SND_PCM_HW_PARBIT_RATE |
287 SND_PCM_HW_PARBIT_PERIOD_SIZE |
288 SND_PCM_HW_PARBIT_BUFFER_SIZE |
289 SND_PCM_HW_PARBIT_PERIODS |
290 SND_PCM_HW_PARBIT_PERIOD_TIME |
291 SND_PCM_HW_PARBIT_BUFFER_TIME |
292 SND_PCM_HW_PARBIT_TICK_TIME);
293 err = _snd_pcm_hw_params_refine(sparams, links, params);
299 static int snd_pcm_linear_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
300 snd_pcm_hw_params_t *sparams)
303 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
304 SND_PCM_HW_PARBIT_RATE |
305 SND_PCM_HW_PARBIT_PERIOD_SIZE |
306 SND_PCM_HW_PARBIT_BUFFER_SIZE |
307 SND_PCM_HW_PARBIT_PERIODS |
308 SND_PCM_HW_PARBIT_PERIOD_TIME |
309 SND_PCM_HW_PARBIT_BUFFER_TIME |
310 SND_PCM_HW_PARBIT_TICK_TIME);
311 err = _snd_pcm_hw_params_refine(params, links, sparams);
317 static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
319 return snd_pcm_hw_refine_slave(pcm, params,
320 snd_pcm_linear_hw_refine_cprepare,
321 snd_pcm_linear_hw_refine_cchange,
322 snd_pcm_linear_hw_refine_sprepare,
323 snd_pcm_linear_hw_refine_schange,
324 snd_pcm_generic_hw_refine);
327 static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
329 snd_pcm_linear_t *linear = pcm->private_data;
330 snd_pcm_format_t format;
331 int err = snd_pcm_hw_params_slave(pcm, params,
332 snd_pcm_linear_hw_refine_cchange,
333 snd_pcm_linear_hw_refine_sprepare,
334 snd_pcm_linear_hw_refine_schange,
335 snd_pcm_generic_hw_params);
338 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
341 linear->use_getput = (snd_pcm_format_physical_width(format) == 24 ||
342 snd_pcm_format_physical_width(linear->sformat) == 24);
343 if (linear->use_getput) {
344 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
345 linear->get_idx = snd_pcm_linear_get32_index(format, SND_PCM_FORMAT_S32);
346 linear->put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, linear->sformat);
348 linear->get_idx = snd_pcm_linear_get32_index(linear->sformat, SND_PCM_FORMAT_S32);
349 linear->put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, format);
352 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
353 linear->conv_idx = snd_pcm_linear_convert_index(format,
356 linear->conv_idx = snd_pcm_linear_convert_index(linear->sformat,
362 static snd_pcm_uframes_t
363 snd_pcm_linear_write_areas(snd_pcm_t *pcm,
364 const snd_pcm_channel_area_t *areas,
365 snd_pcm_uframes_t offset,
366 snd_pcm_uframes_t size,
367 const snd_pcm_channel_area_t *slave_areas,
368 snd_pcm_uframes_t slave_offset,
369 snd_pcm_uframes_t *slave_sizep)
371 snd_pcm_linear_t *linear = pcm->private_data;
372 if (size > *slave_sizep)
374 if (linear->use_getput)
375 snd_pcm_linear_getput(slave_areas, slave_offset,
378 linear->get_idx, linear->put_idx);
380 snd_pcm_linear_convert(slave_areas, slave_offset,
382 pcm->channels, size, linear->conv_idx);
387 static snd_pcm_uframes_t
388 snd_pcm_linear_read_areas(snd_pcm_t *pcm,
389 const snd_pcm_channel_area_t *areas,
390 snd_pcm_uframes_t offset,
391 snd_pcm_uframes_t size,
392 const snd_pcm_channel_area_t *slave_areas,
393 snd_pcm_uframes_t slave_offset,
394 snd_pcm_uframes_t *slave_sizep)
396 snd_pcm_linear_t *linear = pcm->private_data;
397 if (size > *slave_sizep)
399 if (linear->use_getput)
400 snd_pcm_linear_getput(areas, offset,
401 slave_areas, slave_offset,
403 linear->get_idx, linear->put_idx);
405 snd_pcm_linear_convert(areas, offset,
406 slave_areas, slave_offset,
407 pcm->channels, size, linear->conv_idx);
412 static void snd_pcm_linear_dump(snd_pcm_t *pcm, snd_output_t *out)
414 snd_pcm_linear_t *linear = pcm->private_data;
415 snd_output_printf(out, "Linear conversion PCM (%s)\n",
416 snd_pcm_format_name(linear->sformat));
418 snd_output_printf(out, "Its setup is:\n");
419 snd_pcm_dump_setup(pcm, out);
421 snd_output_printf(out, "Slave: ");
422 snd_pcm_dump(linear->plug.gen.slave, out);
425 static const snd_pcm_ops_t snd_pcm_linear_ops = {
426 .close = snd_pcm_generic_close,
427 .info = snd_pcm_generic_info,
428 .hw_refine = snd_pcm_linear_hw_refine,
429 .hw_params = snd_pcm_linear_hw_params,
430 .hw_free = snd_pcm_generic_hw_free,
431 .sw_params = snd_pcm_generic_sw_params,
432 .channel_info = snd_pcm_generic_channel_info,
433 .dump = snd_pcm_linear_dump,
434 .nonblock = snd_pcm_generic_nonblock,
435 .async = snd_pcm_generic_async,
436 .mmap = snd_pcm_generic_mmap,
437 .munmap = snd_pcm_generic_munmap,
442 * \brief Creates a new linear conversion PCM
443 * \param pcmp Returns created PCM handle
444 * \param name Name of PCM
445 * \param sformat Slave (destination) format
446 * \param slave Slave PCM handle
447 * \param close_slave When set, the slave PCM handle is closed with copy PCM
448 * \retval zero on success otherwise a negative error code
449 * \warning Using of this function might be dangerous in the sense
450 * of compatibility reasons. The prototype might be freely
453 int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
456 snd_pcm_linear_t *linear;
458 assert(pcmp && slave);
459 if (snd_pcm_format_linear(sformat) != 1)
461 linear = calloc(1, sizeof(snd_pcm_linear_t));
465 snd_pcm_plugin_init(&linear->plug);
466 linear->sformat = sformat;
467 linear->plug.read = snd_pcm_linear_read_areas;
468 linear->plug.write = snd_pcm_linear_write_areas;
469 linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
470 linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
471 linear->plug.gen.slave = slave;
472 linear->plug.gen.close_slave = close_slave;
474 err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR, name, slave->stream, slave->mode);
479 pcm->ops = &snd_pcm_linear_ops;
480 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
481 pcm->private_data = linear;
482 pcm->poll_fd = slave->poll_fd;
483 pcm->poll_events = slave->poll_events;
484 pcm->monotonic = slave->monotonic;
485 snd_pcm_set_hw_ptr(pcm, &linear->plug.hw_ptr, -1, 0);
486 snd_pcm_set_appl_ptr(pcm, &linear->plug.appl_ptr, -1, 0);
492 /*! \page pcm_plugins
494 \section pcm_plugins_linear Plugin: linear
496 This plugin converts linear samples from master linear conversion PCM to given
497 slave PCM. The channel count, format and rate must match for both of them.
501 type linear # Linear conversion PCM
502 slave STR # Slave name
504 slave { # Slave definition
505 pcm STR # Slave PCM name
507 pcm { } # Slave PCM definition
508 format STR # Slave format
513 \subsection pcm_plugins_linear_funcref Function reference
516 <LI>snd_pcm_linear_open()
517 <LI>_snd_pcm_linear_open()
523 * \brief Creates a new linear conversion PCM
524 * \param pcmp Returns created PCM handle
525 * \param name Name of PCM
526 * \param root Root configuration node
527 * \param conf Configuration node with copy PCM description
528 * \param stream Stream type
529 * \param mode Stream mode
530 * \retval zero on success otherwise a negative error code
531 * \warning Using of this function might be dangerous in the sense
532 * of compatibility reasons. The prototype might be freely
535 int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
536 snd_config_t *root, snd_config_t *conf,
537 snd_pcm_stream_t stream, int mode)
539 snd_config_iterator_t i, next;
542 snd_config_t *slave = NULL, *sconf;
543 snd_pcm_format_t sformat;
544 snd_config_for_each(i, next, conf) {
545 snd_config_t *n = snd_config_iterator_entry(i);
547 if (snd_config_get_id(n, &id) < 0)
549 if (snd_pcm_conf_generic_id(id))
551 if (strcmp(id, "slave") == 0) {
555 SNDERR("Unknown field %s", id);
559 SNDERR("slave is not defined");
562 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
563 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
566 if (snd_pcm_format_linear(sformat) != 1) {
567 snd_config_delete(sconf);
568 SNDERR("slave format is not linear");
571 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
572 snd_config_delete(sconf);
575 err = snd_pcm_linear_open(pcmp, name, sformat, spcm, 1);
581 SND_DLSYM_BUILD_VERSION(_snd_pcm_linear_open, SND_PCM_DLSYM_VERSION);