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 SNDERR("softvol supports only S16_LE, S16_BE, S24_LE, S24_3LE, "
639 svol->sformat = slave->format;
643 static snd_pcm_uframes_t
644 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
645 const snd_pcm_channel_area_t *areas,
646 snd_pcm_uframes_t offset,
647 snd_pcm_uframes_t size,
648 const snd_pcm_channel_area_t *slave_areas,
649 snd_pcm_uframes_t slave_offset,
650 snd_pcm_uframes_t *slave_sizep)
652 snd_pcm_softvol_t *svol = pcm->private_data;
653 if (size > *slave_sizep)
655 get_current_volume(svol);
656 if (svol->cchannels == 1)
657 softvol_convert_mono_vol(svol, slave_areas, slave_offset,
658 areas, offset, pcm->channels, size);
660 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
661 areas, offset, pcm->channels, size);
666 static snd_pcm_uframes_t
667 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
668 const snd_pcm_channel_area_t *areas,
669 snd_pcm_uframes_t offset,
670 snd_pcm_uframes_t size,
671 const snd_pcm_channel_area_t *slave_areas,
672 snd_pcm_uframes_t slave_offset,
673 snd_pcm_uframes_t *slave_sizep)
675 snd_pcm_softvol_t *svol = pcm->private_data;
676 if (size > *slave_sizep)
678 get_current_volume(svol);
679 if (svol->cchannels == 1)
680 softvol_convert_mono_vol(svol, areas, offset, slave_areas,
681 slave_offset, pcm->channels, size);
683 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
684 slave_offset, pcm->channels, size);
689 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
691 snd_pcm_softvol_t *svol = pcm->private_data;
692 snd_output_printf(out, "Soft volume PCM\n");
693 snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
694 if (svol->max_val == 1)
695 snd_output_printf(out, "boolean\n");
697 snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
698 snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
699 snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
702 snd_output_printf(out, "Its setup is:\n");
703 snd_pcm_dump_setup(pcm, out);
705 snd_output_printf(out, "Slave: ");
706 snd_pcm_dump(svol->plug.gen.slave, out);
709 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
710 unsigned int *old_tlv, size_t old_tlv_size)
713 tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
714 tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
715 tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
716 tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
717 (int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
718 if (sizeof(tlv) <= old_tlv_size && memcmp(tlv, old_tlv, sizeof(tlv)) == 0)
720 return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
723 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
728 unsigned int def_val;
730 if (svol->max_val == 1) {
731 snd_ctl_elem_info_set_read_write(cinfo, 1, 1);
732 err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
734 err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
735 0, svol->max_val, 0);
739 if (svol->max_val == 1)
742 add_tlv_info(svol, cinfo, NULL, 0);
743 /* set zero dB value as default, or max_val if
744 there is no 0 dB setting */
745 def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
747 for (i = 0; i < count; i++)
748 svol->elem.value.integer.value[i] = def_val;
749 return snd_ctl_elem_write(svol->ctl, &svol->elem);
753 * load and set up user-control
754 * returns 0 if the user-control is found or created,
755 * returns 1 if the control is a hw control,
756 * or a negative error code
758 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
759 int ctl_card, snd_ctl_elem_id_t *ctl_id,
760 int cchannels, double min_dB, double max_dB,
764 snd_pcm_info_t info = {0};
765 snd_ctl_elem_info_t cinfo = {0};
770 err = snd_pcm_info(pcm, &info);
773 ctl_card = snd_pcm_info_get_card(&info);
775 SNDERR("No card defined for softvol control");
779 sprintf(tmp_name, "hw:%d", ctl_card);
780 err = snd_ctl_open(&svol->ctl, tmp_name, 0);
782 SNDERR("Cannot open CTL %s", tmp_name);
786 svol->elem.id = *ctl_id;
787 svol->max_val = resolution - 1;
788 svol->min_dB = min_dB;
789 svol->max_dB = max_dB;
790 if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
791 svol->zero_dB_val = svol->max_val;
792 else if (svol->max_dB < 0)
793 svol->zero_dB_val = 0; /* there is no 0 dB setting */
795 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
798 snd_ctl_elem_info_set_id(&cinfo, ctl_id);
799 if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
800 if (err != -ENOENT) {
801 SNDERR("Cannot get info for CTL %s", tmp_name);
804 err = add_user_ctl(svol, &cinfo, cchannels);
806 SNDERR("Cannot add a control");
810 if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
811 /* hardware control exists */
812 return 1; /* notify */
814 } else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
815 cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
816 cinfo.count != (unsigned int)cchannels ||
817 cinfo.value.integer.min != 0 ||
818 cinfo.value.integer.max != svol->max_val ||
819 (svol->max_val > 1 &&
820 (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
821 (svol->max_val < 2 &&
822 (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) != 0)) {
823 err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
825 SNDERR("Control %s mismatch", tmp_name);
828 /* clear cinfo including numid */
829 snd_ctl_elem_info_clear(&cinfo);
830 snd_ctl_elem_info_set_id(&cinfo, ctl_id);
831 if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
832 SNDERR("Cannot add a control");
835 } else if (svol->max_val > 1) {
836 /* check TLV availability */
838 err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
840 add_tlv_info(svol, &cinfo, tlv, err < 0 ? 0 : sizeof(tlv));
844 if (svol->max_val == 1)
847 /* set up dB table */
848 if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
849 resolution == PRESET_RESOLUTION)
850 svol->dB_value = (unsigned int*)preset_dB_value;
852 #ifndef HAVE_SOFT_FLOAT
853 svol->dB_value = calloc(resolution, sizeof(unsigned int));
854 if (! svol->dB_value) {
855 SNDERR("cannot allocate dB table");
858 svol->min_dB = min_dB;
859 svol->max_dB = max_dB;
860 for (i = 0; i <= svol->max_val; i++) {
861 double db = svol->min_dB +
862 (i * (svol->max_dB - svol->min_dB)) /
864 double v = (pow(10.0, db / 20.0) *
865 (double)(1 << VOL_SCALE_SHIFT));
866 svol->dB_value[i] = (unsigned int)v;
868 if (svol->zero_dB_val)
869 svol->dB_value[svol->zero_dB_val] = 65535;
871 SNDERR("Cannot handle the given dB range and resolution");
878 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
879 .close = snd_pcm_softvol_close,
880 .info = snd_pcm_generic_info,
881 .hw_refine = snd_pcm_softvol_hw_refine,
882 .hw_params = snd_pcm_softvol_hw_params,
883 .hw_free = snd_pcm_generic_hw_free,
884 .sw_params = snd_pcm_generic_sw_params,
885 .channel_info = snd_pcm_generic_channel_info,
886 .dump = snd_pcm_softvol_dump,
887 .nonblock = snd_pcm_generic_nonblock,
888 .async = snd_pcm_generic_async,
889 .mmap = snd_pcm_generic_mmap,
890 .munmap = snd_pcm_generic_munmap,
891 .query_chmaps = snd_pcm_generic_query_chmaps,
892 .get_chmap = snd_pcm_generic_get_chmap,
893 .set_chmap = snd_pcm_generic_set_chmap,
897 * \brief Creates a new SoftVolume PCM
898 * \param pcmp Returns created PCM handle
899 * \param name Name of PCM
900 * \param sformat Slave format
901 * \param ctl_card card index of the control
902 * \param ctl_id The control element
903 * \param cchannels PCM channels
904 * \param min_dB minimal dB value
905 * \param max_dB maximal dB value
906 * \param resolution resolution of control
907 * \param slave Slave PCM handle
908 * \param close_slave When set, the slave PCM handle is closed with copy PCM
909 * \retval zero on success otherwise a negative error code
910 * \warning Using of this function might be dangerous in the sense
911 * of compatibility reasons. The prototype might be freely
914 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
915 snd_pcm_format_t sformat,
916 int ctl_card, snd_ctl_elem_id_t *ctl_id,
918 double min_dB, double max_dB, int resolution,
919 snd_pcm_t *slave, int close_slave)
922 snd_pcm_softvol_t *svol;
924 assert(pcmp && slave);
925 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
926 sformat != SND_PCM_FORMAT_S16_LE &&
927 sformat != SND_PCM_FORMAT_S16_BE &&
928 sformat != SND_PCM_FORMAT_S24_3LE &&
929 sformat != SND_PCM_FORMAT_S24_LE &&
930 sformat != SND_PCM_FORMAT_S32_LE &&
931 sformat != SND_PCM_FORMAT_S32_BE)
933 svol = calloc(1, sizeof(*svol));
936 err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
937 min_dB, max_dB, resolution);
942 if (err > 0) { /* hardware control - no need for softvol! */
944 *pcmp = slave; /* just pass the slave */
945 if (!slave->name && name)
946 slave->name = strdup(name);
951 snd_pcm_plugin_init(&svol->plug);
952 svol->sformat = sformat;
953 svol->cchannels = cchannels;
954 svol->plug.read = snd_pcm_softvol_read_areas;
955 svol->plug.write = snd_pcm_softvol_write_areas;
956 svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
957 svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
958 svol->plug.gen.slave = slave;
959 svol->plug.gen.close_slave = close_slave;
961 err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
966 pcm->ops = &snd_pcm_softvol_ops;
967 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
968 pcm->private_data = svol;
969 pcm->poll_fd = slave->poll_fd;
970 pcm->poll_events = slave->poll_events;
972 * Since the softvol converts on the place, and the format/channels
973 * must be identical between source and destination, we don't need
976 pcm->mmap_shadow = 1;
977 pcm->tstamp_type = slave->tstamp_type;
978 snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
979 snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
985 static int _snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id,
986 int *cardp, int *cchannels)
988 snd_config_iterator_t i, next;
989 int iface = SND_CTL_ELEM_IFACE_MIXER;
990 const char *name = NULL;
996 assert(ctl_id && cardp && cchannels);
1000 snd_config_for_each(i, next, conf) {
1001 snd_config_t *n = snd_config_iterator_entry(i);
1003 if (snd_config_get_id(n, &id) < 0)
1005 if (strcmp(id, "comment") == 0)
1007 if (strcmp(id, "card") == 0) {
1008 err = snd_config_get_card(n);
1014 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
1015 err = snd_config_get_ctl_iface(n);
1021 if (strcmp(id, "name") == 0) {
1022 if ((err = snd_config_get_string(n, &name)) < 0) {
1023 SNDERR("field %s is not a string", id);
1028 if (strcmp(id, "index") == 0) {
1029 if ((err = snd_config_get_integer(n, &index)) < 0) {
1030 SNDERR("field %s is not an integer", id);
1035 if (strcmp(id, "device") == 0) {
1036 if ((err = snd_config_get_integer(n, &device)) < 0) {
1037 SNDERR("field %s is not an integer", id);
1042 if (strcmp(id, "subdevice") == 0) {
1043 if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
1044 SNDERR("field %s is not an integer", id);
1049 if (strcmp(id, "count") == 0) {
1051 if ((err = snd_config_get_integer(n, &v)) < 0) {
1052 SNDERR("field %s is not an integer", id);
1055 if (v < 1 || v > 2) {
1056 SNDERR("Invalid count %ld", v);
1062 SNDERR("Unknown field %s", id);
1066 SNDERR("Missing control name");
1075 snd_ctl_elem_id_set_interface(ctl_id, iface);
1076 snd_ctl_elem_id_set_name(ctl_id, name);
1077 snd_ctl_elem_id_set_index(ctl_id, index);
1078 snd_ctl_elem_id_set_device(ctl_id, device);
1079 snd_ctl_elem_id_set_subdevice(ctl_id, subdevice);
1087 /*! \page pcm_plugins
1089 \section pcm_plugins_softvol Plugin: Soft Volume
1091 This plugin applies the software volume attenuation.
1092 The format, rate and channels must match for both of source and destination.
1094 When the control is stereo (count=2), the channels are assumed to be either
1095 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
1097 If the control already exists and it's a system control (i.e. no
1098 user-defined control), the plugin simply passes its slave without
1103 type softvol # Soft Volume conversion PCM
1104 slave STR # Slave name
1106 slave { # Slave definition
1107 pcm STR # Slave PCM name
1109 pcm { } # Slave PCM definition
1110 [format STR] # Slave format
1113 name STR # control element id string
1114 [card STR] # control card index
1115 [iface STR] # interface of the element
1116 [index INT] # index of the element
1117 [device INT] # device number of the element
1118 [subdevice INT] # subdevice number of the element
1119 [count INT] # control channels 1 or 2 (default: 2)
1121 [min_dB REAL] # minimal dB value (default: -51.0)
1122 [max_dB REAL] # maximal dB value (default: 0.0)
1123 [resolution INT] # resolution (default: 256)
1124 # resolution = 2 means a mute switch
1128 \subsection pcm_plugins_softvol_funcref Function reference
1131 <LI>snd_pcm_softvol_open()
1132 <LI>_snd_pcm_softvol_open()
1138 * \brief Creates a new Soft Volume PCM
1139 * \param pcmp Returns created PCM handle
1140 * \param name Name of PCM
1141 * \param root Root configuration node
1142 * \param conf Configuration node with Soft Volume PCM description
1143 * \param stream Stream type
1144 * \param mode Stream mode
1145 * \retval zero on success otherwise a negative error code
1146 * \warning Using of this function might be dangerous in the sense
1147 * of compatibility reasons. The prototype might be freely
1148 * changed in future.
1150 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
1151 snd_config_t *root, snd_config_t *conf,
1152 snd_pcm_stream_t stream, int mode)
1154 snd_config_iterator_t i, next;
1157 snd_config_t *slave = NULL, *sconf;
1158 snd_config_t *control = NULL;
1159 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1160 snd_ctl_elem_id_t ctl_id = {0};
1161 int resolution = PRESET_RESOLUTION;
1162 double min_dB = PRESET_MIN_DB;
1163 double max_dB = ZERO_DB;
1164 int card = -1, cchannels = 2;
1166 snd_config_for_each(i, next, conf) {
1167 snd_config_t *n = snd_config_iterator_entry(i);
1169 if (snd_config_get_id(n, &id) < 0)
1171 if (snd_pcm_conf_generic_id(id))
1173 if (strcmp(id, "slave") == 0) {
1177 if (strcmp(id, "control") == 0) {
1181 if (strcmp(id, "resolution") == 0) {
1183 err = snd_config_get_integer(n, &v);
1185 SNDERR("Invalid resolution value");
1191 if (strcmp(id, "min_dB") == 0) {
1192 err = snd_config_get_ireal(n, &min_dB);
1194 SNDERR("Invalid min_dB value");
1199 if (strcmp(id, "max_dB") == 0) {
1200 err = snd_config_get_ireal(n, &max_dB);
1202 SNDERR("Invalid max_dB value");
1207 SNDERR("Unknown field %s", id);
1211 SNDERR("slave is not defined");
1215 SNDERR("control is not defined");
1219 SNDERR("min_dB must be a negative value");
1222 if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1223 SNDERR("max_dB must be larger than min_dB and less than %d dB",
1224 MAX_DB_UPPER_LIMIT);
1227 if (resolution <= 1 || resolution > 1024) {
1228 SNDERR("Invalid resolution value %d", resolution);
1231 if (mode & SND_PCM_NO_SOFTVOL) {
1232 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1235 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1237 snd_config_delete(sconf);
1239 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1240 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1243 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1244 sformat != SND_PCM_FORMAT_S16_LE &&
1245 sformat != SND_PCM_FORMAT_S16_BE &&
1246 sformat != SND_PCM_FORMAT_S24_3LE &&
1247 sformat != SND_PCM_FORMAT_S24_LE &&
1248 sformat != SND_PCM_FORMAT_S32_LE &&
1249 sformat != SND_PCM_FORMAT_S32_BE) {
1250 SNDERR("only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
1251 snd_config_delete(sconf);
1254 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1255 snd_config_delete(sconf);
1258 err = _snd_pcm_parse_control_id(control, &ctl_id, &card, &cchannels);
1260 snd_pcm_close(spcm);
1263 err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
1264 cchannels, min_dB, max_dB,
1265 resolution, spcm, 1);
1267 snd_pcm_close(spcm);
1272 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);