2 * \file pcm/pcm_softvol.c
4 * \brief PCM Soft Volume Plugin Interface
5 * \author Takashi Iwai <tiwai@suse.de>
9 * PCM - Soft Volume Plugin
10 * Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #include "pcm_local.h"
30 #include "pcm_plugin.h"
33 #include <sound/tlv.h>
36 /* entry for static linking */
37 const char *_snd_module_pcm_softvol = "";
43 /* This field need to be the first */
44 snd_pcm_plugin_t plug;
45 snd_pcm_format_t sformat;
46 unsigned int cchannels;
48 snd_ctl_elem_value_t elem;
49 unsigned int cur_vol[2];
50 unsigned int max_val; /* max index */
51 unsigned int zero_dB_val; /* index at 0 dB */
54 unsigned int *dB_value;
57 #define VOL_SCALE_SHIFT 16
58 #define VOL_SCALE_MASK ((1 << VOL_SCALE_SHIFT) - 1)
60 #define PRESET_RESOLUTION 256
61 #define PRESET_MIN_DB -51.0
64 * The gain algorithm as it stands supports gain factors up to 32767, which
65 * is a fraction more than 90 dB, so set 90 dB as the maximum possible gain.
67 #define MAX_DB_UPPER_LIMIT 90
69 static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
70 0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
71 0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
72 0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
73 0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
74 0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
75 0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
76 0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
77 0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
78 0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
79 0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
80 0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
81 0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
82 0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
83 0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
84 0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
85 0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
86 0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
87 0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
88 0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
89 0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
90 0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
91 0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
92 0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
93 0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
94 0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
95 0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
96 0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
97 0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
98 0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
99 0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
100 0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
101 0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
104 /* (32bit x 16bit) >> 16 */
109 static inline int MULTI_DIV_32x16(int a, unsigned short b)
114 #if __BYTE_ORDER == __LITTLE_ENDIAN
115 x.i = (unsigned short)v.s[0];
116 x.i *= (unsigned int)b;
118 y.i += (int)v.s[1] * b;
120 x.i = (unsigned int)v.s[1] * b;
122 y.i += (int)v.s[0] * b;
127 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
129 unsigned int gain = (b >> VOL_SCALE_SHIFT);
131 a = swap ? (int)bswap_32(a) : a;
132 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
134 long long amp = (long long)a * gain + fraction;
135 if (amp > (int)0x7fffffff)
136 amp = (int)0x7fffffff;
137 else if (amp < (int)0x80000000)
138 amp = (int)0x80000000;
139 return swap ? (int)bswap_32((int)amp) : (int)amp;
141 return swap ? (int)bswap_32(fraction) : fraction;
144 /* always little endian */
145 static inline int MULTI_DIV_24(int a, unsigned int b)
147 unsigned int gain = b >> VOL_SCALE_SHIFT;
149 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
151 long long amp = (long long)a * gain + fraction;
152 if (amp > (int)0x7fffff)
154 else if (amp < (int)0x800000)
161 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
163 unsigned int gain = b >> VOL_SCALE_SHIFT;
165 a = swap ? (short)bswap_16(a) : a;
166 fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
168 int amp = a * gain + fraction;
169 if (abs(amp) > 0x7fff)
170 amp = (a<0) ? (short)0x8000 : (short)0x7fff;
171 return swap ? (short)bswap_16((short)amp) : (short)amp;
173 return swap ? (short)bswap_16((short)fraction) : (short)fraction;
176 #endif /* DOC_HIDDEN */
179 * apply volumue attenuation
181 * TODO: use SIMD operations
185 #define CONVERT_AREA(TYPE, swap) do { \
186 unsigned int ch, fr; \
188 for (ch = 0; ch < channels; ch++) { \
189 src_area = &src_areas[ch]; \
190 dst_area = &dst_areas[ch]; \
191 src = snd_pcm_channel_area_addr(src_area, src_offset); \
192 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
193 src_step = snd_pcm_channel_area_step(src_area) / sizeof(TYPE); \
194 dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
202 } else if (vol_scale == 0xffff) { \
210 *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
218 #define CONVERT_AREA_S24_3LE() do { \
219 unsigned int ch, fr; \
220 unsigned char *src, *dst; \
222 for (ch = 0; ch < channels; ch++) { \
223 src_area = &src_areas[ch]; \
224 dst_area = &dst_areas[ch]; \
225 src = snd_pcm_channel_area_addr(src_area, src_offset); \
226 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
227 src_step = snd_pcm_channel_area_step(src_area); \
228 dst_step = snd_pcm_channel_area_step(dst_area); \
233 dst[0] = dst[1] = dst[2] = 0; \
236 } else if (vol_scale == 0xffff) { \
248 (((signed char *) src)[2] << 16); \
249 tmp = MULTI_DIV_24(tmp, vol_scale); \
252 dst[2] = tmp >> 16; \
260 #define CONVERT_AREA_S24_LE() do { \
261 unsigned int ch, fr; \
264 for (ch = 0; ch < channels; ch++) { \
265 src_area = &src_areas[ch]; \
266 dst_area = &dst_areas[ch]; \
267 src = snd_pcm_channel_area_addr(src_area, src_offset); \
268 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
269 src_step = snd_pcm_channel_area_step(src_area) \
271 dst_step = snd_pcm_channel_area_step(dst_area) \
280 } else if (vol_scale == 0xffff) { \
289 tmp = (signed int) tmp >> 8; \
290 *dst = MULTI_DIV_24(tmp, vol_scale); \
298 #define GET_VOL_SCALE \
302 vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
309 vol_scale = vol[ch & 1]; \
313 #endif /* DOC_HIDDEN */
315 /* 2-channel stereo control */
316 static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
317 const snd_pcm_channel_area_t *dst_areas,
318 snd_pcm_uframes_t dst_offset,
319 const snd_pcm_channel_area_t *src_areas,
320 snd_pcm_uframes_t src_offset,
321 unsigned int channels,
322 snd_pcm_uframes_t frames)
324 const snd_pcm_channel_area_t *dst_area, *src_area;
325 unsigned int src_step, dst_step;
326 unsigned int vol_scale, vol[2], vol_c;
328 if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
329 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
332 } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
333 svol->cur_vol[1] == svol->zero_dB_val) {
334 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
335 channels, frames, svol->sformat);
339 if (svol->max_val == 1) {
340 vol[0] = svol->cur_vol[0] ? 0xffff : 0;
341 vol[1] = svol->cur_vol[1] ? 0xffff : 0;
342 vol_c = vol[0] | vol[1];
344 vol[0] = svol->dB_value[svol->cur_vol[0]];
345 vol[1] = svol->dB_value[svol->cur_vol[1]];
346 vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
348 switch (svol->sformat) {
349 case SND_PCM_FORMAT_S16_LE:
350 case SND_PCM_FORMAT_S16_BE:
353 !snd_pcm_format_cpu_endian(svol->sformat));
355 case SND_PCM_FORMAT_S32_LE:
356 case SND_PCM_FORMAT_S32_BE:
359 !snd_pcm_format_cpu_endian(svol->sformat));
361 case SND_PCM_FORMAT_S24_LE:
363 CONVERT_AREA_S24_LE();
365 case SND_PCM_FORMAT_S24_3LE:
366 CONVERT_AREA_S24_3LE();
374 #define GET_VOL_SCALE
377 static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
378 const snd_pcm_channel_area_t *dst_areas,
379 snd_pcm_uframes_t dst_offset,
380 const snd_pcm_channel_area_t *src_areas,
381 snd_pcm_uframes_t src_offset,
382 unsigned int channels,
383 snd_pcm_uframes_t frames)
385 const snd_pcm_channel_area_t *dst_area, *src_area;
386 unsigned int src_step, dst_step;
387 unsigned int vol_scale;
389 if (svol->cur_vol[0] == 0) {
390 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
393 } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
394 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
395 channels, frames, svol->sformat);
399 if (svol->max_val == 1)
400 vol_scale = svol->cur_vol[0] ? 0xffff : 0;
402 vol_scale = svol->dB_value[svol->cur_vol[0]];
403 switch (svol->sformat) {
404 case SND_PCM_FORMAT_S16_LE:
405 case SND_PCM_FORMAT_S16_BE:
408 !snd_pcm_format_cpu_endian(svol->sformat));
410 case SND_PCM_FORMAT_S32_LE:
411 case SND_PCM_FORMAT_S32_BE:
414 !snd_pcm_format_cpu_endian(svol->sformat));
416 case SND_PCM_FORMAT_S24_LE:
418 CONVERT_AREA_S24_LE();
420 case SND_PCM_FORMAT_S24_3LE:
421 CONVERT_AREA_S24_3LE();
429 * get the current volume value from driver
431 * TODO: mmap support?
433 static void get_current_volume(snd_pcm_softvol_t *svol)
438 if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
440 for (i = 0; i < svol->cchannels; i++) {
441 val = svol->elem.value.integer.value[i];
442 if (val > svol->max_val)
444 svol->cur_vol[i] = val;
448 static void softvol_free(snd_pcm_softvol_t *svol)
450 if (svol->plug.gen.close_slave)
451 snd_pcm_close(svol->plug.gen.slave);
453 snd_ctl_close(svol->ctl);
454 if (svol->dB_value && svol->dB_value != preset_dB_value)
455 free(svol->dB_value);
459 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
461 snd_pcm_softvol_t *svol = pcm->private_data;
466 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
467 snd_pcm_hw_params_t *params)
470 snd_pcm_softvol_t *svol = pcm->private_data;
471 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
472 snd_pcm_format_mask_t format_mask = {
474 (1ULL << SND_PCM_FORMAT_S16_LE) |
475 (1ULL << SND_PCM_FORMAT_S16_BE) |
476 (1ULL << SND_PCM_FORMAT_S24_LE) |
477 (1ULL << SND_PCM_FORMAT_S32_LE) |
478 (1ULL << SND_PCM_FORMAT_S32_BE),
479 (1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
482 if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
483 snd_pcm_format_mask_none(&format_mask);
484 snd_pcm_format_mask_set(&format_mask, svol->sformat);
486 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
490 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
494 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
497 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
500 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
504 static int snd_pcm_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
506 snd_pcm_softvol_t *svol = pcm->private_data;
507 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
508 _snd_pcm_hw_params_any(sparams);
509 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
511 if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
512 _snd_pcm_hw_params_set_format(sparams, svol->sformat);
513 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
519 * refine the access mask
521 static int check_access_mask(snd_pcm_hw_params_t *src,
522 snd_pcm_hw_params_t *dst)
524 const snd_pcm_access_mask_t *mask;
525 snd_pcm_access_mask_t smask;
527 mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
528 snd_mask_none(&smask);
529 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
530 snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
531 snd_pcm_access_mask_set(&smask,
532 SND_PCM_ACCESS_RW_INTERLEAVED);
533 snd_pcm_access_mask_set(&smask,
534 SND_PCM_ACCESS_MMAP_INTERLEAVED);
536 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
537 snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
538 snd_pcm_access_mask_set(&smask,
539 SND_PCM_ACCESS_RW_NONINTERLEAVED);
540 snd_pcm_access_mask_set(&smask,
541 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
543 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
544 snd_pcm_access_mask_set(&smask,
545 SND_PCM_ACCESS_MMAP_COMPLEX);
547 return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask);
550 static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
551 snd_pcm_hw_params_t *params,
552 snd_pcm_hw_params_t *sparams)
554 snd_pcm_softvol_t *svol = pcm->private_data;
556 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
557 SND_PCM_HW_PARBIT_RATE |
558 SND_PCM_HW_PARBIT_PERIODS |
559 SND_PCM_HW_PARBIT_PERIOD_SIZE |
560 SND_PCM_HW_PARBIT_PERIOD_TIME |
561 SND_PCM_HW_PARBIT_BUFFER_SIZE |
562 SND_PCM_HW_PARBIT_BUFFER_TIME |
563 SND_PCM_HW_PARBIT_TICK_TIME);
564 if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
565 links |= (SND_PCM_HW_PARBIT_FORMAT |
566 SND_PCM_HW_PARBIT_SUBFORMAT |
567 SND_PCM_HW_PARBIT_SAMPLE_BITS);
568 err = _snd_pcm_hw_params_refine(sparams, links, params);
572 err = check_access_mask(params, sparams);
579 static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
580 snd_pcm_hw_params_t *params,
581 snd_pcm_hw_params_t *sparams)
583 snd_pcm_softvol_t *svol = pcm->private_data;
585 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
586 SND_PCM_HW_PARBIT_RATE |
587 SND_PCM_HW_PARBIT_PERIODS |
588 SND_PCM_HW_PARBIT_PERIOD_SIZE |
589 SND_PCM_HW_PARBIT_PERIOD_TIME |
590 SND_PCM_HW_PARBIT_BUFFER_SIZE |
591 SND_PCM_HW_PARBIT_BUFFER_TIME |
592 SND_PCM_HW_PARBIT_TICK_TIME);
593 if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
594 links |= (SND_PCM_HW_PARBIT_FORMAT |
595 SND_PCM_HW_PARBIT_SUBFORMAT |
596 SND_PCM_HW_PARBIT_SAMPLE_BITS);
597 err = _snd_pcm_hw_params_refine(params, links, sparams);
601 err = check_access_mask(sparams, params);
608 static int snd_pcm_softvol_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
610 return snd_pcm_hw_refine_slave(pcm, params,
611 snd_pcm_softvol_hw_refine_cprepare,
612 snd_pcm_softvol_hw_refine_cchange,
613 snd_pcm_softvol_hw_refine_sprepare,
614 snd_pcm_softvol_hw_refine_schange,
615 snd_pcm_generic_hw_refine);
618 static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
620 snd_pcm_softvol_t *svol = pcm->private_data;
621 snd_pcm_t *slave = svol->plug.gen.slave;
622 int err = snd_pcm_hw_params_slave(pcm, params,
623 snd_pcm_softvol_hw_refine_cchange,
624 snd_pcm_softvol_hw_refine_sprepare,
625 snd_pcm_softvol_hw_refine_schange,
626 snd_pcm_generic_hw_params);
629 if (slave->format != SND_PCM_FORMAT_S16_LE &&
630 slave->format != SND_PCM_FORMAT_S16_BE &&
631 slave->format != SND_PCM_FORMAT_S24_3LE &&
632 slave->format != SND_PCM_FORMAT_S24_LE &&
633 slave->format != SND_PCM_FORMAT_S32_LE &&
634 slave->format != SND_PCM_FORMAT_S32_BE) {
635 snd_error(PCM, "softvol supports only S16_LE, S16_BE, S24_LE, S24_3LE, "
640 svol->sformat = slave->format;
644 static snd_pcm_uframes_t
645 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
646 const snd_pcm_channel_area_t *areas,
647 snd_pcm_uframes_t offset,
648 snd_pcm_uframes_t size,
649 const snd_pcm_channel_area_t *slave_areas,
650 snd_pcm_uframes_t slave_offset,
651 snd_pcm_uframes_t *slave_sizep)
653 snd_pcm_softvol_t *svol = pcm->private_data;
654 if (size > *slave_sizep)
656 get_current_volume(svol);
657 if (svol->cchannels == 1)
658 softvol_convert_mono_vol(svol, slave_areas, slave_offset,
659 areas, offset, pcm->channels, size);
661 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
662 areas, offset, pcm->channels, size);
667 static snd_pcm_uframes_t
668 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
669 const snd_pcm_channel_area_t *areas,
670 snd_pcm_uframes_t offset,
671 snd_pcm_uframes_t size,
672 const snd_pcm_channel_area_t *slave_areas,
673 snd_pcm_uframes_t slave_offset,
674 snd_pcm_uframes_t *slave_sizep)
676 snd_pcm_softvol_t *svol = pcm->private_data;
677 if (size > *slave_sizep)
679 get_current_volume(svol);
680 if (svol->cchannels == 1)
681 softvol_convert_mono_vol(svol, areas, offset, slave_areas,
682 slave_offset, pcm->channels, size);
684 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
685 slave_offset, pcm->channels, size);
690 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
692 snd_pcm_softvol_t *svol = pcm->private_data;
693 snd_output_printf(out, "Soft volume PCM\n");
694 snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
695 if (svol->max_val == 1)
696 snd_output_printf(out, "boolean\n");
698 snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
699 snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
700 snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
703 snd_output_printf(out, "Its setup is:\n");
704 snd_pcm_dump_setup(pcm, out);
706 snd_output_printf(out, "Slave: ");
707 snd_pcm_dump(svol->plug.gen.slave, out);
710 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
711 unsigned int *old_tlv, size_t old_tlv_size)
714 tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
715 tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
716 tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
717 tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
718 (int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
719 if (sizeof(tlv) <= old_tlv_size && memcmp(tlv, old_tlv, sizeof(tlv)) == 0)
721 return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
724 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
729 unsigned int def_val;
731 if (svol->max_val == 1) {
732 snd_ctl_elem_info_set_read_write(cinfo, 1, 1);
733 err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
735 err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
736 0, svol->max_val, 0);
740 if (svol->max_val == 1)
743 add_tlv_info(svol, cinfo, NULL, 0);
744 /* set zero dB value as default, or max_val if
745 there is no 0 dB setting */
746 def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
748 for (i = 0; i < count; i++)
749 svol->elem.value.integer.value[i] = def_val;
750 return snd_ctl_elem_write(svol->ctl, &svol->elem);
754 * load and set up user-control
755 * returns 0 if the user-control is found or created,
756 * returns 1 if the control is a hw control,
757 * or a negative error code
759 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
760 int ctl_card, snd_ctl_elem_id_t *ctl_id,
761 int cchannels, double min_dB, double max_dB,
765 snd_pcm_info_t info = {0};
766 snd_ctl_elem_info_t cinfo = {0};
771 err = snd_pcm_info(pcm, &info);
774 ctl_card = snd_pcm_info_get_card(&info);
776 snd_error(PCM, "No card defined for softvol control");
780 sprintf(tmp_name, "hw:%d", ctl_card);
781 err = snd_ctl_open(&svol->ctl, tmp_name, 0);
783 snd_error(PCM, "Cannot open CTL %s", tmp_name);
787 svol->elem.id = *ctl_id;
788 svol->max_val = resolution - 1;
789 svol->min_dB = min_dB;
790 svol->max_dB = max_dB;
791 if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
792 svol->zero_dB_val = svol->max_val;
793 else if (svol->max_dB < 0)
794 svol->zero_dB_val = 0; /* there is no 0 dB setting */
796 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
799 snd_ctl_elem_info_set_id(&cinfo, ctl_id);
800 if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
801 if (err != -ENOENT) {
802 snd_error(PCM, "Cannot get info for CTL %s", tmp_name);
805 err = add_user_ctl(svol, &cinfo, cchannels);
807 snd_error(PCM, "Cannot add a control");
811 if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
812 /* hardware control exists */
813 return 1; /* notify */
815 } else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
816 cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
817 cinfo.count != (unsigned int)cchannels ||
818 cinfo.value.integer.min != 0 ||
819 cinfo.value.integer.max != svol->max_val ||
820 (svol->max_val > 1 &&
821 (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
822 (svol->max_val < 2 &&
823 (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) != 0)) {
824 err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
826 snd_error(PCM, "Control %s mismatch", tmp_name);
829 /* clear cinfo including numid */
830 snd_ctl_elem_info_clear(&cinfo);
831 snd_ctl_elem_info_set_id(&cinfo, ctl_id);
832 if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
833 snd_error(PCM, "Cannot add a control");
836 } else if (svol->max_val > 1) {
837 /* check TLV availability */
839 err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
841 add_tlv_info(svol, &cinfo, tlv, err < 0 ? 0 : sizeof(tlv));
845 if (svol->max_val == 1)
848 /* set up dB table */
849 if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
850 resolution == PRESET_RESOLUTION)
851 svol->dB_value = (unsigned int*)preset_dB_value;
853 #ifndef HAVE_SOFT_FLOAT
854 svol->dB_value = calloc(resolution, sizeof(unsigned int));
855 if (! svol->dB_value) {
856 snd_error(PCM, "cannot allocate dB table");
859 svol->min_dB = min_dB;
860 svol->max_dB = max_dB;
861 for (i = 0; i <= svol->max_val; i++) {
862 double db = svol->min_dB +
863 (i * (svol->max_dB - svol->min_dB)) /
865 double v = (pow(10.0, db / 20.0) *
866 (double)(1 << VOL_SCALE_SHIFT));
867 svol->dB_value[i] = (unsigned int)v;
869 if (svol->zero_dB_val)
870 svol->dB_value[svol->zero_dB_val] = 65535;
872 snd_error(PCM, "Cannot handle the given dB range and resolution");
879 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
880 .close = snd_pcm_softvol_close,
881 .info = snd_pcm_generic_info,
882 .hw_refine = snd_pcm_softvol_hw_refine,
883 .hw_params = snd_pcm_softvol_hw_params,
884 .hw_free = snd_pcm_generic_hw_free,
885 .sw_params = snd_pcm_generic_sw_params,
886 .channel_info = snd_pcm_generic_channel_info,
887 .dump = snd_pcm_softvol_dump,
888 .nonblock = snd_pcm_generic_nonblock,
889 .async = snd_pcm_generic_async,
890 .mmap = snd_pcm_generic_mmap,
891 .munmap = snd_pcm_generic_munmap,
892 .query_chmaps = snd_pcm_generic_query_chmaps,
893 .get_chmap = snd_pcm_generic_get_chmap,
894 .set_chmap = snd_pcm_generic_set_chmap,
898 * \brief Creates a new SoftVolume PCM
899 * \param pcmp Returns created PCM handle
900 * \param name Name of PCM
901 * \param sformat Slave format
902 * \param ctl_card card index of the control
903 * \param ctl_id The control element
904 * \param cchannels PCM channels
905 * \param min_dB minimal dB value
906 * \param max_dB maximal dB value
907 * \param resolution resolution of control
908 * \param slave Slave PCM handle
909 * \param close_slave When set, the slave PCM handle is closed with copy PCM
910 * \retval zero on success otherwise a negative error code
911 * \warning Using of this function might be dangerous in the sense
912 * of compatibility reasons. The prototype might be freely
915 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
916 snd_pcm_format_t sformat,
917 int ctl_card, snd_ctl_elem_id_t *ctl_id,
919 double min_dB, double max_dB, int resolution,
920 snd_pcm_t *slave, int close_slave)
923 snd_pcm_softvol_t *svol;
925 assert(pcmp && slave);
926 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
927 sformat != SND_PCM_FORMAT_S16_LE &&
928 sformat != SND_PCM_FORMAT_S16_BE &&
929 sformat != SND_PCM_FORMAT_S24_3LE &&
930 sformat != SND_PCM_FORMAT_S24_LE &&
931 sformat != SND_PCM_FORMAT_S32_LE &&
932 sformat != SND_PCM_FORMAT_S32_BE)
934 svol = calloc(1, sizeof(*svol));
937 err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
938 min_dB, max_dB, resolution);
943 if (err > 0) { /* hardware control - no need for softvol! */
945 *pcmp = slave; /* just pass the slave */
946 if (!slave->name && name)
947 slave->name = strdup(name);
952 snd_pcm_plugin_init(&svol->plug);
953 svol->sformat = sformat;
954 svol->cchannels = cchannels;
955 svol->plug.read = snd_pcm_softvol_read_areas;
956 svol->plug.write = snd_pcm_softvol_write_areas;
957 svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
958 svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
959 svol->plug.gen.slave = slave;
960 svol->plug.gen.close_slave = close_slave;
962 err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
967 pcm->ops = &snd_pcm_softvol_ops;
968 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
969 pcm->private_data = svol;
970 pcm->poll_fd = slave->poll_fd;
971 pcm->poll_events = slave->poll_events;
973 * Since the softvol converts on the place, and the format/channels
974 * must be identical between source and destination, we don't need
977 pcm->mmap_shadow = 1;
978 pcm->tstamp_type = slave->tstamp_type;
979 snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
980 snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
986 static int _snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id,
987 int *cardp, int *cchannels)
989 snd_config_iterator_t i, next;
990 int iface = SND_CTL_ELEM_IFACE_MIXER;
991 const char *name = NULL;
997 assert(ctl_id && cardp && cchannels);
1001 snd_config_for_each(i, next, conf) {
1002 snd_config_t *n = snd_config_iterator_entry(i);
1004 if (snd_config_get_id(n, &id) < 0)
1006 if (strcmp(id, "comment") == 0)
1008 if (strcmp(id, "card") == 0) {
1009 err = snd_config_get_card(n);
1015 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
1016 err = snd_config_get_ctl_iface(n);
1022 if (strcmp(id, "name") == 0) {
1023 if ((err = snd_config_get_string(n, &name)) < 0) {
1024 snd_error(PCM, "field %s is not a string", id);
1029 if (strcmp(id, "index") == 0) {
1030 if ((err = snd_config_get_integer(n, &index)) < 0) {
1031 snd_error(PCM, "field %s is not an integer", id);
1036 if (strcmp(id, "device") == 0) {
1037 if ((err = snd_config_get_integer(n, &device)) < 0) {
1038 snd_error(PCM, "field %s is not an integer", id);
1043 if (strcmp(id, "subdevice") == 0) {
1044 if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
1045 snd_error(PCM, "field %s is not an integer", id);
1050 if (strcmp(id, "count") == 0) {
1052 if ((err = snd_config_get_integer(n, &v)) < 0) {
1053 snd_error(PCM, "field %s is not an integer", id);
1056 if (v < 1 || v > 2) {
1057 snd_error(PCM, "Invalid count %ld", v);
1063 snd_error(PCM, "Unknown field %s", id);
1067 snd_error(PCM, "Missing control name");
1076 snd_ctl_elem_id_set_interface(ctl_id, iface);
1077 snd_ctl_elem_id_set_name(ctl_id, name);
1078 snd_ctl_elem_id_set_index(ctl_id, index);
1079 snd_ctl_elem_id_set_device(ctl_id, device);
1080 snd_ctl_elem_id_set_subdevice(ctl_id, subdevice);
1088 /*! \page pcm_plugins
1090 \section pcm_plugins_softvol Plugin: Soft Volume
1092 This plugin applies the software volume attenuation.
1093 The format, rate and channels must match for both of source and destination.
1095 When the control is stereo (count=2), the channels are assumed to be either
1096 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
1098 If the control already exists and it's a system control (i.e. no
1099 user-defined control), the plugin simply passes its slave without
1104 type softvol # Soft Volume conversion PCM
1105 slave STR # Slave name
1107 slave { # Slave definition
1108 pcm STR # Slave PCM name
1110 pcm { } # Slave PCM definition
1111 [format STR] # Slave format
1114 name STR # control element id string
1115 [card STR] # control card index
1116 [iface STR] # interface of the element
1117 [index INT] # index of the element
1118 [device INT] # device number of the element
1119 [subdevice INT] # subdevice number of the element
1120 [count INT] # control channels 1 or 2 (default: 2)
1122 [min_dB REAL] # minimal dB value (default: -51.0)
1123 [max_dB REAL] # maximal dB value (default: 0.0)
1124 [resolution INT] # resolution (default: 256)
1125 # resolution = 2 means a mute switch
1129 \subsection pcm_plugins_softvol_funcref Function reference
1132 <LI>snd_pcm_softvol_open()
1133 <LI>_snd_pcm_softvol_open()
1139 * \brief Creates a new Soft Volume PCM
1140 * \param pcmp Returns created PCM handle
1141 * \param name Name of PCM
1142 * \param root Root configuration node
1143 * \param conf Configuration node with Soft Volume PCM description
1144 * \param stream Stream type
1145 * \param mode Stream mode
1146 * \retval zero on success otherwise a negative error code
1147 * \warning Using of this function might be dangerous in the sense
1148 * of compatibility reasons. The prototype might be freely
1149 * changed in future.
1151 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
1152 snd_config_t *root, snd_config_t *conf,
1153 snd_pcm_stream_t stream, int mode)
1155 snd_config_iterator_t i, next;
1158 snd_config_t *slave = NULL, *sconf;
1159 snd_config_t *control = NULL;
1160 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1161 snd_ctl_elem_id_t ctl_id = {0};
1162 int resolution = PRESET_RESOLUTION;
1163 double min_dB = PRESET_MIN_DB;
1164 double max_dB = ZERO_DB;
1165 int card = -1, cchannels = 2;
1167 snd_config_for_each(i, next, conf) {
1168 snd_config_t *n = snd_config_iterator_entry(i);
1170 if (snd_config_get_id(n, &id) < 0)
1172 if (snd_pcm_conf_generic_id(id))
1174 if (strcmp(id, "slave") == 0) {
1178 if (strcmp(id, "control") == 0) {
1182 if (strcmp(id, "resolution") == 0) {
1184 err = snd_config_get_integer(n, &v);
1186 snd_error(PCM, "Invalid resolution value");
1192 if (strcmp(id, "min_dB") == 0) {
1193 err = snd_config_get_ireal(n, &min_dB);
1195 snd_error(PCM, "Invalid min_dB value");
1200 if (strcmp(id, "max_dB") == 0) {
1201 err = snd_config_get_ireal(n, &max_dB);
1203 snd_error(PCM, "Invalid max_dB value");
1208 snd_error(PCM, "Unknown field %s", id);
1212 snd_error(PCM, "slave is not defined");
1216 snd_error(PCM, "control is not defined");
1220 snd_error(PCM, "min_dB must be a negative value");
1223 if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1224 snd_error(PCM, "max_dB must be larger than min_dB and less than %d dB",
1225 MAX_DB_UPPER_LIMIT);
1229 if (resolution <= 1 || resolution > 1024) {
1230 snd_error(PCM, "Invalid resolution value %d", resolution);
1233 if (mode & SND_PCM_NO_SOFTVOL) {
1234 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1237 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1239 snd_config_delete(sconf);
1241 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1242 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1245 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1246 sformat != SND_PCM_FORMAT_S16_LE &&
1247 sformat != SND_PCM_FORMAT_S16_BE &&
1248 sformat != SND_PCM_FORMAT_S24_3LE &&
1249 sformat != SND_PCM_FORMAT_S24_LE &&
1250 sformat != SND_PCM_FORMAT_S32_LE &&
1251 sformat != SND_PCM_FORMAT_S32_BE) {
1252 snd_error(PCM, "only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
1253 snd_config_delete(sconf);
1256 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1257 snd_config_delete(sconf);
1260 err = _snd_pcm_parse_control_id(control, &ctl_id, &card, &cchannels);
1262 snd_pcm_close(spcm);
1265 err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
1266 cchannels, min_dB, max_dB,
1267 resolution, spcm, 1);
1269 snd_pcm_close(spcm);
1274 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);