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_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
112 int sign, width, pwidth, endian;
113 sign = (snd_pcm_format_signed(src_format) !=
114 snd_pcm_format_signed(dst_format));
115 #ifdef SND_LITTLE_ENDIAN
116 endian = snd_pcm_format_big_endian(dst_format);
118 endian = snd_pcm_format_little_endian(dst_format);
122 pwidth = snd_pcm_format_physical_width(dst_format);
123 width = snd_pcm_format_width(dst_format);
134 return width * 4 + endian * 2 + sign + 16;
136 width = width / 8 - 1;
137 return width * 4 + endian * 2 + sign;
141 void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
142 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
143 unsigned int channels, snd_pcm_uframes_t frames,
144 unsigned int convidx)
147 #include "plugin_ops.h"
149 void *conv = conv_labels[convidx];
150 unsigned int channel;
151 for (channel = 0; channel < channels; ++channel) {
154 int src_step, dst_step;
155 snd_pcm_uframes_t frames1;
156 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
157 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
158 src = snd_pcm_channel_area_addr(src_area, src_offset);
159 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
160 src_step = snd_pcm_channel_area_step(src_area);
161 dst_step = snd_pcm_channel_area_step(dst_area);
163 while (frames1-- > 0) {
165 #define CONV_END after
166 #include "plugin_ops.h"
175 void snd_pcm_linear_getput(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
176 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
177 unsigned int channels, snd_pcm_uframes_t frames,
178 unsigned int get_idx, unsigned int put_idx)
180 #define CONV24_LABELS
181 #include "plugin_ops.h"
183 void *get = get32_labels[get_idx];
184 void *put = put32_labels[put_idx];
185 unsigned int channel;
186 u_int32_t sample = 0;
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 CONV24_END after
202 #include "plugin_ops.h"
211 #endif /* DOC_HIDDEN */
213 static int snd_pcm_linear_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
216 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
217 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
218 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
222 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
226 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
229 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
233 static int snd_pcm_linear_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
235 snd_pcm_linear_t *linear = pcm->private_data;
236 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
237 _snd_pcm_hw_params_any(sparams);
238 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
240 _snd_pcm_hw_params_set_format(sparams, linear->sformat);
241 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
245 static int snd_pcm_linear_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
246 snd_pcm_hw_params_t *sparams)
249 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
250 SND_PCM_HW_PARBIT_RATE |
251 SND_PCM_HW_PARBIT_PERIOD_SIZE |
252 SND_PCM_HW_PARBIT_BUFFER_SIZE |
253 SND_PCM_HW_PARBIT_PERIODS |
254 SND_PCM_HW_PARBIT_PERIOD_TIME |
255 SND_PCM_HW_PARBIT_BUFFER_TIME |
256 SND_PCM_HW_PARBIT_TICK_TIME);
257 err = _snd_pcm_hw_params_refine(sparams, links, params);
263 static int snd_pcm_linear_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
264 snd_pcm_hw_params_t *sparams)
267 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
268 SND_PCM_HW_PARBIT_RATE |
269 SND_PCM_HW_PARBIT_PERIOD_SIZE |
270 SND_PCM_HW_PARBIT_BUFFER_SIZE |
271 SND_PCM_HW_PARBIT_PERIODS |
272 SND_PCM_HW_PARBIT_PERIOD_TIME |
273 SND_PCM_HW_PARBIT_BUFFER_TIME |
274 SND_PCM_HW_PARBIT_TICK_TIME);
275 err = _snd_pcm_hw_params_refine(params, links, sparams);
281 static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
283 return snd_pcm_hw_refine_slave(pcm, params,
284 snd_pcm_linear_hw_refine_cprepare,
285 snd_pcm_linear_hw_refine_cchange,
286 snd_pcm_linear_hw_refine_sprepare,
287 snd_pcm_linear_hw_refine_schange,
288 snd_pcm_generic_hw_refine);
291 static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
293 snd_pcm_linear_t *linear = pcm->private_data;
294 snd_pcm_format_t format;
295 int err = snd_pcm_hw_params_slave(pcm, params,
296 snd_pcm_linear_hw_refine_cchange,
297 snd_pcm_linear_hw_refine_sprepare,
298 snd_pcm_linear_hw_refine_schange,
299 snd_pcm_generic_hw_params);
302 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
305 linear->use_getput = (snd_pcm_format_physical_width(format) == 24 ||
306 snd_pcm_format_physical_width(linear->sformat) == 24);
307 if (linear->use_getput) {
308 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
309 linear->get_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
310 linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, linear->sformat);
312 linear->get_idx = snd_pcm_linear_get_index(linear->sformat, SND_PCM_FORMAT_S32);
313 linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, format);
316 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
317 linear->conv_idx = snd_pcm_linear_convert_index(format,
320 linear->conv_idx = snd_pcm_linear_convert_index(linear->sformat,
326 static snd_pcm_uframes_t
327 snd_pcm_linear_write_areas(snd_pcm_t *pcm,
328 const snd_pcm_channel_area_t *areas,
329 snd_pcm_uframes_t offset,
330 snd_pcm_uframes_t size,
331 const snd_pcm_channel_area_t *slave_areas,
332 snd_pcm_uframes_t slave_offset,
333 snd_pcm_uframes_t *slave_sizep)
335 snd_pcm_linear_t *linear = pcm->private_data;
336 if (size > *slave_sizep)
338 if (linear->use_getput)
339 snd_pcm_linear_getput(slave_areas, slave_offset,
342 linear->get_idx, linear->put_idx);
344 snd_pcm_linear_convert(slave_areas, slave_offset,
346 pcm->channels, size, linear->conv_idx);
351 static snd_pcm_uframes_t
352 snd_pcm_linear_read_areas(snd_pcm_t *pcm,
353 const snd_pcm_channel_area_t *areas,
354 snd_pcm_uframes_t offset,
355 snd_pcm_uframes_t size,
356 const snd_pcm_channel_area_t *slave_areas,
357 snd_pcm_uframes_t slave_offset,
358 snd_pcm_uframes_t *slave_sizep)
360 snd_pcm_linear_t *linear = pcm->private_data;
361 if (size > *slave_sizep)
363 if (linear->use_getput)
364 snd_pcm_linear_getput(areas, offset,
365 slave_areas, slave_offset,
367 linear->get_idx, linear->put_idx);
369 snd_pcm_linear_convert(areas, offset,
370 slave_areas, slave_offset,
371 pcm->channels, size, linear->conv_idx);
376 static void snd_pcm_linear_dump(snd_pcm_t *pcm, snd_output_t *out)
378 snd_pcm_linear_t *linear = pcm->private_data;
379 snd_output_printf(out, "Linear conversion PCM (%s)\n",
380 snd_pcm_format_name(linear->sformat));
382 snd_output_printf(out, "Its setup is:\n");
383 snd_pcm_dump_setup(pcm, out);
385 snd_output_printf(out, "Slave: ");
386 snd_pcm_dump(linear->plug.gen.slave, out);
389 static const snd_pcm_ops_t snd_pcm_linear_ops = {
390 .close = snd_pcm_generic_close,
391 .info = snd_pcm_generic_info,
392 .hw_refine = snd_pcm_linear_hw_refine,
393 .hw_params = snd_pcm_linear_hw_params,
394 .hw_free = snd_pcm_generic_hw_free,
395 .sw_params = snd_pcm_generic_sw_params,
396 .channel_info = snd_pcm_generic_channel_info,
397 .dump = snd_pcm_linear_dump,
398 .nonblock = snd_pcm_generic_nonblock,
399 .async = snd_pcm_generic_async,
400 .mmap = snd_pcm_generic_mmap,
401 .munmap = snd_pcm_generic_munmap,
402 .query_chmaps = snd_pcm_generic_query_chmaps,
403 .get_chmap = snd_pcm_generic_get_chmap,
404 .set_chmap = snd_pcm_generic_set_chmap,
409 * \brief Creates a new linear conversion PCM
410 * \param pcmp Returns created PCM handle
411 * \param name Name of PCM
412 * \param sformat Slave (destination) format
413 * \param slave Slave PCM handle
414 * \param close_slave When set, the slave PCM handle is closed with copy PCM
415 * \retval zero on success otherwise a negative error code
416 * \warning Using of this function might be dangerous in the sense
417 * of compatibility reasons. The prototype might be freely
420 int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
423 snd_pcm_linear_t *linear;
425 assert(pcmp && slave);
426 if (snd_pcm_format_linear(sformat) != 1)
428 linear = calloc(1, sizeof(snd_pcm_linear_t));
432 snd_pcm_plugin_init(&linear->plug);
433 linear->sformat = sformat;
434 linear->plug.read = snd_pcm_linear_read_areas;
435 linear->plug.write = snd_pcm_linear_write_areas;
436 linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
437 linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
438 linear->plug.gen.slave = slave;
439 linear->plug.gen.close_slave = close_slave;
441 err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR, name, slave->stream, slave->mode);
446 pcm->ops = &snd_pcm_linear_ops;
447 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
448 pcm->private_data = linear;
449 pcm->poll_fd = slave->poll_fd;
450 pcm->poll_events = slave->poll_events;
451 pcm->tstamp_type = slave->tstamp_type;
452 snd_pcm_set_hw_ptr(pcm, &linear->plug.hw_ptr, -1, 0);
453 snd_pcm_set_appl_ptr(pcm, &linear->plug.appl_ptr, -1, 0);
459 /*! \page pcm_plugins
461 \section pcm_plugins_linear Plugin: linear
463 This plugin converts linear samples from master linear conversion PCM to given
464 slave PCM. The channel count, format and rate must match for both of them.
468 type linear # Linear conversion PCM
469 slave STR # Slave name
471 slave { # Slave definition
472 pcm STR # Slave PCM name
474 pcm { } # Slave PCM definition
475 format STR # Slave format
480 \subsection pcm_plugins_linear_funcref Function reference
483 <LI>snd_pcm_linear_open()
484 <LI>_snd_pcm_linear_open()
490 * \brief Creates a new linear conversion PCM
491 * \param pcmp Returns created PCM handle
492 * \param name Name of PCM
493 * \param root Root configuration node
494 * \param conf Configuration node with copy PCM description
495 * \param stream Stream type
496 * \param mode Stream mode
497 * \retval zero on success otherwise a negative error code
498 * \warning Using of this function might be dangerous in the sense
499 * of compatibility reasons. The prototype might be freely
502 int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
503 snd_config_t *root, snd_config_t *conf,
504 snd_pcm_stream_t stream, int mode)
506 snd_config_iterator_t i, next;
509 snd_config_t *slave = NULL, *sconf;
510 snd_pcm_format_t sformat;
511 snd_config_for_each(i, next, conf) {
512 snd_config_t *n = snd_config_iterator_entry(i);
514 if (snd_config_get_id(n, &id) < 0)
516 if (snd_pcm_conf_generic_id(id))
518 if (strcmp(id, "slave") == 0) {
522 SNDERR("Unknown field %s", id);
526 SNDERR("slave is not defined");
529 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
530 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
533 if (snd_pcm_format_linear(sformat) != 1) {
534 snd_config_delete(sconf);
535 SNDERR("slave format is not linear");
538 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
539 snd_config_delete(sconf);
542 err = snd_pcm_linear_open(pcmp, name, sformat, spcm, 1);
548 SND_DLSYM_BUILD_VERSION(_snd_pcm_linear_open, SND_PCM_DLSYM_VERSION);