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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "pcm_local.h"
32 #include "pcm_plugin.h"
35 /* entry for static linking */
36 const char *_snd_module_pcm_softvol = "";
42 /* This field need to be the first */
43 snd_pcm_plugin_t plug;
44 snd_pcm_format_t sformat;
45 unsigned int cchannels;
47 snd_ctl_elem_value_t elem;
48 unsigned int cur_vol[2];
49 unsigned int max_val; /* max index */
50 unsigned int zero_dB_val; /* index at 0 dB */
53 unsigned int *dB_value;
56 #define VOL_SCALE_SHIFT 16
57 #define VOL_SCALE_MASK ((1 << VOL_SCALE_SHIFT) - 1)
59 #define PRESET_RESOLUTION 256
60 #define PRESET_MIN_DB -51.0
62 #define MAX_DB_UPPER_LIMIT 50
64 static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
65 0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
66 0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
67 0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
68 0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
69 0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
70 0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
71 0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
72 0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
73 0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
74 0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
75 0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
76 0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
77 0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
78 0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
79 0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
80 0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
81 0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
82 0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
83 0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
84 0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
85 0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
86 0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
87 0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
88 0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
89 0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
90 0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
91 0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
92 0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
93 0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
94 0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
95 0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
96 0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
99 /* (32bit x 16bit) >> 16 */
104 static inline int MULTI_DIV_32x16(int a, unsigned short b)
109 #if __BYTE_ORDER == __LITTLE_ENDIAN
110 x.i = (unsigned short)v.s[0];
113 y.i += (int)v.s[1] * b;
115 x.i = (unsigned int)v.s[1] * b;
117 y.i += (int)v.s[0] * b;
122 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
124 unsigned int gain = (b >> VOL_SCALE_SHIFT);
126 a = swap ? (int)bswap_32(a) : a;
127 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
129 long long amp = (long long)a * gain + fraction;
130 if (amp > (int)0x7fffffff)
131 amp = (int)0x7fffffff;
132 else if (amp < (int)0x80000000)
133 amp = (int)0x80000000;
134 return swap ? (int)bswap_32((int)amp) : (int)amp;
136 return swap ? (int)bswap_32(fraction) : fraction;
139 /* always little endian */
140 static inline int MULTI_DIV_24(int a, unsigned int b)
142 unsigned int gain = b >> VOL_SCALE_SHIFT;
144 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
146 long long amp = (long long)a * gain + fraction;
147 if (amp > (int)0x7fffff)
149 else if (amp < (int)0x800000)
156 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
158 unsigned int gain = b >> VOL_SCALE_SHIFT;
160 a = swap ? (short)bswap_16(a) : a;
161 fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
163 int amp = a * gain + fraction;
164 if (abs(amp) > 0x7fff)
165 amp = (a<0) ? (short)0x8000 : (short)0x7fff;
166 return swap ? (short)bswap_16((short)amp) : (short)amp;
168 return swap ? (short)bswap_16((short)fraction) : (short)fraction;
171 #endif /* DOC_HIDDEN */
174 * apply volumue attenuation
176 * TODO: use SIMD operations
180 #define CONVERT_AREA(TYPE, swap) do { \
181 unsigned int ch, fr; \
183 for (ch = 0; ch < channels; ch++) { \
184 src_area = &src_areas[ch]; \
185 dst_area = &dst_areas[ch]; \
186 src = snd_pcm_channel_area_addr(src_area, src_offset); \
187 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
188 src_step = snd_pcm_channel_area_step(src_area) / sizeof(TYPE); \
189 dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
197 } else if (vol_scale == 0xffff) { \
205 *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
213 #define CONVERT_AREA_S24_3LE() do { \
214 unsigned int ch, fr; \
215 unsigned char *src, *dst; \
217 for (ch = 0; ch < channels; ch++) { \
218 src_area = &src_areas[ch]; \
219 dst_area = &dst_areas[ch]; \
220 src = snd_pcm_channel_area_addr(src_area, src_offset); \
221 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
222 src_step = snd_pcm_channel_area_step(src_area); \
223 dst_step = snd_pcm_channel_area_step(dst_area); \
228 dst[0] = dst[1] = dst[2] = 0; \
231 } else if (vol_scale == 0xffff) { \
243 (((signed char *) src)[2] << 16); \
244 tmp = MULTI_DIV_24(tmp, vol_scale); \
247 dst[2] = tmp >> 16; \
255 #define GET_VOL_SCALE \
259 vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
266 vol_scale = vol[ch & 1]; \
270 #endif /* DOC_HIDDEN */
272 /* 2-channel stereo control */
273 static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
274 const snd_pcm_channel_area_t *dst_areas,
275 snd_pcm_uframes_t dst_offset,
276 const snd_pcm_channel_area_t *src_areas,
277 snd_pcm_uframes_t src_offset,
278 unsigned int channels,
279 snd_pcm_uframes_t frames)
281 const snd_pcm_channel_area_t *dst_area, *src_area;
282 unsigned int src_step, dst_step;
283 unsigned int vol_scale, vol[2], vol_c;
285 if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
286 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
289 } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
290 svol->cur_vol[1] == svol->zero_dB_val) {
291 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
292 channels, frames, svol->sformat);
296 if (svol->max_val == 1) {
297 vol[0] = svol->cur_vol[0] ? 0xffff : 0;
298 vol[1] = svol->cur_vol[1] ? 0xffff : 0;
299 vol_c = vol[0] | vol[1];
301 vol[0] = svol->dB_value[svol->cur_vol[0]];
302 vol[1] = svol->dB_value[svol->cur_vol[1]];
303 vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
305 switch (svol->sformat) {
306 case SND_PCM_FORMAT_S16_LE:
307 case SND_PCM_FORMAT_S16_BE:
310 !snd_pcm_format_cpu_endian(svol->sformat));
312 case SND_PCM_FORMAT_S32_LE:
313 case SND_PCM_FORMAT_S32_BE:
316 !snd_pcm_format_cpu_endian(svol->sformat));
318 case SND_PCM_FORMAT_S24_3LE:
319 CONVERT_AREA_S24_3LE();
327 #define GET_VOL_SCALE
330 static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
331 const snd_pcm_channel_area_t *dst_areas,
332 snd_pcm_uframes_t dst_offset,
333 const snd_pcm_channel_area_t *src_areas,
334 snd_pcm_uframes_t src_offset,
335 unsigned int channels,
336 snd_pcm_uframes_t frames)
338 const snd_pcm_channel_area_t *dst_area, *src_area;
339 unsigned int src_step, dst_step;
340 unsigned int vol_scale;
342 if (svol->cur_vol[0] == 0) {
343 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
346 } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
347 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
348 channels, frames, svol->sformat);
352 if (svol->max_val == 1)
353 vol_scale = svol->cur_vol[0] ? 0xffff : 0;
355 vol_scale = svol->dB_value[svol->cur_vol[0]];
356 switch (svol->sformat) {
357 case SND_PCM_FORMAT_S16_LE:
358 case SND_PCM_FORMAT_S16_BE:
361 !snd_pcm_format_cpu_endian(svol->sformat));
363 case SND_PCM_FORMAT_S32_LE:
364 case SND_PCM_FORMAT_S32_BE:
367 !snd_pcm_format_cpu_endian(svol->sformat));
369 case SND_PCM_FORMAT_S24_3LE:
370 CONVERT_AREA_S24_3LE();
378 * get the current volume value from driver
380 * TODO: mmap support?
382 static void get_current_volume(snd_pcm_softvol_t *svol)
387 if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
389 for (i = 0; i < svol->cchannels; i++) {
390 val = svol->elem.value.integer.value[i];
391 if (val > svol->max_val)
393 svol->cur_vol[i] = val;
397 static void softvol_free(snd_pcm_softvol_t *svol)
399 if (svol->plug.gen.close_slave)
400 snd_pcm_close(svol->plug.gen.slave);
402 snd_ctl_close(svol->ctl);
403 if (svol->dB_value && svol->dB_value != preset_dB_value)
404 free(svol->dB_value);
408 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
410 snd_pcm_softvol_t *svol = pcm->private_data;
415 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
416 snd_pcm_hw_params_t *params)
419 snd_pcm_softvol_t *svol = pcm->private_data;
420 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
421 snd_pcm_format_mask_t format_mask = {
423 (1ULL << SND_PCM_FORMAT_S16_LE) |
424 (1ULL << SND_PCM_FORMAT_S16_BE) |
425 (1ULL << SND_PCM_FORMAT_S32_LE) |
426 (1ULL << SND_PCM_FORMAT_S32_BE),
427 (1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
430 if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
431 snd_pcm_format_mask_none(&format_mask);
432 snd_pcm_format_mask_set(&format_mask, svol->sformat);
434 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
438 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
442 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
445 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
448 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
452 static int snd_pcm_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
454 snd_pcm_softvol_t *svol = pcm->private_data;
455 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
456 _snd_pcm_hw_params_any(sparams);
457 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
459 if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
460 _snd_pcm_hw_params_set_format(sparams, svol->sformat);
461 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
467 * refine the access mask
469 static int check_access_mask(snd_pcm_hw_params_t *src,
470 snd_pcm_hw_params_t *dst)
472 const snd_pcm_access_mask_t *mask;
473 snd_pcm_access_mask_t smask;
475 mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
476 snd_mask_none(&smask);
477 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
478 snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
479 snd_pcm_access_mask_set(&smask,
480 SND_PCM_ACCESS_RW_INTERLEAVED);
481 snd_pcm_access_mask_set(&smask,
482 SND_PCM_ACCESS_MMAP_INTERLEAVED);
484 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
485 snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
486 snd_pcm_access_mask_set(&smask,
487 SND_PCM_ACCESS_RW_NONINTERLEAVED);
488 snd_pcm_access_mask_set(&smask,
489 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
491 if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
492 snd_pcm_access_mask_set(&smask,
493 SND_PCM_ACCESS_MMAP_COMPLEX);
495 return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask);
498 static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
499 snd_pcm_hw_params_t *params,
500 snd_pcm_hw_params_t *sparams)
502 snd_pcm_softvol_t *svol = pcm->private_data;
504 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
505 SND_PCM_HW_PARBIT_RATE |
506 SND_PCM_HW_PARBIT_PERIODS |
507 SND_PCM_HW_PARBIT_PERIOD_SIZE |
508 SND_PCM_HW_PARBIT_PERIOD_TIME |
509 SND_PCM_HW_PARBIT_BUFFER_SIZE |
510 SND_PCM_HW_PARBIT_BUFFER_TIME |
511 SND_PCM_HW_PARBIT_TICK_TIME);
512 if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
513 links |= (SND_PCM_HW_PARBIT_FORMAT |
514 SND_PCM_HW_PARBIT_SUBFORMAT |
515 SND_PCM_HW_PARBIT_SAMPLE_BITS);
516 err = _snd_pcm_hw_params_refine(sparams, links, params);
520 err = check_access_mask(params, sparams);
527 static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
528 snd_pcm_hw_params_t *params,
529 snd_pcm_hw_params_t *sparams)
531 snd_pcm_softvol_t *svol = pcm->private_data;
533 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
534 SND_PCM_HW_PARBIT_RATE |
535 SND_PCM_HW_PARBIT_PERIODS |
536 SND_PCM_HW_PARBIT_PERIOD_SIZE |
537 SND_PCM_HW_PARBIT_PERIOD_TIME |
538 SND_PCM_HW_PARBIT_BUFFER_SIZE |
539 SND_PCM_HW_PARBIT_BUFFER_TIME |
540 SND_PCM_HW_PARBIT_TICK_TIME);
541 if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
542 links |= (SND_PCM_HW_PARBIT_FORMAT |
543 SND_PCM_HW_PARBIT_SUBFORMAT |
544 SND_PCM_HW_PARBIT_SAMPLE_BITS);
545 err = _snd_pcm_hw_params_refine(params, links, sparams);
549 err = check_access_mask(sparams, params);
556 static int snd_pcm_softvol_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
558 return snd_pcm_hw_refine_slave(pcm, params,
559 snd_pcm_softvol_hw_refine_cprepare,
560 snd_pcm_softvol_hw_refine_cchange,
561 snd_pcm_softvol_hw_refine_sprepare,
562 snd_pcm_softvol_hw_refine_schange,
563 snd_pcm_generic_hw_refine);
566 static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
568 snd_pcm_softvol_t *svol = pcm->private_data;
569 snd_pcm_t *slave = svol->plug.gen.slave;
570 int err = snd_pcm_hw_params_slave(pcm, params,
571 snd_pcm_softvol_hw_refine_cchange,
572 snd_pcm_softvol_hw_refine_sprepare,
573 snd_pcm_softvol_hw_refine_schange,
574 snd_pcm_generic_hw_params);
577 if (slave->format != SND_PCM_FORMAT_S16_LE &&
578 slave->format != SND_PCM_FORMAT_S16_BE &&
579 slave->format != SND_PCM_FORMAT_S24_3LE &&
580 slave->format != SND_PCM_FORMAT_S32_LE &&
581 slave->format != SND_PCM_FORMAT_S32_BE) {
582 SNDERR("softvol supports only S16_LE, S16_BE, S24_3LE, S32_LE "
586 svol->sformat = slave->format;
590 static snd_pcm_uframes_t
591 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
592 const snd_pcm_channel_area_t *areas,
593 snd_pcm_uframes_t offset,
594 snd_pcm_uframes_t size,
595 const snd_pcm_channel_area_t *slave_areas,
596 snd_pcm_uframes_t slave_offset,
597 snd_pcm_uframes_t *slave_sizep)
599 snd_pcm_softvol_t *svol = pcm->private_data;
600 if (size > *slave_sizep)
602 get_current_volume(svol);
603 if (svol->cchannels == 1)
604 softvol_convert_mono_vol(svol, slave_areas, slave_offset,
605 areas, offset, pcm->channels, size);
607 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
608 areas, offset, pcm->channels, size);
613 static snd_pcm_uframes_t
614 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
615 const snd_pcm_channel_area_t *areas,
616 snd_pcm_uframes_t offset,
617 snd_pcm_uframes_t size,
618 const snd_pcm_channel_area_t *slave_areas,
619 snd_pcm_uframes_t slave_offset,
620 snd_pcm_uframes_t *slave_sizep)
622 snd_pcm_softvol_t *svol = pcm->private_data;
623 if (size > *slave_sizep)
625 get_current_volume(svol);
626 if (svol->cchannels == 1)
627 softvol_convert_mono_vol(svol, areas, offset, slave_areas,
628 slave_offset, pcm->channels, size);
630 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
631 slave_offset, pcm->channels, size);
636 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
638 snd_pcm_softvol_t *svol = pcm->private_data;
639 snd_output_printf(out, "Soft volume PCM\n");
640 snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
641 if (svol->max_val == 1)
642 snd_output_printf(out, "boolean\n");
644 snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
645 snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
646 snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
649 snd_output_printf(out, "Its setup is:\n");
650 snd_pcm_dump_setup(pcm, out);
652 snd_output_printf(out, "Slave: ");
653 snd_pcm_dump(svol->plug.gen.slave, out);
656 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
659 tlv[0] = SND_CTL_TLVT_DB_SCALE;
660 tlv[1] = 2 * sizeof(int);
661 tlv[2] = svol->min_dB * 100;
662 tlv[3] = (svol->max_dB - svol->min_dB) * 100 / svol->max_val;
663 return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
666 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo, int count)
670 unsigned int def_val;
672 if (svol->max_val == 1)
673 err = snd_ctl_elem_add_boolean(svol->ctl, &cinfo->id, count);
675 err = snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, count,
676 0, svol->max_val, 0);
679 if (svol->max_val == 1)
682 add_tlv_info(svol, cinfo);
683 /* set zero dB value as default, or max_val if
684 there is no 0 dB setting */
685 def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
687 for (i = 0; i < count; i++)
688 svol->elem.value.integer.value[i] = def_val;
689 return snd_ctl_elem_write(svol->ctl, &svol->elem);
693 * load and set up user-control
694 * returns 0 if the user-control is found or created,
695 * returns 1 if the control is a hw control,
696 * or a negative error code
698 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
699 int ctl_card, snd_ctl_elem_id_t *ctl_id,
700 int cchannels, double min_dB, double max_dB,
704 snd_pcm_info_t *info;
705 snd_ctl_elem_info_t *cinfo;
710 snd_pcm_info_alloca(&info);
711 err = snd_pcm_info(pcm, info);
714 ctl_card = snd_pcm_info_get_card(info);
716 SNDERR("No card defined for softvol control");
720 sprintf(tmp_name, "hw:%d", ctl_card);
721 err = snd_ctl_open(&svol->ctl, tmp_name, 0);
723 SNDERR("Cannot open CTL %s", tmp_name);
727 svol->elem.id = *ctl_id;
728 svol->max_val = resolution - 1;
729 svol->min_dB = min_dB;
730 svol->max_dB = max_dB;
731 if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
732 svol->zero_dB_val = svol->max_val;
733 else if (svol->max_dB < 0)
734 svol->zero_dB_val = 0; /* there is no 0 dB setting */
736 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) * svol->max_val;
738 snd_ctl_elem_info_alloca(&cinfo);
739 snd_ctl_elem_info_set_id(cinfo, ctl_id);
740 if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
741 if (err != -ENOENT) {
742 SNDERR("Cannot get info for CTL %s", tmp_name);
745 err = add_user_ctl(svol, cinfo, cchannels);
747 SNDERR("Cannot add a control");
751 if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
752 /* hardware control exists */
753 return 1; /* notify */
755 } else if ((cinfo->type != SND_CTL_ELEM_TYPE_INTEGER &&
756 cinfo->type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
757 cinfo->count != (unsigned int)cchannels ||
758 cinfo->value.integer.min != 0 ||
759 cinfo->value.integer.max != resolution - 1) {
760 if ((err = snd_ctl_elem_remove(svol->ctl, &cinfo->id)) < 0) {
761 SNDERR("Control %s mismatch", tmp_name);
764 snd_ctl_elem_info_set_id(cinfo, ctl_id); /* reset numid */
765 if ((err = add_user_ctl(svol, cinfo, cchannels)) < 0) {
766 SNDERR("Cannot add a control");
769 } else if (svol->max_val > 1) {
770 /* check TLV availability */
772 err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo->id, tlv, sizeof(tlv));
774 add_tlv_info(svol, cinfo);
778 if (svol->max_val == 1)
781 /* set up dB table */
782 if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB && resolution == PRESET_RESOLUTION)
783 svol->dB_value = (unsigned int*)preset_dB_value;
785 #ifndef HAVE_SOFT_FLOAT
786 svol->dB_value = calloc(resolution, sizeof(unsigned int));
787 if (! svol->dB_value) {
788 SNDERR("cannot allocate dB table");
791 svol->min_dB = min_dB;
792 svol->max_dB = max_dB;
793 for (i = 0; i <= svol->max_val; i++) {
794 double db = svol->min_dB + (i * (svol->max_dB - svol->min_dB)) / svol->max_val;
795 double v = (pow(10.0, db / 20.0) * (double)(1 << VOL_SCALE_SHIFT));
796 svol->dB_value[i] = (unsigned int)v;
798 if (svol->zero_dB_val)
799 svol->dB_value[svol->zero_dB_val] = 65535;
801 SNDERR("Cannot handle the given dB range and resolution");
808 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
809 .close = snd_pcm_softvol_close,
810 .info = snd_pcm_generic_info,
811 .hw_refine = snd_pcm_softvol_hw_refine,
812 .hw_params = snd_pcm_softvol_hw_params,
813 .hw_free = snd_pcm_generic_hw_free,
814 .sw_params = snd_pcm_generic_sw_params,
815 .channel_info = snd_pcm_generic_channel_info,
816 .dump = snd_pcm_softvol_dump,
817 .nonblock = snd_pcm_generic_nonblock,
818 .async = snd_pcm_generic_async,
819 .mmap = snd_pcm_generic_mmap,
820 .munmap = snd_pcm_generic_munmap,
824 * \brief Creates a new SoftVolume PCM
825 * \param pcmp Returns created PCM handle
826 * \param name Name of PCM
827 * \param sformat Slave format
828 * \param ctl_card card index of the control
829 * \param ctl_id The control element
830 * \param cchannels PCM channels
831 * \param min_dB minimal dB value
832 * \param max_dB maximal dB value
833 * \param resolution resolution of control
834 * \param slave Slave PCM handle
835 * \param close_slave When set, the slave PCM handle is closed with copy PCM
836 * \retval zero on success otherwise a negative error code
837 * \warning Using of this function might be dangerous in the sense
838 * of compatibility reasons. The prototype might be freely
841 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
842 snd_pcm_format_t sformat,
843 int ctl_card, snd_ctl_elem_id_t *ctl_id,
845 double min_dB, double max_dB, int resolution,
846 snd_pcm_t *slave, int close_slave)
849 snd_pcm_softvol_t *svol;
851 assert(pcmp && slave);
852 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
853 sformat != SND_PCM_FORMAT_S16_LE &&
854 sformat != SND_PCM_FORMAT_S16_BE &&
855 sformat != SND_PCM_FORMAT_S24_3LE &&
856 sformat != SND_PCM_FORMAT_S32_LE &&
857 sformat != SND_PCM_FORMAT_S32_BE)
859 svol = calloc(1, sizeof(*svol));
862 err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
863 min_dB, max_dB, resolution);
868 if (err > 0) { /* hardware control - no need for softvol! */
870 *pcmp = slave; /* just pass the slave */
871 if (!slave->name && name)
872 slave->name = strdup(name);
877 snd_pcm_plugin_init(&svol->plug);
878 svol->sformat = sformat;
879 svol->cchannels = cchannels;
880 svol->plug.read = snd_pcm_softvol_read_areas;
881 svol->plug.write = snd_pcm_softvol_write_areas;
882 svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
883 svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
884 svol->plug.gen.slave = slave;
885 svol->plug.gen.close_slave = close_slave;
887 err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
892 pcm->ops = &snd_pcm_softvol_ops;
893 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
894 pcm->private_data = svol;
895 pcm->poll_fd = slave->poll_fd;
896 pcm->poll_events = slave->poll_events;
898 * Since the softvol converts on the place, and the format/channels
899 * must be identical between source and destination, we don't need
902 pcm->mmap_shadow = 1;
903 pcm->monotonic = slave->monotonic;
904 snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
905 snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
912 int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
913 int *cchannelsp, int *hwctlp);
915 /*! \page pcm_plugins
917 \section pcm_plugins_softvol Plugin: Soft Volume
919 This plugin applies the software volume attenuation.
920 The format, rate and channels must match for both of source and destination.
922 When the control is stereo (count=2), the channels are assumed to be either
923 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
925 If the control already exists and it's a system control (i.e. no
926 user-defined control), the plugin simply passes its slave without
931 type softvol # Soft Volume conversion PCM
932 slave STR # Slave name
934 slave { # Slave definition
935 pcm STR # Slave PCM name
937 pcm { } # Slave PCM definition
938 [format STR] # Slave format
941 name STR # control element id string
942 [card STR] # control card index
943 [iface STR] # interface of the element
944 [index INT] # index of the element
945 [device INT] # device number of the element
946 [subdevice INT] # subdevice number of the element
947 [count INT] # control channels 1 or 2 (default: 2)
949 [min_dB REAL] # minimal dB value (default: -51.0)
950 [max_dB REAL] # maximal dB value (default: 0.0)
951 [resolution INT] # resolution (default: 256)
952 # resolution = 2 means a mute switch
956 \subsection pcm_plugins_softvol_funcref Function reference
959 <LI>snd_pcm_softvol_open()
960 <LI>_snd_pcm_softvol_open()
966 * \brief Creates a new Soft Volume PCM
967 * \param pcmp Returns created PCM handle
968 * \param name Name of PCM
969 * \param root Root configuration node
970 * \param conf Configuration node with Soft Volume PCM description
971 * \param stream Stream type
972 * \param mode Stream mode
973 * \retval zero on success otherwise a negative error code
974 * \warning Using of this function might be dangerous in the sense
975 * of compatibility reasons. The prototype might be freely
978 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
979 snd_config_t *root, snd_config_t *conf,
980 snd_pcm_stream_t stream, int mode)
982 snd_config_iterator_t i, next;
985 snd_config_t *slave = NULL, *sconf;
986 snd_config_t *control = NULL;
987 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
988 snd_ctl_elem_id_t *ctl_id;
989 int resolution = PRESET_RESOLUTION;
990 double min_dB = PRESET_MIN_DB;
991 double max_dB = ZERO_DB;
992 int card = -1, cchannels = 2;
994 snd_config_for_each(i, next, conf) {
995 snd_config_t *n = snd_config_iterator_entry(i);
997 if (snd_config_get_id(n, &id) < 0)
999 if (snd_pcm_conf_generic_id(id))
1001 if (strcmp(id, "slave") == 0) {
1005 if (strcmp(id, "control") == 0) {
1009 if (strcmp(id, "resolution") == 0) {
1011 err = snd_config_get_integer(n, &v);
1013 SNDERR("Invalid resolution value");
1019 if (strcmp(id, "min_dB") == 0) {
1020 err = snd_config_get_real(n, &min_dB);
1022 SNDERR("Invalid min_dB value");
1027 if (strcmp(id, "max_dB") == 0) {
1028 err = snd_config_get_real(n, &max_dB);
1030 SNDERR("Invalid max_dB value");
1035 SNDERR("Unknown field %s", id);
1039 SNDERR("slave is not defined");
1043 SNDERR("control is not defined");
1047 SNDERR("min_dB must be a negative value");
1050 if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1051 SNDERR("max_dB must be larger than min_dB and less than %d dB",
1052 MAX_DB_UPPER_LIMIT);
1055 if (resolution <= 1 || resolution > 1024) {
1056 SNDERR("Invalid resolution value %d", resolution);
1059 if (mode & SND_PCM_NO_SOFTVOL) {
1060 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1063 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1065 snd_config_delete(sconf);
1067 snd_ctl_elem_id_alloca(&ctl_id);
1068 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1069 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1072 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1073 sformat != SND_PCM_FORMAT_S16_LE &&
1074 sformat != SND_PCM_FORMAT_S16_BE &&
1075 sformat != SND_PCM_FORMAT_S24_3LE &&
1076 sformat != SND_PCM_FORMAT_S32_LE &&
1077 sformat != SND_PCM_FORMAT_S32_BE) {
1078 SNDERR("only S16_LE, S16_BE, S24_3LE, S32_LE or S32_BE format "
1080 snd_config_delete(sconf);
1083 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1084 snd_config_delete(sconf);
1087 if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) {
1088 snd_pcm_close(spcm);
1091 err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, cchannels,
1092 min_dB, max_dB, resolution, spcm, 1);
1094 snd_pcm_close(spcm);
1099 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);