]> git.alsa-project.org Git - alsa-lib.git/blob - src/pcm/pcm_softvol.c
38c6367944feee5c6216e66dd328987da31bf0da
[alsa-lib.git] / src / pcm / pcm_softvol.c
1 /**
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
7  */
8 /*
9  *  PCM - Soft Volume Plugin
10  *  Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
11  *
12  *
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.
17  *
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.
22  *
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
26  *
27  */
28
29 #include "pcm_local.h"
30 #include "pcm_plugin.h"
31 #include "bswap.h"
32 #include <math.h>
33 #include <sound/tlv.h>
34
35 #ifndef PIC
36 /* entry for static linking */
37 const char *_snd_module_pcm_softvol = "";
38 #endif
39
40 #ifndef DOC_HIDDEN
41
42 typedef struct {
43         /* This field need to be the first */
44         snd_pcm_plugin_t plug;
45         snd_pcm_format_t sformat;
46         unsigned int cchannels;
47         snd_ctl_t *ctl;
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 */
52         double min_dB;
53         double max_dB;
54         unsigned int *dB_value;
55 } snd_pcm_softvol_t;
56
57 #define VOL_SCALE_SHIFT         16
58 #define VOL_SCALE_MASK          ((1 << VOL_SCALE_SHIFT) - 1)
59
60 #define PRESET_RESOLUTION       256
61 #define PRESET_MIN_DB           -51.0
62 #define ZERO_DB                  0.0
63 /*
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.
66  */
67 #define MAX_DB_UPPER_LIMIT      90
68
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,
102 };
103
104 /* (32bit x 16bit) >> 16 */
105 typedef union {
106         int i;
107         short s[2];
108 } val_t;
109 static inline int MULTI_DIV_32x16(int a, unsigned short b)
110 {
111         val_t v, x, y;
112         v.i = a;
113         y.i = 0;
114 #if __BYTE_ORDER == __LITTLE_ENDIAN
115         x.i = (unsigned short)v.s[0];
116         x.i *= (unsigned int)b;
117         y.s[0] = x.s[1];
118         y.i += (int)v.s[1] * b;
119 #else
120         x.i = (unsigned int)v.s[1] * b;
121         y.s[1] = x.s[0];
122         y.i += (int)v.s[0] * b;
123 #endif
124         return y.i;
125 }
126
127 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
128 {
129         unsigned int gain = (b >> VOL_SCALE_SHIFT);
130         int fraction;
131         a = swap ? (int)bswap_32(a) : a;
132         fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
133         if (gain) {
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;
140         }
141         return swap ? (int)bswap_32(fraction) : fraction;
142 }
143
144 /* always little endian */
145 static inline int MULTI_DIV_24(int a, unsigned int b)
146 {
147         unsigned int gain = b >> VOL_SCALE_SHIFT;
148         int fraction;
149         fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
150         if (gain) {
151                 long long amp = (long long)a * gain + fraction;
152                 if (amp > (int)0x7fffff)
153                         amp = (int)0x7fffff;
154                 else if (amp < (int)0x800000)
155                         amp = (int)0x800000;
156                 return (int)amp;
157         }
158         return fraction;
159 }
160
161 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
162 {
163         unsigned int gain = b >> VOL_SCALE_SHIFT;
164         int fraction;
165         a = swap ? (short)bswap_16(a) : a;
166         fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
167         if (gain) {
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;
172         }
173         return swap ? (short)bswap_16((short)fraction) : (short)fraction;
174 }
175
176 #endif /* DOC_HIDDEN */
177
178 /*
179  * apply volumue attenuation
180  *
181  * TODO: use SIMD operations
182  */
183
184 #ifndef DOC_HIDDEN
185 #define CONVERT_AREA(TYPE, swap) do {   \
186         unsigned int ch, fr; \
187         TYPE *src, *dst; \
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); \
195                 GET_VOL_SCALE; \
196                 fr = frames; \
197                 if (! vol_scale) { \
198                         while (fr--) { \
199                                 *dst = 0; \
200                                 dst += dst_step; \
201                         } \
202                 } else if (vol_scale == 0xffff) { \
203                         while (fr--) { \
204                                 *dst = *src; \
205                                 src += src_step; \
206                                 dst += dst_step; \
207                         } \
208                 } else { \
209                         while (fr--) { \
210                                 *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
211                                 src += src_step; \
212                                 dst += dst_step; \
213                         } \
214                 } \
215         } \
216 } while (0)
217
218 #define CONVERT_AREA_S24_3LE() do {                                     \
219         unsigned int ch, fr;                                            \
220         unsigned char *src, *dst;                                       \
221         int tmp;                                                        \
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);         \
229                 GET_VOL_SCALE;                                          \
230                 fr = frames;                                            \
231                 if (! vol_scale) {                                      \
232                         while (fr--) {                                  \
233                                 dst[0] = dst[1] = dst[2] = 0;           \
234                                 dst += dst_step;                        \
235                         }                                               \
236                 } else if (vol_scale == 0xffff) {                       \
237                         while (fr--) {                                  \
238                                 dst[0] = src[0];                        \
239                                 dst[1] = src[1];                        \
240                                 dst[2] = src[2];                        \
241                                 src += dst_step;                        \
242                                 dst += src_step;                        \
243                         }                                               \
244                 } else {                                                \
245                         while (fr--) {                                  \
246                                 tmp = src[0] |                          \
247                                       (src[1] << 8) |                   \
248                                       (((signed char *) src)[2] << 16); \
249                                 tmp = MULTI_DIV_24(tmp, vol_scale);     \
250                                 dst[0] = tmp;                           \
251                                 dst[1] = tmp >> 8;                      \
252                                 dst[2] = tmp >> 16;                     \
253                                 src += dst_step;                        \
254                                 dst += src_step;                        \
255                         }                                               \
256                 }                                                       \
257         }                                                               \
258 } while (0)
259
260 #define CONVERT_AREA_S24_LE() do {                                      \
261         unsigned int ch, fr;                                            \
262         int *src, *dst;                                                 \
263         int tmp;                                                        \
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)          \
270                                 / sizeof(int);                          \
271                 dst_step = snd_pcm_channel_area_step(dst_area)          \
272                                 / sizeof(int);                          \
273                 GET_VOL_SCALE;                                          \
274                 fr = frames;                                            \
275                 if (! vol_scale) {                                      \
276                         while (fr--) {                                  \
277                                 *dst = 0;                               \
278                                 dst += dst_step;                        \
279                         }                                               \
280                 } else if (vol_scale == 0xffff) {                       \
281                         while (fr--) {                                  \
282                                 *dst = *src;                            \
283                                 src += dst_step;                        \
284                                 dst += src_step;                        \
285                         }                                               \
286                 } else {                                                \
287                         while (fr--) {                                  \
288                                 tmp = *src << 8;                        \
289                                 tmp = (signed int) tmp >> 8;            \
290                                 *dst = MULTI_DIV_24(tmp, vol_scale);    \
291                                 src += dst_step;                        \
292                                 dst += src_step;                        \
293                         }                                               \
294                 }                                                       \
295         }                                                               \
296 } while (0)
297                 
298 #define GET_VOL_SCALE \
299         switch (ch) { \
300         case 0: \
301         case 2: \
302                 vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
303                 break; \
304         case 4: \
305         case 5: \
306                 vol_scale = vol_c; \
307                 break; \
308         default: \
309                 vol_scale = vol[ch & 1]; \
310                 break; \
311         }
312
313 #endif /* DOC_HIDDEN */
314
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)
323 {
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;
327
328         if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
329                 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
330                                       svol->sformat);
331                 return;
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);
336                 return;
337         }
338
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];
343         } else {
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];
347         }
348         switch (svol->sformat) {
349         case SND_PCM_FORMAT_S16_LE:
350         case SND_PCM_FORMAT_S16_BE:
351                 /* 16bit samples */
352                 CONVERT_AREA(short, 
353                              !snd_pcm_format_cpu_endian(svol->sformat));
354                 break;
355         case SND_PCM_FORMAT_S32_LE:
356         case SND_PCM_FORMAT_S32_BE:
357                 /* 32bit samples */
358                 CONVERT_AREA(int,
359                              !snd_pcm_format_cpu_endian(svol->sformat));
360                 break;
361         case SND_PCM_FORMAT_S24_LE:
362                 /* 24bit samples */
363                 CONVERT_AREA_S24_LE();
364                 break;
365         case SND_PCM_FORMAT_S24_3LE:
366                 CONVERT_AREA_S24_3LE();
367                 break;
368         default:
369                 break;
370         }
371 }
372
373 #undef GET_VOL_SCALE
374 #define GET_VOL_SCALE
375
376 /* mono control */
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)
384 {
385         const snd_pcm_channel_area_t *dst_area, *src_area;
386         unsigned int src_step, dst_step;
387         unsigned int vol_scale;
388
389         if (svol->cur_vol[0] == 0) {
390                 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
391                                       svol->sformat);
392                 return;
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);
396                 return;
397         }
398
399         if (svol->max_val == 1)
400                 vol_scale = svol->cur_vol[0] ? 0xffff : 0;
401         else
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:
406                 /* 16bit samples */
407                 CONVERT_AREA(short, 
408                              !snd_pcm_format_cpu_endian(svol->sformat));
409                 break;
410         case SND_PCM_FORMAT_S32_LE:
411         case SND_PCM_FORMAT_S32_BE:
412                 /* 32bit samples */
413                 CONVERT_AREA(int,
414                              !snd_pcm_format_cpu_endian(svol->sformat));
415                 break;
416         case SND_PCM_FORMAT_S24_LE:
417                 /* 24bit samples */
418                 CONVERT_AREA_S24_LE();
419                 break;
420         case SND_PCM_FORMAT_S24_3LE:
421                 CONVERT_AREA_S24_3LE();
422                 break;
423         default:
424                 break;
425         }
426 }
427
428 /*
429  * get the current volume value from driver
430  *
431  * TODO: mmap support?
432  */
433 static void get_current_volume(snd_pcm_softvol_t *svol)
434 {
435         unsigned int val;
436         unsigned int i;
437
438         if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
439                 return;
440         for (i = 0; i < svol->cchannels; i++) {
441                 val = svol->elem.value.integer.value[i];
442                 if (val > svol->max_val)
443                         val = svol->max_val;
444                 svol->cur_vol[i] = val;
445         }
446 }
447
448 static void softvol_free(snd_pcm_softvol_t *svol)
449 {
450         if (svol->plug.gen.close_slave)
451                 snd_pcm_close(svol->plug.gen.slave);
452         if (svol->ctl)
453                 snd_ctl_close(svol->ctl);
454         if (svol->dB_value && svol->dB_value != preset_dB_value)
455                 free(svol->dB_value);
456         free(svol);
457 }
458
459 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
460 {
461         snd_pcm_softvol_t *svol = pcm->private_data;
462         softvol_free(svol);
463         return 0;
464 }
465
466 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
467                                               snd_pcm_hw_params_t *params)
468 {
469         int err;
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 = {
473                 {
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))
480                 }
481         };
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);
485         }
486         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
487                                          &access_mask);
488         if (err < 0)
489                 return err;
490         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
491                                          &format_mask);
492         if (err < 0)
493                 return err;
494         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
495         if (err < 0)
496                 return err;
497         err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
498         if (err < 0)
499                 return err;
500         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
501         return 0;
502 }
503
504 static int snd_pcm_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
505 {
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,
510                                    &saccess_mask);
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);
514         }
515         return 0;
516 }
517
518 /*
519  * refine the access mask
520  */
521 static int check_access_mask(snd_pcm_hw_params_t *src,
522                              snd_pcm_hw_params_t *dst)
523 {
524         const snd_pcm_access_mask_t *mask;
525         snd_pcm_access_mask_t smask;
526
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);
535         }
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);
542         }
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);
546
547         return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask);
548 }
549
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)
553 {
554         snd_pcm_softvol_t *svol = pcm->private_data;
555         int err;
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);
569         if (err < 0)
570                 return err;
571
572         err = check_access_mask(params, sparams);
573         if (err < 0)
574                 return err;
575
576         return 0;
577 }
578         
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)
582 {
583         snd_pcm_softvol_t *svol = pcm->private_data;
584         int err;
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);
598         if (err < 0)
599                 return err;
600
601         err = check_access_mask(sparams, params);
602         if (err < 0)
603                 return err;
604
605         return 0;
606 }
607
608 static int snd_pcm_softvol_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
609 {
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);
616 }
617
618 static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
619 {
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);
627         if (err < 0)
628                 return err;
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, "
636                        "S32_LE or S32_BE");
637                 return -EINVAL;
638         }
639         svol->sformat = slave->format;
640         return 0;
641 }
642
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)
651 {
652         snd_pcm_softvol_t *svol = pcm->private_data;
653         if (size > *slave_sizep)
654                 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);
659         else
660                 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
661                                            areas, offset, pcm->channels, size);
662         *slave_sizep = size;
663         return size;
664 }
665
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)
674 {
675         snd_pcm_softvol_t *svol = pcm->private_data;
676         if (size > *slave_sizep)
677                 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);
682         else
683                 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
684                                            slave_offset, pcm->channels, size);
685         *slave_sizep = size;
686         return size;
687 }
688
689 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
690 {
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");
696         else {
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);
700         }
701         if (pcm->setup) {
702                 snd_output_printf(out, "Its setup is:\n");
703                 snd_pcm_dump_setup(pcm, out);
704         }
705         snd_output_printf(out, "Slave: ");
706         snd_pcm_dump(svol->plug.gen.slave, out);
707 }
708
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)
711 {
712         unsigned int tlv[4];
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)
719                 return 0;
720         return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
721 }
722
723 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
724                         int count)
725 {
726         int err;
727         int i;
728         unsigned int def_val;
729         
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);
733         } else {
734                 err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
735                                                    0, svol->max_val, 0);
736         }
737         if (err < 0)
738                 return err;
739         if (svol->max_val == 1)
740                 def_val = 1;
741         else {
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;
746         }
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);
750 }
751
752 /*
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
757  */
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,
761                                 int resolution)
762 {
763         char tmp_name[32];
764         snd_pcm_info_t info = {0};
765         snd_ctl_elem_info_t cinfo = {0};
766         int err;
767         unsigned int i;
768
769         if (ctl_card < 0) {
770                 err = snd_pcm_info(pcm, &info);
771                 if (err < 0)
772                         return err;
773                 ctl_card = snd_pcm_info_get_card(&info);
774                 if (ctl_card < 0) {
775                         SNDERR("No card defined for softvol control");
776                         return -EINVAL;
777                 }
778         }
779         sprintf(tmp_name, "hw:%d", ctl_card);
780         err = snd_ctl_open(&svol->ctl, tmp_name, 0);
781         if (err < 0) {
782                 SNDERR("Cannot open CTL %s", tmp_name);
783                 return err;
784         }
785
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 */
794         else
795                 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
796                                                                 svol->max_val;
797                 
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);
802                         return err;
803                 }
804                 err = add_user_ctl(svol, &cinfo, cchannels);
805                 if (err < 0) {
806                         SNDERR("Cannot add a control");
807                         return err;
808                 }
809         } else {
810                 if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
811                         /* hardware control exists */
812                         return 1; /* notify */
813
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);
824                         if (err < 0) {
825                                 SNDERR("Control %s mismatch", tmp_name);
826                                 return err;
827                         }
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");
833                                 return err;
834                         }
835                 } else if (svol->max_val > 1) {
836                         /* check TLV availability */
837                         unsigned int tlv[4];
838                         err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
839                                                     sizeof(tlv));
840                         add_tlv_info(svol, &cinfo, tlv, err < 0 ? 0 : sizeof(tlv));
841                 }
842         }
843
844         if (svol->max_val == 1)
845                 return 0;
846
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;
851         else {
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");
856                         return -ENOMEM;
857                 }
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)) /
863                                         svol->max_val;
864                         double v = (pow(10.0, db / 20.0) *
865                                         (double)(1 << VOL_SCALE_SHIFT));
866                         svol->dB_value[i] = (unsigned int)v;
867                 }
868                 if (svol->zero_dB_val)
869                         svol->dB_value[svol->zero_dB_val] = 65535;
870 #else
871                 SNDERR("Cannot handle the given dB range and resolution");
872                 return -EINVAL;
873 #endif
874         }
875         return 0;
876 }
877
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,
894 };
895
896 /**
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
912  *          changed in future.
913  */
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,
917                          int cchannels,
918                          double min_dB, double max_dB, int resolution,
919                          snd_pcm_t *slave, int close_slave)
920 {
921         snd_pcm_t *pcm;
922         snd_pcm_softvol_t *svol;
923         int err;
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)
932                 return -EINVAL;
933         svol = calloc(1, sizeof(*svol));
934         if (! svol)
935                 return -ENOMEM;
936         err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
937                                    min_dB, max_dB, resolution);
938         if (err < 0) {
939                 softvol_free(svol);
940                 return err;
941         }
942         if (err > 0) { /* hardware control - no need for softvol! */
943                 softvol_free(svol);
944                 *pcmp = slave; /* just pass the slave */
945                 if (!slave->name && name)
946                         slave->name = strdup(name);
947                 return 0;
948         }
949
950         /* do softvol */
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;
960
961         err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
962         if (err < 0) {
963                 softvol_free(svol);
964                 return err;
965         }
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;
971         /*
972          * Since the softvol converts on the place, and the format/channels
973          * must be identical between source and destination, we don't need
974          * an extra buffer.
975          */
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);
980         *pcmp = pcm;
981
982         return 0;
983 }
984
985 static int _snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id,
986                                      int *cardp, int *cchannels)
987 {
988         snd_config_iterator_t i, next;
989         int iface = SND_CTL_ELEM_IFACE_MIXER;
990         const char *name = NULL;
991         long index = 0;
992         long device = -1;
993         long subdevice = -1;
994         int err;
995
996         assert(ctl_id && cardp && cchannels);
997
998         *cardp = -1;
999         *cchannels = 2;
1000         snd_config_for_each(i, next, conf) {
1001                 snd_config_t *n = snd_config_iterator_entry(i);
1002                 const char *id;
1003                 if (snd_config_get_id(n, &id) < 0)
1004                         continue;
1005                 if (strcmp(id, "comment") == 0)
1006                         continue;
1007                 if (strcmp(id, "card") == 0) {
1008                         err = snd_config_get_card(n);
1009                         if (err < 0)
1010                                 goto _err;
1011                         *cardp = err;
1012                         continue;
1013                 }
1014                 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
1015                         err = snd_config_get_ctl_iface(n);
1016                         if (err < 0)
1017                                 goto _err;
1018                         iface = err;
1019                         continue;
1020                 }
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);
1024                                 goto _err;
1025                         }
1026                         continue;
1027                 }
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);
1031                                 goto _err;
1032                         }
1033                         continue;
1034                 }
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);
1038                                 goto _err;
1039                         }
1040                         continue;
1041                 }
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);
1045                                 goto _err;
1046                         }
1047                         continue;
1048                 }
1049                 if (strcmp(id, "count") == 0) {
1050                         long v;
1051                         if ((err = snd_config_get_integer(n, &v)) < 0) {
1052                                 SNDERR("field %s is not an integer", id);
1053                                 goto _err;
1054                         }
1055                         if (v < 1 || v > 2) {
1056                                 SNDERR("Invalid count %ld", v);
1057                                 goto _err;
1058                         }
1059                         *cchannels = v;
1060                         continue;
1061                 }
1062                 SNDERR("Unknown field %s", id);
1063                 return -EINVAL;
1064         }
1065         if (name == NULL) {
1066                 SNDERR("Missing control name");
1067                 err = -EINVAL;
1068                 goto _err;
1069         }
1070         if (device < 0)
1071                 device = 0;
1072         if (subdevice < 0)
1073                 subdevice = 0;
1074
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);
1080
1081         return 0;
1082
1083  _err:
1084         return err;
1085 }
1086
1087 /*! \page pcm_plugins
1088
1089 \section pcm_plugins_softvol Plugin: Soft Volume
1090
1091 This plugin applies the software volume attenuation.
1092 The format, rate and channels must match for both of source and destination.
1093
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.
1096
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
1099 any changes.
1100
1101 \code
1102 pcm.name {
1103         type softvol            # Soft Volume conversion PCM
1104         slave STR               # Slave name
1105         # or
1106         slave {                 # Slave definition
1107                 pcm STR         # Slave PCM name
1108                 # or
1109                 pcm { }         # Slave PCM definition
1110                 [format STR]    # Slave format
1111         }
1112         control {
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)
1120         }
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
1125 }
1126 \endcode
1127
1128 \subsection pcm_plugins_softvol_funcref Function reference
1129
1130 <UL>
1131   <LI>snd_pcm_softvol_open()
1132   <LI>_snd_pcm_softvol_open()
1133 </UL>
1134
1135 */
1136
1137 /**
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.
1149  */
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)
1153 {
1154         snd_config_iterator_t i, next;
1155         int err;
1156         snd_pcm_t *spcm;
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;
1165
1166         snd_config_for_each(i, next, conf) {
1167                 snd_config_t *n = snd_config_iterator_entry(i);
1168                 const char *id;
1169                 if (snd_config_get_id(n, &id) < 0)
1170                         continue;
1171                 if (snd_pcm_conf_generic_id(id))
1172                         continue;
1173                 if (strcmp(id, "slave") == 0) {
1174                         slave = n;
1175                         continue;
1176                 }
1177                 if (strcmp(id, "control") == 0) {
1178                         control = n;
1179                         continue;
1180                 }
1181                 if (strcmp(id, "resolution") == 0) {
1182                         long v;
1183                         err = snd_config_get_integer(n, &v);
1184                         if (err < 0) {
1185                                 SNDERR("Invalid resolution value");
1186                                 return err;
1187                         }
1188                         resolution = v;
1189                         continue;
1190                 }
1191                 if (strcmp(id, "min_dB") == 0) {
1192                         err = snd_config_get_ireal(n, &min_dB);
1193                         if (err < 0) {
1194                                 SNDERR("Invalid min_dB value");
1195                                 return err;
1196                         }
1197                         continue;
1198                 }
1199                 if (strcmp(id, "max_dB") == 0) {
1200                         err = snd_config_get_ireal(n, &max_dB);
1201                         if (err < 0) {
1202                                 SNDERR("Invalid max_dB value");
1203                                 return err;
1204                         }
1205                         continue;
1206                 }
1207                 SNDERR("Unknown field %s", id);
1208                 return -EINVAL;
1209         }
1210         if (!slave) {
1211                 SNDERR("slave is not defined");
1212                 return -EINVAL;
1213         }
1214         if (!control) {
1215                 SNDERR("control is not defined");
1216                 return -EINVAL;
1217         }
1218         if (min_dB >= 0) {
1219                 SNDERR("min_dB must be a negative value");
1220                 return -EINVAL;
1221         }
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);
1225                 return -EINVAL;
1226         }
1227         if (resolution <= 1 || resolution > 1024) {
1228                 SNDERR("Invalid resolution value %d", resolution);
1229                 return -EINVAL;
1230         }
1231         if (mode & SND_PCM_NO_SOFTVOL) {
1232                 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1233                 if (err < 0)
1234                         return err;
1235                 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1236                                                mode, conf);
1237                 snd_config_delete(sconf);
1238         } else {
1239                 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1240                                          SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1241                 if (err < 0)
1242                         return err;
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);
1252                         return -EINVAL;
1253                 }
1254                 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1255                 snd_config_delete(sconf);
1256                 if (err < 0)
1257                         return err;
1258                 err = _snd_pcm_parse_control_id(control, &ctl_id, &card, &cchannels);
1259                 if (err < 0) {
1260                         snd_pcm_close(spcm);
1261                         return err;
1262                 }
1263                 err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
1264                                            cchannels, min_dB, max_dB,
1265                                            resolution, spcm, 1);
1266                 if (err < 0)
1267                         snd_pcm_close(spcm);
1268         }
1269         return err;
1270 }
1271 #ifndef DOC_HIDDEN
1272 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1273 #endif