]> git.alsa-project.org Git - alsa-lib.git/blob - src/pcm/pcm_softvol.c
ec8ca22362f3f0283984736dd59ff56f2eebc2d9
[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                 snd_error(PCM, "softvol supports only S16_LE, S16_BE, S24_LE, S24_3LE, "
636                                "S32_LE or S32_BE");
637
638                 return -EINVAL;
639         }
640         svol->sformat = slave->format;
641         return 0;
642 }
643
644 static snd_pcm_uframes_t
645 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
646                             const snd_pcm_channel_area_t *areas,
647                             snd_pcm_uframes_t offset,
648                             snd_pcm_uframes_t size,
649                             const snd_pcm_channel_area_t *slave_areas,
650                             snd_pcm_uframes_t slave_offset,
651                             snd_pcm_uframes_t *slave_sizep)
652 {
653         snd_pcm_softvol_t *svol = pcm->private_data;
654         if (size > *slave_sizep)
655                 size = *slave_sizep;
656         get_current_volume(svol);
657         if (svol->cchannels == 1)
658                 softvol_convert_mono_vol(svol, slave_areas, slave_offset,
659                                          areas, offset, pcm->channels, size);
660         else
661                 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
662                                            areas, offset, pcm->channels, size);
663         *slave_sizep = size;
664         return size;
665 }
666
667 static snd_pcm_uframes_t
668 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
669                            const snd_pcm_channel_area_t *areas,
670                            snd_pcm_uframes_t offset,
671                            snd_pcm_uframes_t size,
672                            const snd_pcm_channel_area_t *slave_areas,
673                            snd_pcm_uframes_t slave_offset,
674                            snd_pcm_uframes_t *slave_sizep)
675 {
676         snd_pcm_softvol_t *svol = pcm->private_data;
677         if (size > *slave_sizep)
678                 size = *slave_sizep;
679         get_current_volume(svol);
680         if (svol->cchannels == 1)
681                 softvol_convert_mono_vol(svol, areas, offset, slave_areas,
682                                          slave_offset, pcm->channels, size);
683         else
684                 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
685                                            slave_offset, pcm->channels, size);
686         *slave_sizep = size;
687         return size;
688 }
689
690 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
691 {
692         snd_pcm_softvol_t *svol = pcm->private_data;
693         snd_output_printf(out, "Soft volume PCM\n");
694         snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
695         if (svol->max_val == 1)
696                 snd_output_printf(out, "boolean\n");
697         else {
698                 snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
699                 snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
700                 snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
701         }
702         if (pcm->setup) {
703                 snd_output_printf(out, "Its setup is:\n");
704                 snd_pcm_dump_setup(pcm, out);
705         }
706         snd_output_printf(out, "Slave: ");
707         snd_pcm_dump(svol->plug.gen.slave, out);
708 }
709
710 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
711                         unsigned int *old_tlv, size_t old_tlv_size)
712 {
713         unsigned int tlv[4];
714         tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
715         tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
716         tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
717         tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
718                 (int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
719         if (sizeof(tlv) <= old_tlv_size && memcmp(tlv, old_tlv, sizeof(tlv)) == 0)
720                 return 0;
721         return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
722 }
723
724 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
725                         int count)
726 {
727         int err;
728         int i;
729         unsigned int def_val;
730
731         if (svol->max_val == 1) {
732                 snd_ctl_elem_info_set_read_write(cinfo, 1, 1);
733                 err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
734         } else {
735                 err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
736                                                    0, svol->max_val, 0);
737         }
738         if (err < 0)
739                 return err;
740         if (svol->max_val == 1)
741                 def_val = 1;
742         else {
743                 add_tlv_info(svol, cinfo, NULL, 0);
744                 /* set zero dB value as default, or max_val if
745                    there is no 0 dB setting */
746                 def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
747         }
748         for (i = 0; i < count; i++)
749                 svol->elem.value.integer.value[i] = def_val;
750         return snd_ctl_elem_write(svol->ctl, &svol->elem);
751 }
752
753 /*
754  * load and set up user-control
755  * returns 0 if the user-control is found or created,
756  * returns 1 if the control is a hw control,
757  * or a negative error code
758  */
759 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
760                                 int ctl_card, snd_ctl_elem_id_t *ctl_id,
761                                 int cchannels, double min_dB, double max_dB,
762                                 int resolution)
763 {
764         char tmp_name[32];
765         snd_pcm_info_t info = {0};
766         snd_ctl_elem_info_t cinfo = {0};
767         int err;
768         unsigned int i;
769
770         if (ctl_card < 0) {
771                 err = snd_pcm_info(pcm, &info);
772                 if (err < 0)
773                         return err;
774                 ctl_card = snd_pcm_info_get_card(&info);
775                 if (ctl_card < 0) {
776                         snd_error(PCM, "No card defined for softvol control");
777                         return -EINVAL;
778                 }
779         }
780         sprintf(tmp_name, "hw:%d", ctl_card);
781         err = snd_ctl_open(&svol->ctl, tmp_name, 0);
782         if (err < 0) {
783                 snd_error(PCM, "Cannot open CTL %s", tmp_name);
784                 return err;
785         }
786
787         svol->elem.id = *ctl_id;
788         svol->max_val = resolution - 1;
789         svol->min_dB = min_dB;
790         svol->max_dB = max_dB;
791         if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
792                 svol->zero_dB_val = svol->max_val;
793         else if (svol->max_dB < 0)
794                 svol->zero_dB_val = 0; /* there is no 0 dB setting */
795         else
796                 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
797                                                                 svol->max_val;
798
799         snd_ctl_elem_info_set_id(&cinfo, ctl_id);
800         if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
801                 if (err != -ENOENT) {
802                         snd_error(PCM, "Cannot get info for CTL %s", tmp_name);
803                         return err;
804                 }
805                 err = add_user_ctl(svol, &cinfo, cchannels);
806                 if (err < 0) {
807                         snd_error(PCM, "Cannot add a control");
808                         return err;
809                 }
810         } else {
811                 if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
812                         /* hardware control exists */
813                         return 1; /* notify */
814
815                 } else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
816                             cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
817                            cinfo.count != (unsigned int)cchannels ||
818                            cinfo.value.integer.min != 0 ||
819                            cinfo.value.integer.max != svol->max_val ||
820                            (svol->max_val > 1 &&
821                             (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
822                            (svol->max_val < 2 &&
823                             (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) != 0)) {
824                         err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
825                         if (err < 0) {
826                                 snd_error(PCM, "Control %s mismatch", tmp_name);
827                                 return err;
828                         }
829                         /* clear cinfo including numid */
830                         snd_ctl_elem_info_clear(&cinfo);
831                         snd_ctl_elem_info_set_id(&cinfo, ctl_id);
832                         if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
833                                 snd_error(PCM, "Cannot add a control");
834                                 return err;
835                         }
836                 } else if (svol->max_val > 1) {
837                         /* check TLV availability */
838                         unsigned int tlv[4];
839                         err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
840                                                     sizeof(tlv));
841                         add_tlv_info(svol, &cinfo, tlv, err < 0 ? 0 : sizeof(tlv));
842                 }
843         }
844
845         if (svol->max_val == 1)
846                 return 0;
847
848         /* set up dB table */
849         if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
850                                                 resolution == PRESET_RESOLUTION)
851                 svol->dB_value = (unsigned int*)preset_dB_value;
852         else {
853 #ifndef HAVE_SOFT_FLOAT
854                 svol->dB_value = calloc(resolution, sizeof(unsigned int));
855                 if (! svol->dB_value) {
856                         snd_error(PCM, "cannot allocate dB table");
857                         return -ENOMEM;
858                 }
859                 svol->min_dB = min_dB;
860                 svol->max_dB = max_dB;
861                 for (i = 0; i <= svol->max_val; i++) {
862                         double db = svol->min_dB +
863                                 (i * (svol->max_dB - svol->min_dB)) /
864                                         svol->max_val;
865                         double v = (pow(10.0, db / 20.0) *
866                                         (double)(1 << VOL_SCALE_SHIFT));
867                         svol->dB_value[i] = (unsigned int)v;
868                 }
869                 if (svol->zero_dB_val)
870                         svol->dB_value[svol->zero_dB_val] = 65535;
871 #else
872                 snd_error(PCM, "Cannot handle the given dB range and resolution");
873                 return -EINVAL;
874 #endif
875         }
876         return 0;
877 }
878
879 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
880         .close = snd_pcm_softvol_close,
881         .info = snd_pcm_generic_info,
882         .hw_refine = snd_pcm_softvol_hw_refine,
883         .hw_params = snd_pcm_softvol_hw_params,
884         .hw_free = snd_pcm_generic_hw_free,
885         .sw_params = snd_pcm_generic_sw_params,
886         .channel_info = snd_pcm_generic_channel_info,
887         .dump = snd_pcm_softvol_dump,
888         .nonblock = snd_pcm_generic_nonblock,
889         .async = snd_pcm_generic_async,
890         .mmap = snd_pcm_generic_mmap,
891         .munmap = snd_pcm_generic_munmap,
892         .query_chmaps = snd_pcm_generic_query_chmaps,
893         .get_chmap = snd_pcm_generic_get_chmap,
894         .set_chmap = snd_pcm_generic_set_chmap,
895 };
896
897 /**
898  * \brief Creates a new SoftVolume PCM
899  * \param pcmp Returns created PCM handle
900  * \param name Name of PCM
901  * \param sformat Slave format
902  * \param ctl_card card index of the control
903  * \param ctl_id The control element
904  * \param cchannels PCM channels
905  * \param min_dB minimal dB value
906  * \param max_dB maximal dB value
907  * \param resolution resolution of control
908  * \param slave Slave PCM handle
909  * \param close_slave When set, the slave PCM handle is closed with copy PCM
910  * \retval zero on success otherwise a negative error code
911  * \warning Using of this function might be dangerous in the sense
912  *          of compatibility reasons. The prototype might be freely
913  *          changed in future.
914  */
915 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
916                          snd_pcm_format_t sformat,
917                          int ctl_card, snd_ctl_elem_id_t *ctl_id,
918                          int cchannels,
919                          double min_dB, double max_dB, int resolution,
920                          snd_pcm_t *slave, int close_slave)
921 {
922         snd_pcm_t *pcm;
923         snd_pcm_softvol_t *svol;
924         int err;
925         assert(pcmp && slave);
926         if (sformat != SND_PCM_FORMAT_UNKNOWN &&
927             sformat != SND_PCM_FORMAT_S16_LE &&
928             sformat != SND_PCM_FORMAT_S16_BE &&
929             sformat != SND_PCM_FORMAT_S24_3LE &&
930             sformat != SND_PCM_FORMAT_S24_LE &&
931             sformat != SND_PCM_FORMAT_S32_LE &&
932             sformat != SND_PCM_FORMAT_S32_BE)
933                 return -EINVAL;
934         svol = calloc(1, sizeof(*svol));
935         if (! svol)
936                 return -ENOMEM;
937         err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
938                                    min_dB, max_dB, resolution);
939         if (err < 0) {
940                 softvol_free(svol);
941                 return err;
942         }
943         if (err > 0) { /* hardware control - no need for softvol! */
944                 softvol_free(svol);
945                 *pcmp = slave; /* just pass the slave */
946                 if (!slave->name && name)
947                         slave->name = strdup(name);
948                 return 0;
949         }
950
951         /* do softvol */
952         snd_pcm_plugin_init(&svol->plug);
953         svol->sformat = sformat;
954         svol->cchannels = cchannels;
955         svol->plug.read = snd_pcm_softvol_read_areas;
956         svol->plug.write = snd_pcm_softvol_write_areas;
957         svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
958         svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
959         svol->plug.gen.slave = slave;
960         svol->plug.gen.close_slave = close_slave;
961
962         err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
963         if (err < 0) {
964                 softvol_free(svol);
965                 return err;
966         }
967         pcm->ops = &snd_pcm_softvol_ops;
968         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
969         pcm->private_data = svol;
970         pcm->poll_fd = slave->poll_fd;
971         pcm->poll_events = slave->poll_events;
972         /*
973          * Since the softvol converts on the place, and the format/channels
974          * must be identical between source and destination, we don't need
975          * an extra buffer.
976          */
977         pcm->mmap_shadow = 1;
978         pcm->tstamp_type = slave->tstamp_type;
979         snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
980         snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
981         *pcmp = pcm;
982
983         return 0;
984 }
985
986 static int _snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id,
987                                      int *cardp, int *cchannels)
988 {
989         snd_config_iterator_t i, next;
990         int iface = SND_CTL_ELEM_IFACE_MIXER;
991         const char *name = NULL;
992         long index = 0;
993         long device = -1;
994         long subdevice = -1;
995         int err;
996
997         assert(ctl_id && cardp && cchannels);
998
999         *cardp = -1;
1000         *cchannels = 2;
1001         snd_config_for_each(i, next, conf) {
1002                 snd_config_t *n = snd_config_iterator_entry(i);
1003                 const char *id;
1004                 if (snd_config_get_id(n, &id) < 0)
1005                         continue;
1006                 if (strcmp(id, "comment") == 0)
1007                         continue;
1008                 if (strcmp(id, "card") == 0) {
1009                         err = snd_config_get_card(n);
1010                         if (err < 0)
1011                                 goto _err;
1012                         *cardp = err;
1013                         continue;
1014                 }
1015                 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
1016                         err = snd_config_get_ctl_iface(n);
1017                         if (err < 0)
1018                                 goto _err;
1019                         iface = err;
1020                         continue;
1021                 }
1022                 if (strcmp(id, "name") == 0) {
1023                         if ((err = snd_config_get_string(n, &name)) < 0) {
1024                                 snd_error(PCM, "field %s is not a string", id);
1025                                 goto _err;
1026                         }
1027                         continue;
1028                 }
1029                 if (strcmp(id, "index") == 0) {
1030                         if ((err = snd_config_get_integer(n, &index)) < 0) {
1031                                 snd_error(PCM, "field %s is not an integer", id);
1032                                 goto _err;
1033                         }
1034                         continue;
1035                 }
1036                 if (strcmp(id, "device") == 0) {
1037                         if ((err = snd_config_get_integer(n, &device)) < 0) {
1038                                 snd_error(PCM, "field %s is not an integer", id);
1039                                 goto _err;
1040                         }
1041                         continue;
1042                 }
1043                 if (strcmp(id, "subdevice") == 0) {
1044                         if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
1045                                 snd_error(PCM, "field %s is not an integer", id);
1046                                 goto _err;
1047                         }
1048                         continue;
1049                 }
1050                 if (strcmp(id, "count") == 0) {
1051                         long v;
1052                         if ((err = snd_config_get_integer(n, &v)) < 0) {
1053                                 snd_error(PCM, "field %s is not an integer", id);
1054                                 goto _err;
1055                         }
1056                         if (v < 1 || v > 2) {
1057                                 snd_error(PCM, "Invalid count %ld", v);
1058                                 goto _err;
1059                         }
1060                         *cchannels = v;
1061                         continue;
1062                 }
1063                 snd_error(PCM, "Unknown field %s", id);
1064                 return -EINVAL;
1065         }
1066         if (name == NULL) {
1067                 snd_error(PCM, "Missing control name");
1068                 err = -EINVAL;
1069                 goto _err;
1070         }
1071         if (device < 0)
1072                 device = 0;
1073         if (subdevice < 0)
1074                 subdevice = 0;
1075
1076         snd_ctl_elem_id_set_interface(ctl_id, iface);
1077         snd_ctl_elem_id_set_name(ctl_id, name);
1078         snd_ctl_elem_id_set_index(ctl_id, index);
1079         snd_ctl_elem_id_set_device(ctl_id, device);
1080         snd_ctl_elem_id_set_subdevice(ctl_id, subdevice);
1081
1082         return 0;
1083
1084  _err:
1085         return err;
1086 }
1087
1088 /*! \page pcm_plugins
1089
1090 \section pcm_plugins_softvol Plugin: Soft Volume
1091
1092 This plugin applies the software volume attenuation.
1093 The format, rate and channels must match for both of source and destination.
1094
1095 When the control is stereo (count=2), the channels are assumed to be either
1096 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
1097
1098 If the control already exists and it's a system control (i.e. no
1099 user-defined control), the plugin simply passes its slave without
1100 any changes.
1101
1102 \code
1103 pcm.name {
1104         type softvol            # Soft Volume conversion PCM
1105         slave STR               # Slave name
1106         # or
1107         slave {                 # Slave definition
1108                 pcm STR         # Slave PCM name
1109                 # or
1110                 pcm { }         # Slave PCM definition
1111                 [format STR]    # Slave format
1112         }
1113         control {
1114                 name STR        # control element id string
1115                 [card STR]      # control card index
1116                 [iface STR]     # interface of the element
1117                 [index INT]     # index of the element
1118                 [device INT]    # device number of the element
1119                 [subdevice INT] # subdevice number of the element
1120                 [count INT]     # control channels 1 or 2 (default: 2)
1121         }
1122         [min_dB REAL]           # minimal dB value (default: -51.0)
1123         [max_dB REAL]           # maximal dB value (default:   0.0)
1124         [resolution INT]        # resolution (default: 256)
1125                                 # resolution = 2 means a mute switch
1126 }
1127 \endcode
1128
1129 \subsection pcm_plugins_softvol_funcref Function reference
1130
1131 <UL>
1132   <LI>snd_pcm_softvol_open()
1133   <LI>_snd_pcm_softvol_open()
1134 </UL>
1135
1136 */
1137
1138 /**
1139  * \brief Creates a new Soft Volume PCM
1140  * \param pcmp Returns created PCM handle
1141  * \param name Name of PCM
1142  * \param root Root configuration node
1143  * \param conf Configuration node with Soft Volume PCM description
1144  * \param stream Stream type
1145  * \param mode Stream mode
1146  * \retval zero on success otherwise a negative error code
1147  * \warning Using of this function might be dangerous in the sense
1148  *          of compatibility reasons. The prototype might be freely
1149  *          changed in future.
1150  */
1151 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
1152                           snd_config_t *root, snd_config_t *conf,
1153                           snd_pcm_stream_t stream, int mode)
1154 {
1155         snd_config_iterator_t i, next;
1156         int err;
1157         snd_pcm_t *spcm;
1158         snd_config_t *slave = NULL, *sconf;
1159         snd_config_t *control = NULL;
1160         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1161         snd_ctl_elem_id_t ctl_id = {0};
1162         int resolution = PRESET_RESOLUTION;
1163         double min_dB = PRESET_MIN_DB;
1164         double max_dB = ZERO_DB;
1165         int card = -1, cchannels = 2;
1166
1167         snd_config_for_each(i, next, conf) {
1168                 snd_config_t *n = snd_config_iterator_entry(i);
1169                 const char *id;
1170                 if (snd_config_get_id(n, &id) < 0)
1171                         continue;
1172                 if (snd_pcm_conf_generic_id(id))
1173                         continue;
1174                 if (strcmp(id, "slave") == 0) {
1175                         slave = n;
1176                         continue;
1177                 }
1178                 if (strcmp(id, "control") == 0) {
1179                         control = n;
1180                         continue;
1181                 }
1182                 if (strcmp(id, "resolution") == 0) {
1183                         long v;
1184                         err = snd_config_get_integer(n, &v);
1185                         if (err < 0) {
1186                                 snd_error(PCM, "Invalid resolution value");
1187                                 return err;
1188                         }
1189                         resolution = v;
1190                         continue;
1191                 }
1192                 if (strcmp(id, "min_dB") == 0) {
1193                         err = snd_config_get_ireal(n, &min_dB);
1194                         if (err < 0) {
1195                                 snd_error(PCM, "Invalid min_dB value");
1196                                 return err;
1197                         }
1198                         continue;
1199                 }
1200                 if (strcmp(id, "max_dB") == 0) {
1201                         err = snd_config_get_ireal(n, &max_dB);
1202                         if (err < 0) {
1203                                 snd_error(PCM, "Invalid max_dB value");
1204                                 return err;
1205                         }
1206                         continue;
1207                 }
1208                 snd_error(PCM, "Unknown field %s", id);
1209                 return -EINVAL;
1210         }
1211         if (!slave) {
1212                 snd_error(PCM, "slave is not defined");
1213                 return -EINVAL;
1214         }
1215         if (!control) {
1216                 snd_error(PCM, "control is not defined");
1217                 return -EINVAL;
1218         }
1219         if (min_dB >= 0) {
1220                 snd_error(PCM, "min_dB must be a negative value");
1221                 return -EINVAL;
1222         }
1223         if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1224                 snd_error(PCM, "max_dB must be larger than min_dB and less than %d dB",
1225                                MAX_DB_UPPER_LIMIT);
1226
1227                 return -EINVAL;
1228         }
1229         if (resolution <= 1 || resolution > 1024) {
1230                 snd_error(PCM, "Invalid resolution value %d", resolution);
1231                 return -EINVAL;
1232         }
1233         if (mode & SND_PCM_NO_SOFTVOL) {
1234                 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1235                 if (err < 0)
1236                         return err;
1237                 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1238                                                mode, conf);
1239                 snd_config_delete(sconf);
1240         } else {
1241                 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1242                                          SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1243                 if (err < 0)
1244                         return err;
1245                 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1246                     sformat != SND_PCM_FORMAT_S16_LE &&
1247                     sformat != SND_PCM_FORMAT_S16_BE &&
1248                     sformat != SND_PCM_FORMAT_S24_3LE &&
1249                     sformat != SND_PCM_FORMAT_S24_LE &&
1250                     sformat != SND_PCM_FORMAT_S32_LE &&
1251                     sformat != SND_PCM_FORMAT_S32_BE) {
1252                         snd_error(PCM, "only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
1253                         snd_config_delete(sconf);
1254                         return -EINVAL;
1255                 }
1256                 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1257                 snd_config_delete(sconf);
1258                 if (err < 0)
1259                         return err;
1260                 err = _snd_pcm_parse_control_id(control, &ctl_id, &card, &cchannels);
1261                 if (err < 0) {
1262                         snd_pcm_close(spcm);
1263                         return err;
1264                 }
1265                 err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
1266                                            cchannels, min_dB, max_dB,
1267                                            resolution, spcm, 1);
1268                 if (err < 0)
1269                         snd_pcm_close(spcm);
1270         }
1271         return err;
1272 }
1273 #ifndef DOC_HIDDEN
1274 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1275 #endif