• Home
  • History
  • Annotate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/iserver/alsa-lib-1.0.26/src/pcm/

Lines Matching defs:*

2  * \file pcm/pcm_softvol.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Soft Volume Plugin Interface
5 * \author Takashi Iwai <tiwai@suse.de>
6 * \date 2004
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
29 #include <byteswap.h>
30 #include <math.h>
31 #include "pcm_local.h"
32 #include "pcm_plugin.h"
34 #ifndef PIC
35 /* entry for static linking */
36 const char *_snd_module_pcm_softvol = "";
37 #endif
39 #ifndef DOC_HIDDEN
41 typedef struct {
42 /* This field need to be the first */
43 snd_pcm_plugin_t plug;
44 snd_pcm_format_t sformat;
45 unsigned int cchannels;
46 snd_ctl_t *ctl;
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 */
51 double min_dB;
52 double max_dB;
53 unsigned int *dB_value;
54 } snd_pcm_softvol_t;
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
61 #define ZERO_DB 0.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 */
100 typedef union {
101 int i;
102 short s[2];
103 } val_t;
104 static inline int MULTI_DIV_32x16(int a, unsigned short b)
106 val_t v, x, y;
107 v.i = a;
108 y.i = 0;
109 #if __BYTE_ORDER == __LITTLE_ENDIAN
110 x.i = (unsigned short)v.s[0];
111 x.i *= b;
112 y.s[0] = x.s[1];
113 y.i += (int)v.s[1] * b;
114 #else
115 x.i = (unsigned int)v.s[1] * b;
116 y.s[1] = x.s[0];
117 y.i += (int)v.s[0] * b;
118 #endif
119 return y.i;
122 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
124 unsigned int gain = (b >> VOL_SCALE_SHIFT);
125 int fraction;
126 a = swap ? (int)bswap_32(a) : a;
127 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
128 if (gain) {
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;
143 int fraction;
144 fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
145 if (gain) {
146 long long amp = (long long)a * gain + fraction;
147 if (amp > (int)0x7fffff)
148 amp = (int)0x7fffff;
149 else if (amp < (int)0x800000)
150 amp = (int)0x800000;
151 return (int)amp;
153 return fraction;
156 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
158 unsigned int gain = b >> VOL_SCALE_SHIFT;
159 int fraction;
160 a = swap ? (short)bswap_16(a) : a;
161 fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
162 if (gain) {
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
179 #ifndef DOC_HIDDEN
180 #define CONVERT_AREA(TYPE, swap) do { \
181 unsigned int ch, fr; \
182 TYPE *src, *dst; \
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); \
190 GET_VOL_SCALE; \
191 fr = frames; \
192 if (! vol_scale) { \
193 while (fr--) { \
194 *dst = 0; \
195 dst += dst_step; \
197 } else if (vol_scale == 0xffff) { \
198 while (fr--) { \
199 *dst = *src; \
200 src += src_step; \
201 dst += dst_step; \
203 } else { \
204 while (fr--) { \
205 *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
206 src += src_step; \
207 dst += dst_step; \
211 } while (0)
213 #define CONVERT_AREA_S24_3LE() do { \
214 unsigned int ch, fr; \
215 unsigned char *src, *dst; \
216 int tmp; \
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); \
224 GET_VOL_SCALE; \
225 fr = frames; \
226 if (! vol_scale) { \
227 while (fr--) { \
228 dst[0] = dst[1] = dst[2] = 0; \
229 dst += dst_step; \
231 } else if (vol_scale == 0xffff) { \
232 while (fr--) { \
233 dst[0] = src[0]; \
234 dst[1] = src[1]; \
235 dst[2] = src[2]; \
236 src += dst_step; \
237 dst += src_step; \
239 } else { \
240 while (fr--) { \
241 tmp = src[0] | \
242 (src[1] << 8) | \
243 (((signed char *) src)[2] << 16); \
244 tmp = MULTI_DIV_24(tmp, vol_scale); \
245 dst[0] = tmp; \
246 dst[1] = tmp >> 8; \
247 dst[2] = tmp >> 16; \
248 src += dst_step; \
249 dst += src_step; \
253 } while (0)
255 #define GET_VOL_SCALE \
256 switch (ch) { \
257 case 0: \
258 case 2: \
259 vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
260 break; \
261 case 4: \
262 case 5: \
263 vol_scale = vol_c; \
264 break; \
265 default: \
266 vol_scale = vol[ch & 1]; \
267 break; \
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,
287 svol->sformat);
288 return;
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);
293 return;
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];
300 } else {
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:
308 /* 16bit samples */
309 CONVERT_AREA(short,
310 !snd_pcm_format_cpu_endian(svol->sformat));
311 break;
312 case SND_PCM_FORMAT_S32_LE:
313 case SND_PCM_FORMAT_S32_BE:
314 /* 32bit samples */
315 CONVERT_AREA(int,
316 !snd_pcm_format_cpu_endian(svol->sformat));
317 break;
318 case SND_PCM_FORMAT_S24_3LE:
319 CONVERT_AREA_S24_3LE();
320 break;
321 default:
322 break;
326 #undef GET_VOL_SCALE
327 #define GET_VOL_SCALE
329 /* mono control */
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,
344 svol->sformat);
345 return;
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);
349 return;
352 if (svol->max_val == 1)
353 vol_scale = svol->cur_vol[0] ? 0xffff : 0;
354 else
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:
359 /* 16bit samples */
360 CONVERT_AREA(short,
361 !snd_pcm_format_cpu_endian(svol->sformat));
362 break;
363 case SND_PCM_FORMAT_S32_LE:
364 case SND_PCM_FORMAT_S32_BE:
365 /* 32bit samples */
366 CONVERT_AREA(int,
367 !snd_pcm_format_cpu_endian(svol->sformat));
368 break;
369 case SND_PCM_FORMAT_S24_3LE:
370 CONVERT_AREA_S24_3LE();
371 break;
372 default:
373 break;
378 * get the current volume value from driver
380 * TODO: mmap support?
382 static void get_current_volume(snd_pcm_softvol_t *svol)
384 unsigned int val;
385 unsigned int i;
387 if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
388 return;
389 for (i = 0; i < svol->cchannels; i++) {
390 val = svol->elem.value.integer.value[i];
391 if (val > svol->max_val)
392 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);
401 if (svol->ctl)
402 snd_ctl_close(svol->ctl);
403 if (svol->dB_value && svol->dB_value != preset_dB_value)
404 free(svol->dB_value);
405 free(svol);
408 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
410 snd_pcm_softvol_t *svol = pcm->private_data;
411 softvol_free(svol);
412 return 0;
415 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
416 snd_pcm_hw_params_t *params)
418 int err;
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,
435 &access_mask);
436 if (err < 0)
437 return err;
438 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
439 &format_mask);
440 if (err < 0)
441 return err;
442 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
443 if (err < 0)
444 return err;
445 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
446 if (err < 0)
447 return err;
448 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
449 return 0;
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,
458 &saccess_mask);
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);
463 return 0;
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;
503 int err;
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);
517 if (err < 0)
518 return err;
520 err = check_access_mask(params, sparams);
521 if (err < 0)
522 return err;
524 return 0;
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;
532 int err;
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);
546 if (err < 0)
547 return err;
549 err = check_access_mask(sparams, params);
550 if (err < 0)
551 return err;
553 return 0;
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);
575 if (err < 0)
576 return err;
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 "
583 " or S32_BE");
584 return -EINVAL;
586 svol->sformat = slave->format;
587 return 0;
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)
601 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);
606 else
607 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
608 areas, offset, pcm->channels, size);
609 *slave_sizep = size;
610 return 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)
624 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);
629 else
630 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
631 slave_offset, pcm->channels, size);
632 *slave_sizep = size;
633 return 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");
643 else {
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);
648 if (pcm->setup) {
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)
658 unsigned int tlv[4];
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)
668 int err;
669 int i;
670 unsigned int def_val;
672 if (svol->max_val == 1)
673 err = snd_ctl_elem_add_boolean(svol->ctl, &cinfo->id, count);
674 else
675 err = snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, count,
676 0, svol->max_val, 0);
677 if (err < 0)
678 return err;
679 if (svol->max_val == 1)
680 def_val = 1;
681 else {
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,
701 int resolution)
703 char tmp_name[32];
704 snd_pcm_info_t *info;
705 snd_ctl_elem_info_t *cinfo;
706 int err;
707 unsigned int i;
709 if (ctl_card < 0) {
710 snd_pcm_info_alloca(&info);
711 err = snd_pcm_info(pcm, info);
712 if (err < 0)
713 return err;
714 ctl_card = snd_pcm_info_get_card(info);
715 if (ctl_card < 0) {
716 SNDERR("No card defined for softvol control");
717 return -EINVAL;
720 sprintf(tmp_name, "hw:%d", ctl_card);
721 err = snd_ctl_open(&svol->ctl, tmp_name, 0);
722 if (err < 0) {
723 SNDERR("Cannot open CTL %s", tmp_name);
724 return err;
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 */
735 else
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);
743 return err;
745 err = add_user_ctl(svol, cinfo, cchannels);
746 if (err < 0) {
747 SNDERR("Cannot add a control");
748 return err;
750 } else {
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);
762 return err;
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");
767 return err;
769 } else if (svol->max_val > 1) {
770 /* check TLV availability */
771 unsigned int tlv[4];
772 err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo->id, tlv, sizeof(tlv));
773 if (err < 0)
774 add_tlv_info(svol, cinfo);
778 if (svol->max_val == 1)
779 return 0;
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;
784 else {
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");
789 return -ENOMEM;
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;
800 #else
801 SNDERR("Cannot handle the given dB range and resolution");
802 return -EINVAL;
803 #endif
805 return 0;
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
839 * changed in future.
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,
844 int cchannels,
845 double min_dB, double max_dB, int resolution,
846 snd_pcm_t *slave, int close_slave)
848 snd_pcm_t *pcm;
849 snd_pcm_softvol_t *svol;
850 int err;
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)
858 return -EINVAL;
859 svol = calloc(1, sizeof(*svol));
860 if (! svol)
861 return -ENOMEM;
862 err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
863 min_dB, max_dB, resolution);
864 if (err < 0) {
865 softvol_free(svol);
866 return err;
868 if (err > 0) { /* hardware control - no need for softvol! */
869 softvol_free(svol);
870 *pcmp = slave; /* just pass the slave */
871 if (!slave->name && name)
872 slave->name = strdup(name);
873 return 0;
876 /* do softvol */
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);
888 if (err < 0) {
889 softvol_free(svol);
890 return err;
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
900 * an extra buffer.
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);
906 *pcmp = pcm;
908 return 0;
911 /* in pcm_misc.c */
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
927 any changes.
929 \code
930 pcm.name {
931 type softvol # Soft Volume conversion PCM
932 slave STR # Slave name
933 # or
934 slave { # Slave definition
935 pcm STR # Slave PCM name
936 # or
937 pcm { } # Slave PCM definition
938 [format STR] # Slave format
940 control {
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
954 \endcode
956 \subsection pcm_plugins_softvol_funcref Function reference
958 <UL>
959 <LI>snd_pcm_softvol_open()
960 <LI>_snd_pcm_softvol_open()
961 </UL>
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
976 * changed in future.
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;
983 int err;
984 snd_pcm_t *spcm;
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);
996 const char *id;
997 if (snd_config_get_id(n, &id) < 0)
998 continue;
999 if (snd_pcm_conf_generic_id(id))
1000 continue;
1001 if (strcmp(id, "slave") == 0) {
1002 slave = n;
1003 continue;
1005 if (strcmp(id, "control") == 0) {
1006 control = n;
1007 continue;
1009 if (strcmp(id, "resolution") == 0) {
1010 long v;
1011 err = snd_config_get_integer(n, &v);
1012 if (err < 0) {
1013 SNDERR("Invalid resolution value");
1014 return err;
1016 resolution = v;
1017 continue;
1019 if (strcmp(id, "min_dB") == 0) {
1020 err = snd_config_get_real(n, &min_dB);
1021 if (err < 0) {
1022 SNDERR("Invalid min_dB value");
1023 return err;
1025 continue;
1027 if (strcmp(id, "max_dB") == 0) {
1028 err = snd_config_get_real(n, &max_dB);
1029 if (err < 0) {
1030 SNDERR("Invalid max_dB value");
1031 return err;
1033 continue;
1035 SNDERR("Unknown field %s", id);
1036 return -EINVAL;
1038 if (!slave) {
1039 SNDERR("slave is not defined");
1040 return -EINVAL;
1042 if (!control) {
1043 SNDERR("control is not defined");
1044 return -EINVAL;
1046 if (min_dB >= 0) {
1047 SNDERR("min_dB must be a negative value");
1048 return -EINVAL;
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);
1053 return -EINVAL;
1055 if (resolution <= 1 || resolution > 1024) {
1056 SNDERR("Invalid resolution value %d", resolution);
1057 return -EINVAL;
1059 if (mode & SND_PCM_NO_SOFTVOL) {
1060 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1061 if (err < 0)
1062 return err;
1063 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1064 mode, conf);
1065 snd_config_delete(sconf);
1066 } else {
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);
1070 if (err < 0)
1071 return err;
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 "
1079 "is supported");
1080 snd_config_delete(sconf);
1081 return -EINVAL;
1083 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1084 snd_config_delete(sconf);
1085 if (err < 0)
1086 return err;
1087 if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) {
1088 snd_pcm_close(spcm);
1089 return err;
1091 err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, cchannels,
1092 min_dB, max_dB, resolution, spcm, 1);
1093 if (err < 0)
1094 snd_pcm_close(spcm);
1096 return err;
1098 #ifndef DOC_HIDDEN
1099 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1100 #endif