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"
34
/* entry for static linking */
35
const char *_snd_module_pcm_linear = "";
40
/* This field need to be the first */
41
snd_pcm_plugin_t plug;
42
unsigned int use_getput;
43
unsigned int conv_idx;
44
unsigned int get_idx, put_idx;
45
snd_pcm_format_t sformat;
51
int snd_pcm_linear_convert_index(snd_pcm_format_t src_format,
52
snd_pcm_format_t dst_format)
54
int src_endian, dst_endian, sign, src_width, dst_width;
56
sign = (snd_pcm_format_signed(src_format) !=
57
snd_pcm_format_signed(dst_format));
58
#ifdef SND_LITTLE_ENDIAN
59
src_endian = snd_pcm_format_big_endian(src_format);
60
dst_endian = snd_pcm_format_big_endian(dst_format);
62
src_endian = snd_pcm_format_little_endian(src_format);
63
dst_endian = snd_pcm_format_little_endian(dst_format);
71
src_width = snd_pcm_format_width(src_format) / 8 - 1;
72
dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
74
return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
77
int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
79
int sign, width, pwidth, endian;
80
sign = (snd_pcm_format_signed(src_format) !=
81
snd_pcm_format_signed(dst_format));
82
#ifdef SND_LITTLE_ENDIAN
83
endian = snd_pcm_format_big_endian(src_format);
85
endian = snd_pcm_format_little_endian(src_format);
89
pwidth = snd_pcm_format_physical_width(src_format);
90
width = snd_pcm_format_width(src_format);
101
return width * 4 + endian * 2 + sign + 16;
103
width = width / 8 - 1;
104
return width * 4 + endian * 2 + sign;
108
int snd_pcm_linear_get32_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
110
return snd_pcm_linear_get_index(src_format, dst_format);
113
int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
115
int sign, width, endian;
116
sign = (snd_pcm_format_signed(src_format) !=
117
snd_pcm_format_signed(dst_format));
118
width = snd_pcm_format_width(dst_format) / 8 - 1;
119
#ifdef SND_LITTLE_ENDIAN
120
endian = snd_pcm_format_big_endian(dst_format);
122
endian = snd_pcm_format_little_endian(dst_format);
126
return width * 4 + endian * 2 + sign;
129
int snd_pcm_linear_put32_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
131
int sign, width, pwidth, endian;
132
sign = (snd_pcm_format_signed(src_format) !=
133
snd_pcm_format_signed(dst_format));
134
#ifdef SND_LITTLE_ENDIAN
135
endian = snd_pcm_format_big_endian(dst_format);
137
endian = snd_pcm_format_little_endian(dst_format);
141
pwidth = snd_pcm_format_physical_width(dst_format);
142
width = snd_pcm_format_width(dst_format);
153
return width * 4 + endian * 2 + sign + 16;
155
width = width / 8 - 1;
156
return width * 4 + endian * 2 + sign;
160
void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
161
const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
162
unsigned int channels, snd_pcm_uframes_t frames,
163
unsigned int convidx)
166
#include "plugin_ops.h"
168
void *conv = conv_labels[convidx];
169
unsigned int channel;
170
for (channel = 0; channel < channels; ++channel) {
173
int src_step, dst_step;
174
snd_pcm_uframes_t frames1;
175
const snd_pcm_channel_area_t *src_area = &src_areas[channel];
176
const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
177
src = snd_pcm_channel_area_addr(src_area, src_offset);
178
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
179
src_step = snd_pcm_channel_area_step(src_area);
180
dst_step = snd_pcm_channel_area_step(dst_area);
182
while (frames1-- > 0) {
184
#define CONV_END after
185
#include "plugin_ops.h"
194
void snd_pcm_linear_getput(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
195
const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
196
unsigned int channels, snd_pcm_uframes_t frames,
197
unsigned int get_idx, unsigned int put_idx)
199
#define CONV24_LABELS
200
#include "plugin_ops.h"
202
void *get = get32_labels[get_idx];
203
void *put = put32_labels[put_idx];
204
unsigned int channel;
205
u_int32_t sample = 0;
206
for (channel = 0; channel < channels; ++channel) {
209
int src_step, dst_step;
210
snd_pcm_uframes_t frames1;
211
const snd_pcm_channel_area_t *src_area = &src_areas[channel];
212
const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
213
src = snd_pcm_channel_area_addr(src_area, src_offset);
214
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
215
src_step = snd_pcm_channel_area_step(src_area);
216
dst_step = snd_pcm_channel_area_step(dst_area);
218
while (frames1-- > 0) {
220
#define CONV24_END after
221
#include "plugin_ops.h"
230
#endif /* DOC_HIDDEN */
232
static int snd_pcm_linear_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
235
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
236
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
237
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
241
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
245
err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
248
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
252
static int snd_pcm_linear_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
254
snd_pcm_linear_t *linear = pcm->private_data;
255
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
256
_snd_pcm_hw_params_any(sparams);
257
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
259
_snd_pcm_hw_params_set_format(sparams, linear->sformat);
260
_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
264
static int snd_pcm_linear_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
265
snd_pcm_hw_params_t *sparams)
268
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
269
SND_PCM_HW_PARBIT_RATE |
270
SND_PCM_HW_PARBIT_PERIOD_SIZE |
271
SND_PCM_HW_PARBIT_BUFFER_SIZE |
272
SND_PCM_HW_PARBIT_PERIODS |
273
SND_PCM_HW_PARBIT_PERIOD_TIME |
274
SND_PCM_HW_PARBIT_BUFFER_TIME |
275
SND_PCM_HW_PARBIT_TICK_TIME);
276
err = _snd_pcm_hw_params_refine(sparams, links, params);
282
static int snd_pcm_linear_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
283
snd_pcm_hw_params_t *sparams)
286
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
287
SND_PCM_HW_PARBIT_RATE |
288
SND_PCM_HW_PARBIT_PERIOD_SIZE |
289
SND_PCM_HW_PARBIT_BUFFER_SIZE |
290
SND_PCM_HW_PARBIT_PERIODS |
291
SND_PCM_HW_PARBIT_PERIOD_TIME |
292
SND_PCM_HW_PARBIT_BUFFER_TIME |
293
SND_PCM_HW_PARBIT_TICK_TIME);
294
err = _snd_pcm_hw_params_refine(params, links, sparams);
300
static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
302
return snd_pcm_hw_refine_slave(pcm, params,
303
snd_pcm_linear_hw_refine_cprepare,
304
snd_pcm_linear_hw_refine_cchange,
305
snd_pcm_linear_hw_refine_sprepare,
306
snd_pcm_linear_hw_refine_schange,
307
snd_pcm_plugin_hw_refine_slave);
310
static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
312
snd_pcm_linear_t *linear = pcm->private_data;
313
snd_pcm_format_t format;
314
int err = snd_pcm_hw_params_slave(pcm, params,
315
snd_pcm_linear_hw_refine_cchange,
316
snd_pcm_linear_hw_refine_sprepare,
317
snd_pcm_linear_hw_refine_schange,
318
snd_pcm_plugin_hw_params_slave);
321
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
324
linear->use_getput = (snd_pcm_format_physical_width(format) == 24 ||
325
snd_pcm_format_physical_width(linear->sformat) == 24);
326
if (linear->use_getput) {
327
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
328
linear->get_idx = snd_pcm_linear_get32_index(format, SND_PCM_FORMAT_S32);
329
linear->put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, linear->sformat);
331
linear->get_idx = snd_pcm_linear_get32_index(linear->sformat, SND_PCM_FORMAT_S32);
332
linear->put_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, format);
335
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
336
linear->conv_idx = snd_pcm_linear_convert_index(format,
339
linear->conv_idx = snd_pcm_linear_convert_index(linear->sformat,
345
static snd_pcm_uframes_t
346
snd_pcm_linear_write_areas(snd_pcm_t *pcm,
347
const snd_pcm_channel_area_t *areas,
348
snd_pcm_uframes_t offset,
349
snd_pcm_uframes_t size,
350
const snd_pcm_channel_area_t *slave_areas,
351
snd_pcm_uframes_t slave_offset,
352
snd_pcm_uframes_t *slave_sizep)
354
snd_pcm_linear_t *linear = pcm->private_data;
355
if (size > *slave_sizep)
357
if (linear->use_getput)
358
snd_pcm_linear_getput(slave_areas, slave_offset,
361
linear->get_idx, linear->put_idx);
363
snd_pcm_linear_convert(slave_areas, slave_offset,
365
pcm->channels, size, linear->conv_idx);
370
static snd_pcm_uframes_t
371
snd_pcm_linear_read_areas(snd_pcm_t *pcm,
372
const snd_pcm_channel_area_t *areas,
373
snd_pcm_uframes_t offset,
374
snd_pcm_uframes_t size,
375
const snd_pcm_channel_area_t *slave_areas,
376
snd_pcm_uframes_t slave_offset,
377
snd_pcm_uframes_t *slave_sizep)
379
snd_pcm_linear_t *linear = pcm->private_data;
380
if (size > *slave_sizep)
382
if (linear->use_getput)
383
snd_pcm_linear_getput(areas, offset,
384
slave_areas, slave_offset,
386
linear->get_idx, linear->put_idx);
388
snd_pcm_linear_convert(areas, offset,
389
slave_areas, slave_offset,
390
pcm->channels, size, linear->conv_idx);
395
static void snd_pcm_linear_dump(snd_pcm_t *pcm, snd_output_t *out)
397
snd_pcm_linear_t *linear = pcm->private_data;
398
snd_output_printf(out, "Linear conversion PCM (%s)\n",
399
snd_pcm_format_name(linear->sformat));
401
snd_output_printf(out, "Its setup is:\n");
402
snd_pcm_dump_setup(pcm, out);
404
snd_output_printf(out, "Slave: ");
405
snd_pcm_dump(linear->plug.slave, out);
408
static snd_pcm_ops_t snd_pcm_linear_ops = {
409
.close = snd_pcm_plugin_close,
410
.info = snd_pcm_plugin_info,
411
.hw_refine = snd_pcm_linear_hw_refine,
412
.hw_params = snd_pcm_linear_hw_params,
413
.hw_free = snd_pcm_plugin_hw_free,
414
.sw_params = snd_pcm_plugin_sw_params,
415
.channel_info = snd_pcm_plugin_channel_info,
416
.dump = snd_pcm_linear_dump,
417
.nonblock = snd_pcm_plugin_nonblock,
418
.async = snd_pcm_plugin_async,
419
.poll_revents = snd_pcm_plugin_poll_revents,
420
.mmap = snd_pcm_plugin_mmap,
421
.munmap = snd_pcm_plugin_munmap,
426
* \brief Creates a new linear conversion PCM
427
* \param pcmp Returns created PCM handle
428
* \param name Name of PCM
429
* \param sformat Slave (destination) format
430
* \param slave Slave PCM handle
431
* \param close_slave When set, the slave PCM handle is closed with copy PCM
432
* \retval zero on success otherwise a negative error code
433
* \warning Using of this function might be dangerous in the sense
434
* of compatibility reasons. The prototype might be freely
437
int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
440
snd_pcm_linear_t *linear;
442
assert(pcmp && slave);
443
if (snd_pcm_format_linear(sformat) != 1)
445
linear = calloc(1, sizeof(snd_pcm_linear_t));
449
snd_pcm_plugin_init(&linear->plug);
450
linear->sformat = sformat;
451
linear->plug.read = snd_pcm_linear_read_areas;
452
linear->plug.write = snd_pcm_linear_write_areas;
453
linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
454
linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
455
linear->plug.slave = slave;
456
linear->plug.close_slave = close_slave;
458
err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR, name, slave->stream, slave->mode);
463
pcm->ops = &snd_pcm_linear_ops;
464
pcm->fast_ops = &snd_pcm_plugin_fast_ops;
465
pcm->private_data = linear;
466
pcm->poll_fd = slave->poll_fd;
467
pcm->poll_events = slave->poll_events;
468
snd_pcm_set_hw_ptr(pcm, &linear->plug.hw_ptr, -1, 0);
469
snd_pcm_set_appl_ptr(pcm, &linear->plug.appl_ptr, -1, 0);
475
/*! \page pcm_plugins
477
\section pcm_plugins_linear Plugin: linear
479
This plugin converts linear samples from master linear conversion PCM to given
480
slave PCM. The channel count, format and rate must match for both of them.
484
type linear # Linear conversion PCM
485
slave STR # Slave name
487
slave { # Slave definition
488
pcm STR # Slave PCM name
490
pcm { } # Slave PCM definition
491
format STR # Slave format
496
\subsection pcm_plugins_linear_funcref Function reference
499
<LI>snd_pcm_linear_open()
500
<LI>_snd_pcm_linear_open()
506
* \brief Creates a new linear conversion PCM
507
* \param pcmp Returns created PCM handle
508
* \param name Name of PCM
509
* \param root Root configuration node
510
* \param conf Configuration node with copy PCM description
511
* \param stream Stream type
512
* \param mode Stream mode
513
* \retval zero on success otherwise a negative error code
514
* \warning Using of this function might be dangerous in the sense
515
* of compatibility reasons. The prototype might be freely
518
int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
519
snd_config_t *root, snd_config_t *conf,
520
snd_pcm_stream_t stream, int mode)
522
snd_config_iterator_t i, next;
525
snd_config_t *slave = NULL, *sconf;
526
snd_pcm_format_t sformat;
527
snd_config_for_each(i, next, conf) {
528
snd_config_t *n = snd_config_iterator_entry(i);
530
if (snd_config_get_id(n, &id) < 0)
532
if (snd_pcm_conf_generic_id(id))
534
if (strcmp(id, "slave") == 0) {
538
SNDERR("Unknown field %s", id);
542
SNDERR("slave is not defined");
545
err = snd_pcm_slave_conf(root, slave, &sconf, 1,
546
SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
549
if (snd_pcm_format_linear(sformat) != 1) {
550
snd_config_delete(sconf);
551
SNDERR("slave format is not linear");
554
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
555
snd_config_delete(sconf);
558
err = snd_pcm_linear_open(pcmp, name, sformat, spcm, 1);
564
SND_DLSYM_BUILD_VERSION(_snd_pcm_linear_open, SND_PCM_DLSYM_VERSION);