Initial Import
[profile/ivi/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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28
29 #include <byteswap.h>
30 #include <math.h>
31 #include "pcm_local.h"
32 #include "pcm_plugin.h"
33
34 #ifndef PIC
35 /* entry for static linking */
36 const char *_snd_module_pcm_softvol = "";
37 #endif
38
39 #ifndef DOC_HIDDEN
40
41 typedef struct {
42         /* This field need to be the first */
43         snd_pcm_plugin_t plug;
44         snd_pcm_format_t sformat;
45         unsigned int cchannels;
46         snd_ctl_t *ctl;
47         snd_ctl_elem_value_t elem;
48         unsigned int cur_vol[2];
49         unsigned int max_val;     /* max index */
50         unsigned int zero_dB_val; /* index at 0 dB */
51         double min_dB;
52         double max_dB;
53         unsigned int *dB_value;
54 } snd_pcm_softvol_t;
55
56 #define VOL_SCALE_SHIFT         16
57 #define VOL_SCALE_MASK          ((1 << VOL_SCALE_SHIFT) - 1)
58
59 #define PRESET_RESOLUTION       256
60 #define PRESET_MIN_DB           -51.0
61 #define ZERO_DB                  0.0
62 #define MAX_DB_UPPER_LIMIT      50
63
64 static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
65         0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
66         0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
67         0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
68         0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
69         0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
70         0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
71         0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
72         0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
73         0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
74         0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
75         0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
76         0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
77         0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
78         0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
79         0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
80         0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
81         0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
82         0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
83         0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
84         0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
85         0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
86         0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
87         0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
88         0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
89         0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
90         0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
91         0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
92         0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
93         0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
94         0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
95         0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
96         0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
97 };
98
99 /* (32bit x 16bit) >> 16 */
100 typedef union {
101         int i;
102         short s[2];
103 } val_t;
104 static inline int MULTI_DIV_32x16(int a, unsigned short b)
105 {
106         val_t v, x, y;
107         v.i = a;
108         y.i = 0;
109 #if __BYTE_ORDER == __LITTLE_ENDIAN
110         x.i = (unsigned short)v.s[0];
111         x.i *= b;
112         y.s[0] = x.s[1];
113         y.i += (int)v.s[1] * b;
114 #else
115         x.i = (unsigned int)v.s[1] * b;
116         y.s[1] = x.s[0];
117         y.i += (int)v.s[0] * b;
118 #endif
119         return y.i;
120 }
121
122 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
123 {
124         unsigned int gain = (b >> VOL_SCALE_SHIFT);
125         int fraction;
126         a = swap ? (int)bswap_32(a) : a;
127         fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
128         if (gain) {
129                 long long amp = (long long)a * gain + fraction;
130                 if (amp > (int)0x7fffffff)
131                         amp = (int)0x7fffffff;
132                 else if (amp < (int)0x80000000)
133                         amp = (int)0x80000000;
134                 return swap ? (int)bswap_32((int)amp) : (int)amp;
135         }
136         return swap ? (int)bswap_32(fraction) : fraction;
137 }
138
139 /* always little endian */
140 static inline int MULTI_DIV_24(int a, unsigned int b)
141 {
142         unsigned int gain = b >> VOL_SCALE_SHIFT;
143         int fraction;
144         fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
145         if (gain) {
146                 long long amp = (long long)a * gain + fraction;
147                 if (amp > (int)0x7fffff)
148                         amp = (int)0x7fffff;
149                 else if (amp < (int)0x800000)
150                         amp = (int)0x800000;
151                 return (int)amp;
152         }
153         return fraction;
154 }
155
156 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
157 {
158         unsigned int gain = b >> VOL_SCALE_SHIFT;
159         int fraction;
160         a = swap ? (short)bswap_16(a) : a;
161         fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
162         if (gain) {
163                 int amp = a * gain + fraction;
164                 if (abs(amp) > 0x7fff)
165                         amp = (a<0) ? (short)0x8000 : (short)0x7fff;
166                 return swap ? (short)bswap_16((short)amp) : (short)amp;
167         }
168         return swap ? (short)bswap_16((short)fraction) : (short)fraction;
169 }
170
171 #endif /* DOC_HIDDEN */
172
173 /*
174  * apply volumue attenuation
175  *
176  * TODO: use SIMD operations
177  */
178
179 #ifndef DOC_HIDDEN
180 #define CONVERT_AREA(TYPE, swap) do {   \
181         unsigned int ch, fr; \
182         TYPE *src, *dst; \
183         for (ch = 0; ch < channels; ch++) { \
184                 src_area = &src_areas[ch]; \
185                 dst_area = &dst_areas[ch]; \
186                 src = snd_pcm_channel_area_addr(src_area, src_offset); \
187                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
188                 src_step = snd_pcm_channel_area_step(src_area) / sizeof(TYPE); \
189                 dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
190                 GET_VOL_SCALE; \
191                 fr = frames; \
192                 if (! vol_scale) { \
193                         while (fr--) { \
194                                 *dst = 0; \
195                                 dst += dst_step; \
196                         } \
197                 } else if (vol_scale == 0xffff) { \
198                         while (fr--) { \
199                                 *dst = *src; \
200                                 src += src_step; \
201                                 dst += dst_step; \
202                         } \
203                 } else { \
204                         while (fr--) { \
205                                 *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
206                                 src += src_step; \
207                                 dst += dst_step; \
208                         } \
209                 } \
210         } \
211 } while (0)
212
213 #define CONVERT_AREA_S24_3LE() do {                                     \
214         unsigned int ch, fr;                                            \
215         unsigned char *src, *dst;                                       \
216         int tmp;                                                        \
217         for (ch = 0; ch < channels; ch++) {                             \
218                 src_area = &src_areas[ch];                              \
219                 dst_area = &dst_areas[ch];                              \
220                 src = snd_pcm_channel_area_addr(src_area, src_offset);  \
221                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);  \
222                 src_step = snd_pcm_channel_area_step(src_area);         \
223                 dst_step = snd_pcm_channel_area_step(dst_area);         \
224                 GET_VOL_SCALE;                                          \
225                 fr = frames;                                            \
226                 if (! vol_scale) {                                      \
227                         while (fr--) {                                  \
228                                 dst[0] = dst[1] = dst[2] = 0;           \
229                                 dst += dst_step;                        \
230                         }                                               \
231                 } else if (vol_scale == 0xffff) {                       \
232                         while (fr--) {                                  \
233                                 dst[0] = src[0];                        \
234                                 dst[1] = src[1];                        \
235                                 dst[2] = src[2];                        \
236                                 src += dst_step;                        \
237                                 dst += src_step;                        \
238                         }                                               \
239                 } else {                                                \
240                         while (fr--) {                                  \
241                                 tmp = src[0] |                          \
242                                       (src[1] << 8) |                   \
243                                       (((signed char *) src)[2] << 16); \
244                                 tmp = MULTI_DIV_24(tmp, vol_scale);     \
245                                 dst[0] = tmp;                           \
246                                 dst[1] = tmp >> 8;                      \
247                                 dst[2] = tmp >> 16;                     \
248                                 src += dst_step;                        \
249                                 dst += src_step;                        \
250                         }                                               \
251                 }                                                       \
252         }                                                               \
253 } while (0)
254                 
255 #define GET_VOL_SCALE \
256         switch (ch) { \
257         case 0: \
258         case 2: \
259                 vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
260                 break; \
261         case 4: \
262         case 5: \
263                 vol_scale = vol_c; \
264                 break; \
265         default: \
266                 vol_scale = vol[ch & 1]; \
267                 break; \
268         }
269
270 #endif /* DOC_HIDDEN */
271
272 /* 2-channel stereo control */
273 static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
274                                        const snd_pcm_channel_area_t *dst_areas,
275                                        snd_pcm_uframes_t dst_offset,
276                                        const snd_pcm_channel_area_t *src_areas,
277                                        snd_pcm_uframes_t src_offset,
278                                        unsigned int channels,
279                                        snd_pcm_uframes_t frames)
280 {
281         const snd_pcm_channel_area_t *dst_area, *src_area;
282         unsigned int src_step, dst_step;
283         unsigned int vol_scale, vol[2], vol_c;
284
285         if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
286                 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
287                                       svol->sformat);
288                 return;
289         } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
290                    svol->cur_vol[1] == svol->zero_dB_val) {
291                 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
292                                    channels, frames, svol->sformat);
293                 return;
294         }
295
296         if (svol->max_val == 1) {
297                 vol[0] = svol->cur_vol[0] ? 0xffff : 0;
298                 vol[1] = svol->cur_vol[1] ? 0xffff : 0;
299                 vol_c = vol[0] | vol[1];
300         } else {
301                 vol[0] = svol->dB_value[svol->cur_vol[0]];
302                 vol[1] = svol->dB_value[svol->cur_vol[1]];
303                 vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
304         }
305         switch (svol->sformat) {
306         case SND_PCM_FORMAT_S16_LE:
307         case SND_PCM_FORMAT_S16_BE:
308                 /* 16bit samples */
309                 CONVERT_AREA(short, 
310                              !snd_pcm_format_cpu_endian(svol->sformat));
311                 break;
312         case SND_PCM_FORMAT_S32_LE:
313         case SND_PCM_FORMAT_S32_BE:
314                 /* 32bit samples */
315                 CONVERT_AREA(int,
316                              !snd_pcm_format_cpu_endian(svol->sformat));
317                 break;
318         case SND_PCM_FORMAT_S24_3LE:
319                 CONVERT_AREA_S24_3LE();
320                 break;
321         default:
322                 break;
323         }
324 }
325
326 #undef GET_VOL_SCALE
327 #define GET_VOL_SCALE
328
329 /* mono control */
330 static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
331                                      const snd_pcm_channel_area_t *dst_areas,
332                                      snd_pcm_uframes_t dst_offset,
333                                      const snd_pcm_channel_area_t *src_areas,
334                                      snd_pcm_uframes_t src_offset,
335                                      unsigned int channels,
336                                      snd_pcm_uframes_t frames)
337 {
338         const snd_pcm_channel_area_t *dst_area, *src_area;
339         unsigned int src_step, dst_step;
340         unsigned int vol_scale;
341
342         if (svol->cur_vol[0] == 0) {
343                 snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
344                                       svol->sformat);
345                 return;
346         } else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
347                 snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
348                                    channels, frames, svol->sformat);
349                 return;
350         }
351
352         if (svol->max_val == 1)
353                 vol_scale = svol->cur_vol[0] ? 0xffff : 0;
354         else
355                 vol_scale = svol->dB_value[svol->cur_vol[0]];
356         switch (svol->sformat) {
357         case SND_PCM_FORMAT_S16_LE:
358         case SND_PCM_FORMAT_S16_BE:
359                 /* 16bit samples */
360                 CONVERT_AREA(short, 
361                              !snd_pcm_format_cpu_endian(svol->sformat));
362                 break;
363         case SND_PCM_FORMAT_S32_LE:
364         case SND_PCM_FORMAT_S32_BE:
365                 /* 32bit samples */
366                 CONVERT_AREA(int,
367                              !snd_pcm_format_cpu_endian(svol->sformat));
368                 break;
369         case SND_PCM_FORMAT_S24_3LE:
370                 CONVERT_AREA_S24_3LE();
371                 break;
372         default:
373                 break;
374         }
375 }
376
377 /*
378  * get the current volume value from driver
379  *
380  * TODO: mmap support?
381  */
382 static void get_current_volume(snd_pcm_softvol_t *svol)
383 {
384         unsigned int val;
385         unsigned int i;
386
387         if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
388                 return;
389         for (i = 0; i < svol->cchannels; i++) {
390                 val = svol->elem.value.integer.value[i];
391                 if (val > svol->max_val)
392                         val = svol->max_val;
393                 svol->cur_vol[i] = val;
394         }
395 }
396
397 static void softvol_free(snd_pcm_softvol_t *svol)
398 {
399         if (svol->plug.gen.close_slave)
400                 snd_pcm_close(svol->plug.gen.slave);
401         if (svol->ctl)
402                 snd_ctl_close(svol->ctl);
403         if (svol->dB_value && svol->dB_value != preset_dB_value)
404                 free(svol->dB_value);
405         free(svol);
406 }
407
408 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
409 {
410         snd_pcm_softvol_t *svol = pcm->private_data;
411         softvol_free(svol);
412         return 0;
413 }
414
415 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
416                                               snd_pcm_hw_params_t *params)
417 {
418         int err;
419         snd_pcm_softvol_t *svol = pcm->private_data;
420         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
421         snd_pcm_format_mask_t format_mask = {
422                 {
423                         (1ULL << SND_PCM_FORMAT_S16_LE) |
424                         (1ULL << SND_PCM_FORMAT_S16_BE) |
425                         (1ULL << SND_PCM_FORMAT_S32_LE) |
426                         (1ULL << SND_PCM_FORMAT_S32_BE),
427                         (1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
428                 }
429         };
430         if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
431                 snd_pcm_format_mask_none(&format_mask);
432                 snd_pcm_format_mask_set(&format_mask, svol->sformat);
433         }
434         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
435                                          &access_mask);
436         if (err < 0)
437                 return err;
438         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
439                                          &format_mask);
440         if (err < 0)
441                 return err;
442         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
443         if (err < 0)
444                 return err;
445         err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
446         if (err < 0)
447                 return err;
448         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
449         return 0;
450 }
451
452 static int snd_pcm_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
453 {
454         snd_pcm_softvol_t *svol = pcm->private_data;
455         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
456         _snd_pcm_hw_params_any(sparams);
457         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
458                                    &saccess_mask);
459         if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
460                 _snd_pcm_hw_params_set_format(sparams, svol->sformat);
461                 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
462         }
463         return 0;
464 }
465
466 /*
467  * refine the access mask
468  */
469 static int check_access_mask(snd_pcm_hw_params_t *src,
470                              snd_pcm_hw_params_t *dst)
471 {
472         const snd_pcm_access_mask_t *mask;
473         snd_pcm_access_mask_t smask;
474
475         mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
476         snd_mask_none(&smask);
477         if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
478             snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
479                 snd_pcm_access_mask_set(&smask,
480                                         SND_PCM_ACCESS_RW_INTERLEAVED);
481                 snd_pcm_access_mask_set(&smask,
482                                         SND_PCM_ACCESS_MMAP_INTERLEAVED);
483         }
484         if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
485             snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))  {
486                 snd_pcm_access_mask_set(&smask,
487                                         SND_PCM_ACCESS_RW_NONINTERLEAVED);
488                 snd_pcm_access_mask_set(&smask,
489                                         SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
490         }
491         if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
492                 snd_pcm_access_mask_set(&smask,
493                                         SND_PCM_ACCESS_MMAP_COMPLEX);
494
495         return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask);
496 }
497
498 static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
499                                              snd_pcm_hw_params_t *params,
500                                              snd_pcm_hw_params_t *sparams)
501 {
502         snd_pcm_softvol_t *svol = pcm->private_data;
503         int err;
504         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
505                               SND_PCM_HW_PARBIT_RATE |
506                               SND_PCM_HW_PARBIT_PERIODS |
507                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
508                               SND_PCM_HW_PARBIT_PERIOD_TIME |
509                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
510                               SND_PCM_HW_PARBIT_BUFFER_TIME |
511                               SND_PCM_HW_PARBIT_TICK_TIME);
512         if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
513                 links |= (SND_PCM_HW_PARBIT_FORMAT | 
514                           SND_PCM_HW_PARBIT_SUBFORMAT |
515                           SND_PCM_HW_PARBIT_SAMPLE_BITS);
516         err = _snd_pcm_hw_params_refine(sparams, links, params);
517         if (err < 0)
518                 return err;
519
520         err = check_access_mask(params, sparams);
521         if (err < 0)
522                 return err;
523
524         return 0;
525 }
526         
527 static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
528                                              snd_pcm_hw_params_t *params,
529                                             snd_pcm_hw_params_t *sparams)
530 {
531         snd_pcm_softvol_t *svol = pcm->private_data;
532         int err;
533         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
534                               SND_PCM_HW_PARBIT_RATE |
535                               SND_PCM_HW_PARBIT_PERIODS |
536                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
537                               SND_PCM_HW_PARBIT_PERIOD_TIME |
538                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
539                               SND_PCM_HW_PARBIT_BUFFER_TIME |
540                               SND_PCM_HW_PARBIT_TICK_TIME);
541         if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
542                 links |= (SND_PCM_HW_PARBIT_FORMAT | 
543                           SND_PCM_HW_PARBIT_SUBFORMAT |
544                           SND_PCM_HW_PARBIT_SAMPLE_BITS);
545         err = _snd_pcm_hw_params_refine(params, links, sparams);
546         if (err < 0)
547                 return err;
548
549         err = check_access_mask(sparams, params);
550         if (err < 0)
551                 return err;
552
553         return 0;
554 }
555
556 static int snd_pcm_softvol_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
557 {
558         return snd_pcm_hw_refine_slave(pcm, params,
559                                        snd_pcm_softvol_hw_refine_cprepare,
560                                        snd_pcm_softvol_hw_refine_cchange,
561                                        snd_pcm_softvol_hw_refine_sprepare,
562                                        snd_pcm_softvol_hw_refine_schange,
563                                        snd_pcm_generic_hw_refine);
564 }
565
566 static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
567 {
568         snd_pcm_softvol_t *svol = pcm->private_data;
569         snd_pcm_t *slave = svol->plug.gen.slave;
570         int err = snd_pcm_hw_params_slave(pcm, params,
571                                           snd_pcm_softvol_hw_refine_cchange,
572                                           snd_pcm_softvol_hw_refine_sprepare,
573                                           snd_pcm_softvol_hw_refine_schange,
574                                           snd_pcm_generic_hw_params);
575         if (err < 0)
576                 return err;
577         if (slave->format != SND_PCM_FORMAT_S16_LE &&
578             slave->format != SND_PCM_FORMAT_S16_BE &&
579             slave->format != SND_PCM_FORMAT_S24_3LE && 
580             slave->format != SND_PCM_FORMAT_S32_LE &&
581             slave->format != SND_PCM_FORMAT_S32_BE) {
582                 SNDERR("softvol supports only S16_LE, S16_BE, S24_3LE, S32_LE "
583                        " or S32_BE");
584                 return -EINVAL;
585         }
586         svol->sformat = slave->format;
587         return 0;
588 }
589
590 static snd_pcm_uframes_t
591 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
592                             const snd_pcm_channel_area_t *areas,
593                             snd_pcm_uframes_t offset,
594                             snd_pcm_uframes_t size,
595                             const snd_pcm_channel_area_t *slave_areas,
596                             snd_pcm_uframes_t slave_offset,
597                             snd_pcm_uframes_t *slave_sizep)
598 {
599         snd_pcm_softvol_t *svol = pcm->private_data;
600         if (size > *slave_sizep)
601                 size = *slave_sizep;
602         get_current_volume(svol);
603         if (svol->cchannels == 1)
604                 softvol_convert_mono_vol(svol, slave_areas, slave_offset,
605                                          areas, offset, pcm->channels, size);
606         else
607                 softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
608                                            areas, offset, pcm->channels, size);
609         *slave_sizep = size;
610         return size;
611 }
612
613 static snd_pcm_uframes_t
614 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
615                            const snd_pcm_channel_area_t *areas,
616                            snd_pcm_uframes_t offset,
617                            snd_pcm_uframes_t size,
618                            const snd_pcm_channel_area_t *slave_areas,
619                            snd_pcm_uframes_t slave_offset,
620                            snd_pcm_uframes_t *slave_sizep)
621 {
622         snd_pcm_softvol_t *svol = pcm->private_data;
623         if (size > *slave_sizep)
624                 size = *slave_sizep;
625         get_current_volume(svol);
626         if (svol->cchannels == 1)
627                 softvol_convert_mono_vol(svol, areas, offset, slave_areas,
628                                          slave_offset, pcm->channels, size);
629         else
630                 softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
631                                            slave_offset, pcm->channels, size);
632         *slave_sizep = size;
633         return size;
634 }
635
636 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
637 {
638         snd_pcm_softvol_t *svol = pcm->private_data;
639         snd_output_printf(out, "Soft volume PCM\n");
640         snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
641         if (svol->max_val == 1)
642                 snd_output_printf(out, "boolean\n");
643         else {
644                 snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
645                 snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
646                 snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
647         }
648         if (pcm->setup) {
649                 snd_output_printf(out, "Its setup is:\n");
650                 snd_pcm_dump_setup(pcm, out);
651         }
652         snd_output_printf(out, "Slave: ");
653         snd_pcm_dump(svol->plug.gen.slave, out);
654 }
655
656 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
657 {
658         unsigned int tlv[4];
659         tlv[0] = SND_CTL_TLVT_DB_SCALE;
660         tlv[1] = 2 * sizeof(int);
661         tlv[2] = svol->min_dB * 100;
662         tlv[3] = (svol->max_dB - svol->min_dB) * 100 / svol->max_val;
663         return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
664 }
665
666 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo, int count)
667 {
668         int err;
669         int i;
670         unsigned int def_val;
671         
672         if (svol->max_val == 1)
673                 err = snd_ctl_elem_add_boolean(svol->ctl, &cinfo->id, count);
674         else
675                 err = snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, count,
676                                                0, svol->max_val, 0);
677         if (err < 0)
678                 return err;
679         if (svol->max_val == 1)
680                 def_val = 1;
681         else {
682                 add_tlv_info(svol, cinfo);
683                 /* set zero dB value as default, or max_val if
684                    there is no 0 dB setting */
685                 def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
686         }
687         for (i = 0; i < count; i++)
688                 svol->elem.value.integer.value[i] = def_val;
689         return snd_ctl_elem_write(svol->ctl, &svol->elem);
690 }
691
692 /*
693  * load and set up user-control
694  * returns 0 if the user-control is found or created,
695  * returns 1 if the control is a hw control,
696  * or a negative error code
697  */
698 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
699                                 int ctl_card, snd_ctl_elem_id_t *ctl_id,
700                                 int cchannels, double min_dB, double max_dB,
701                                 int resolution)
702 {
703         char tmp_name[32];
704         snd_pcm_info_t *info;
705         snd_ctl_elem_info_t *cinfo;
706         int err;
707         unsigned int i;
708
709         if (ctl_card < 0) {
710                 snd_pcm_info_alloca(&info);
711                 err = snd_pcm_info(pcm, info);
712                 if (err < 0)
713                         return err;
714                 ctl_card = snd_pcm_info_get_card(info);
715                 if (ctl_card < 0) {
716                         SNDERR("No card defined for softvol control");
717                         return -EINVAL;
718                 }
719         }
720         sprintf(tmp_name, "hw:%d", ctl_card);
721         err = snd_ctl_open(&svol->ctl, tmp_name, 0);
722         if (err < 0) {
723                 SNDERR("Cannot open CTL %s", tmp_name);
724                 return err;
725         }
726
727         svol->elem.id = *ctl_id;
728         svol->max_val = resolution - 1;
729         svol->min_dB = min_dB;
730         svol->max_dB = max_dB;
731         if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
732                 svol->zero_dB_val = svol->max_val;
733         else if (svol->max_dB < 0)
734                 svol->zero_dB_val = 0; /* there is no 0 dB setting */
735         else
736                 svol->zero_dB_val = (min_dB / (min_dB - max_dB)) * svol->max_val;
737                 
738         snd_ctl_elem_info_alloca(&cinfo);
739         snd_ctl_elem_info_set_id(cinfo, ctl_id);
740         if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
741                 if (err != -ENOENT) {
742                         SNDERR("Cannot get info for CTL %s", tmp_name);
743                         return err;
744                 }
745                 err = add_user_ctl(svol, cinfo, cchannels);
746                 if (err < 0) {
747                         SNDERR("Cannot add a control");
748                         return err;
749                 }
750         } else {
751                 if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
752                         /* hardware control exists */
753                         return 1; /* notify */
754
755                 } else if ((cinfo->type != SND_CTL_ELEM_TYPE_INTEGER &&
756                             cinfo->type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
757                            cinfo->count != (unsigned int)cchannels ||
758                            cinfo->value.integer.min != 0 ||
759                            cinfo->value.integer.max != resolution - 1) {
760                         if ((err = snd_ctl_elem_remove(svol->ctl, &cinfo->id)) < 0) {
761                                 SNDERR("Control %s mismatch", tmp_name);
762                                 return err;
763                         }
764                         snd_ctl_elem_info_set_id(cinfo, ctl_id); /* reset numid */
765                         if ((err = add_user_ctl(svol, cinfo, cchannels)) < 0) {
766                                 SNDERR("Cannot add a control");
767                                 return err;
768                         }
769                 } else if (svol->max_val > 1) {
770                         /* check TLV availability */
771                         unsigned int tlv[4];
772                         err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo->id, tlv, sizeof(tlv));
773                         if (err < 0)
774                                 add_tlv_info(svol, cinfo);
775                 }
776         }
777
778         if (svol->max_val == 1)
779                 return 0;
780
781         /* set up dB table */
782         if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB && resolution == PRESET_RESOLUTION)
783                 svol->dB_value = (unsigned int*)preset_dB_value;
784         else {
785 #ifndef HAVE_SOFT_FLOAT
786                 svol->dB_value = calloc(resolution, sizeof(unsigned int));
787                 if (! svol->dB_value) {
788                         SNDERR("cannot allocate dB table");
789                         return -ENOMEM;
790                 }
791                 svol->min_dB = min_dB;
792                 svol->max_dB = max_dB;
793                 for (i = 0; i <= svol->max_val; i++) {
794                         double db = svol->min_dB + (i * (svol->max_dB - svol->min_dB)) / svol->max_val;
795                         double v = (pow(10.0, db / 20.0) * (double)(1 << VOL_SCALE_SHIFT));
796                         svol->dB_value[i] = (unsigned int)v;
797                 }
798                 if (svol->zero_dB_val)
799                         svol->dB_value[svol->zero_dB_val] = 65535;
800 #else
801                 SNDERR("Cannot handle the given dB range and resolution");
802                 return -EINVAL;
803 #endif
804         }
805         return 0;
806 }
807
808 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
809         .close = snd_pcm_softvol_close,
810         .info = snd_pcm_generic_info,
811         .hw_refine = snd_pcm_softvol_hw_refine,
812         .hw_params = snd_pcm_softvol_hw_params,
813         .hw_free = snd_pcm_generic_hw_free,
814         .sw_params = snd_pcm_generic_sw_params,
815         .channel_info = snd_pcm_generic_channel_info,
816         .dump = snd_pcm_softvol_dump,
817         .nonblock = snd_pcm_generic_nonblock,
818         .async = snd_pcm_generic_async,
819         .mmap = snd_pcm_generic_mmap,
820         .munmap = snd_pcm_generic_munmap,
821 };
822
823 /**
824  * \brief Creates a new SoftVolume PCM
825  * \param pcmp Returns created PCM handle
826  * \param name Name of PCM
827  * \param sformat Slave format
828  * \param ctl_card card index of the control
829  * \param ctl_id The control element
830  * \param cchannels PCM channels
831  * \param min_dB minimal dB value
832  * \param max_dB maximal dB value
833  * \param resolution resolution of control
834  * \param slave Slave PCM handle
835  * \param close_slave When set, the slave PCM handle is closed with copy PCM
836  * \retval zero on success otherwise a negative error code
837  * \warning Using of this function might be dangerous in the sense
838  *          of compatibility reasons. The prototype might be freely
839  *          changed in future.
840  */
841 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
842                          snd_pcm_format_t sformat,
843                          int ctl_card, snd_ctl_elem_id_t *ctl_id,
844                          int cchannels,
845                          double min_dB, double max_dB, int resolution,
846                          snd_pcm_t *slave, int close_slave)
847 {
848         snd_pcm_t *pcm;
849         snd_pcm_softvol_t *svol;
850         int err;
851         assert(pcmp && slave);
852         if (sformat != SND_PCM_FORMAT_UNKNOWN &&
853             sformat != SND_PCM_FORMAT_S16_LE &&
854             sformat != SND_PCM_FORMAT_S16_BE &&
855             sformat != SND_PCM_FORMAT_S24_3LE && 
856             sformat != SND_PCM_FORMAT_S32_LE &&
857             sformat != SND_PCM_FORMAT_S32_BE)
858                 return -EINVAL;
859         svol = calloc(1, sizeof(*svol));
860         if (! svol)
861                 return -ENOMEM;
862         err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
863                                    min_dB, max_dB, resolution);
864         if (err < 0) {
865                 softvol_free(svol);
866                 return err;
867         }
868         if (err > 0) { /* hardware control - no need for softvol! */
869                 softvol_free(svol);
870                 *pcmp = slave; /* just pass the slave */
871                 if (!slave->name && name)
872                         slave->name = strdup(name);
873                 return 0;
874         }
875
876         /* do softvol */
877         snd_pcm_plugin_init(&svol->plug);
878         svol->sformat = sformat;
879         svol->cchannels = cchannels;
880         svol->plug.read = snd_pcm_softvol_read_areas;
881         svol->plug.write = snd_pcm_softvol_write_areas;
882         svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
883         svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
884         svol->plug.gen.slave = slave;
885         svol->plug.gen.close_slave = close_slave;
886
887         err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
888         if (err < 0) {
889                 softvol_free(svol);
890                 return err;
891         }
892         pcm->ops = &snd_pcm_softvol_ops;
893         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
894         pcm->private_data = svol;
895         pcm->poll_fd = slave->poll_fd;
896         pcm->poll_events = slave->poll_events;
897         /*
898          * Since the softvol converts on the place, and the format/channels
899          * must be identical between source and destination, we don't need
900          * an extra buffer.
901          */
902         pcm->mmap_shadow = 1;
903         pcm->monotonic = slave->monotonic;
904         snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
905         snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
906         *pcmp = pcm;
907
908         return 0;
909 }
910
911 /* in pcm_misc.c */
912 int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
913                              int *cchannelsp, int *hwctlp);
914
915 /*! \page pcm_plugins
916
917 \section pcm_plugins_softvol Plugin: Soft Volume
918
919 This plugin applies the software volume attenuation.
920 The format, rate and channels must match for both of source and destination.
921
922 When the control is stereo (count=2), the channels are assumed to be either
923 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
924
925 If the control already exists and it's a system control (i.e. no
926 user-defined control), the plugin simply passes its slave without
927 any changes.
928
929 \code
930 pcm.name {
931         type softvol            # Soft Volume conversion PCM
932         slave STR               # Slave name
933         # or
934         slave {                 # Slave definition
935                 pcm STR         # Slave PCM name
936                 # or
937                 pcm { }         # Slave PCM definition
938                 [format STR]    # Slave format
939         }
940         control {
941                 name STR        # control element id string
942                 [card STR]      # control card index
943                 [iface STR]     # interface of the element
944                 [index INT]     # index of the element
945                 [device INT]    # device number of the element
946                 [subdevice INT] # subdevice number of the element
947                 [count INT]     # control channels 1 or 2 (default: 2)
948         }
949         [min_dB REAL]           # minimal dB value (default: -51.0)
950         [max_dB REAL]           # maximal dB value (default:   0.0)
951         [resolution INT]        # resolution (default: 256)
952                                 # resolution = 2 means a mute switch
953 }
954 \endcode
955
956 \subsection pcm_plugins_softvol_funcref Function reference
957
958 <UL>
959   <LI>snd_pcm_softvol_open()
960   <LI>_snd_pcm_softvol_open()
961 </UL>
962
963 */
964
965 /**
966  * \brief Creates a new Soft Volume PCM
967  * \param pcmp Returns created PCM handle
968  * \param name Name of PCM
969  * \param root Root configuration node
970  * \param conf Configuration node with Soft Volume PCM description
971  * \param stream Stream type
972  * \param mode Stream mode
973  * \retval zero on success otherwise a negative error code
974  * \warning Using of this function might be dangerous in the sense
975  *          of compatibility reasons. The prototype might be freely
976  *          changed in future.
977  */
978 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
979                           snd_config_t *root, snd_config_t *conf, 
980                           snd_pcm_stream_t stream, int mode)
981 {
982         snd_config_iterator_t i, next;
983         int err;
984         snd_pcm_t *spcm;
985         snd_config_t *slave = NULL, *sconf;
986         snd_config_t *control = NULL;
987         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
988         snd_ctl_elem_id_t *ctl_id;
989         int resolution = PRESET_RESOLUTION;
990         double min_dB = PRESET_MIN_DB;
991         double max_dB = ZERO_DB;
992         int card = -1, cchannels = 2;
993
994         snd_config_for_each(i, next, conf) {
995                 snd_config_t *n = snd_config_iterator_entry(i);
996                 const char *id;
997                 if (snd_config_get_id(n, &id) < 0)
998                         continue;
999                 if (snd_pcm_conf_generic_id(id))
1000                         continue;
1001                 if (strcmp(id, "slave") == 0) {
1002                         slave = n;
1003                         continue;
1004                 }
1005                 if (strcmp(id, "control") == 0) {
1006                         control = n;
1007                         continue;
1008                 }
1009                 if (strcmp(id, "resolution") == 0) {
1010                         long v;
1011                         err = snd_config_get_integer(n, &v);
1012                         if (err < 0) {
1013                                 SNDERR("Invalid resolution value");
1014                                 return err;
1015                         }
1016                         resolution = v;
1017                         continue;
1018                 }
1019                 if (strcmp(id, "min_dB") == 0) {
1020                         err = snd_config_get_real(n, &min_dB);
1021                         if (err < 0) {
1022                                 SNDERR("Invalid min_dB value");
1023                                 return err;
1024                         }
1025                         continue;
1026                 }
1027                 if (strcmp(id, "max_dB") == 0) {
1028                         err = snd_config_get_real(n, &max_dB);
1029                         if (err < 0) {
1030                                 SNDERR("Invalid max_dB value");
1031                                 return err;
1032                         }
1033                         continue;
1034                 }
1035                 SNDERR("Unknown field %s", id);
1036                 return -EINVAL;
1037         }
1038         if (!slave) {
1039                 SNDERR("slave is not defined");
1040                 return -EINVAL;
1041         }
1042         if (!control) {
1043                 SNDERR("control is not defined");
1044                 return -EINVAL;
1045         }
1046         if (min_dB >= 0) {
1047                 SNDERR("min_dB must be a negative value");
1048                 return -EINVAL;
1049         }
1050         if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1051                 SNDERR("max_dB must be larger than min_dB and less than %d dB",
1052                        MAX_DB_UPPER_LIMIT);
1053                 return -EINVAL;
1054         }
1055         if (resolution <= 1 || resolution > 1024) {
1056                 SNDERR("Invalid resolution value %d", resolution);
1057                 return -EINVAL;
1058         }
1059         if (mode & SND_PCM_NO_SOFTVOL) {
1060                 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1061                 if (err < 0)
1062                         return err;
1063                 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1064                                                mode, conf);
1065                 snd_config_delete(sconf);
1066         } else {
1067                 snd_ctl_elem_id_alloca(&ctl_id);
1068                 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1069                                          SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1070                 if (err < 0)
1071                         return err;
1072                 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1073                     sformat != SND_PCM_FORMAT_S16_LE &&
1074                     sformat != SND_PCM_FORMAT_S16_BE &&
1075                     sformat != SND_PCM_FORMAT_S24_3LE && 
1076                     sformat != SND_PCM_FORMAT_S32_LE &&
1077                     sformat != SND_PCM_FORMAT_S32_BE) {
1078                         SNDERR("only S16_LE, S16_BE, S24_3LE, S32_LE or S32_BE format "
1079                                "is supported");
1080                         snd_config_delete(sconf);
1081                         return -EINVAL;
1082                 }
1083                 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1084                 snd_config_delete(sconf);
1085                 if (err < 0)
1086                         return err;
1087                 if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) {
1088                         snd_pcm_close(spcm);
1089                         return err;
1090                 }
1091                 err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, cchannels,
1092                                            min_dB, max_dB, resolution, spcm, 1);
1093                 if (err < 0)
1094                         snd_pcm_close(spcm);
1095         }
1096         return err;
1097 }
1098 #ifndef DOC_HIDDEN
1099 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1100 #endif