OSDN Git Service

Implement the channel mapping API
[android-x86/external-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         .query_chmaps = snd_pcm_generic_query_chmaps,
822         .get_chmap = snd_pcm_generic_get_chmap,
823         .set_chmap = snd_pcm_generic_set_chmap,
824 };
825
826 /**
827  * \brief Creates a new SoftVolume PCM
828  * \param pcmp Returns created PCM handle
829  * \param name Name of PCM
830  * \param sformat Slave format
831  * \param ctl_card card index of the control
832  * \param ctl_id The control element
833  * \param cchannels PCM channels
834  * \param min_dB minimal dB value
835  * \param max_dB maximal dB value
836  * \param resolution resolution of control
837  * \param slave Slave PCM handle
838  * \param close_slave When set, the slave PCM handle is closed with copy PCM
839  * \retval zero on success otherwise a negative error code
840  * \warning Using of this function might be dangerous in the sense
841  *          of compatibility reasons. The prototype might be freely
842  *          changed in future.
843  */
844 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
845                          snd_pcm_format_t sformat,
846                          int ctl_card, snd_ctl_elem_id_t *ctl_id,
847                          int cchannels,
848                          double min_dB, double max_dB, int resolution,
849                          snd_pcm_t *slave, int close_slave)
850 {
851         snd_pcm_t *pcm;
852         snd_pcm_softvol_t *svol;
853         int err;
854         assert(pcmp && slave);
855         if (sformat != SND_PCM_FORMAT_UNKNOWN &&
856             sformat != SND_PCM_FORMAT_S16_LE &&
857             sformat != SND_PCM_FORMAT_S16_BE &&
858             sformat != SND_PCM_FORMAT_S24_3LE && 
859             sformat != SND_PCM_FORMAT_S32_LE &&
860             sformat != SND_PCM_FORMAT_S32_BE)
861                 return -EINVAL;
862         svol = calloc(1, sizeof(*svol));
863         if (! svol)
864                 return -ENOMEM;
865         err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
866                                    min_dB, max_dB, resolution);
867         if (err < 0) {
868                 softvol_free(svol);
869                 return err;
870         }
871         if (err > 0) { /* hardware control - no need for softvol! */
872                 softvol_free(svol);
873                 *pcmp = slave; /* just pass the slave */
874                 if (!slave->name && name)
875                         slave->name = strdup(name);
876                 return 0;
877         }
878
879         /* do softvol */
880         snd_pcm_plugin_init(&svol->plug);
881         svol->sformat = sformat;
882         svol->cchannels = cchannels;
883         svol->plug.read = snd_pcm_softvol_read_areas;
884         svol->plug.write = snd_pcm_softvol_write_areas;
885         svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
886         svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
887         svol->plug.gen.slave = slave;
888         svol->plug.gen.close_slave = close_slave;
889
890         err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
891         if (err < 0) {
892                 softvol_free(svol);
893                 return err;
894         }
895         pcm->ops = &snd_pcm_softvol_ops;
896         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
897         pcm->private_data = svol;
898         pcm->poll_fd = slave->poll_fd;
899         pcm->poll_events = slave->poll_events;
900         /*
901          * Since the softvol converts on the place, and the format/channels
902          * must be identical between source and destination, we don't need
903          * an extra buffer.
904          */
905         pcm->mmap_shadow = 1;
906         pcm->monotonic = slave->monotonic;
907         snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
908         snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
909         *pcmp = pcm;
910
911         return 0;
912 }
913
914 /* in pcm_misc.c */
915 int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
916                              int *cchannelsp, int *hwctlp);
917
918 /*! \page pcm_plugins
919
920 \section pcm_plugins_softvol Plugin: Soft Volume
921
922 This plugin applies the software volume attenuation.
923 The format, rate and channels must match for both of source and destination.
924
925 When the control is stereo (count=2), the channels are assumed to be either
926 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
927
928 If the control already exists and it's a system control (i.e. no
929 user-defined control), the plugin simply passes its slave without
930 any changes.
931
932 \code
933 pcm.name {
934         type softvol            # Soft Volume conversion PCM
935         slave STR               # Slave name
936         # or
937         slave {                 # Slave definition
938                 pcm STR         # Slave PCM name
939                 # or
940                 pcm { }         # Slave PCM definition
941                 [format STR]    # Slave format
942         }
943         control {
944                 name STR        # control element id string
945                 [card STR]      # control card index
946                 [iface STR]     # interface of the element
947                 [index INT]     # index of the element
948                 [device INT]    # device number of the element
949                 [subdevice INT] # subdevice number of the element
950                 [count INT]     # control channels 1 or 2 (default: 2)
951         }
952         [min_dB REAL]           # minimal dB value (default: -51.0)
953         [max_dB REAL]           # maximal dB value (default:   0.0)
954         [resolution INT]        # resolution (default: 256)
955                                 # resolution = 2 means a mute switch
956 }
957 \endcode
958
959 \subsection pcm_plugins_softvol_funcref Function reference
960
961 <UL>
962   <LI>snd_pcm_softvol_open()
963   <LI>_snd_pcm_softvol_open()
964 </UL>
965
966 */
967
968 /**
969  * \brief Creates a new Soft Volume PCM
970  * \param pcmp Returns created PCM handle
971  * \param name Name of PCM
972  * \param root Root configuration node
973  * \param conf Configuration node with Soft Volume PCM description
974  * \param stream Stream type
975  * \param mode Stream mode
976  * \retval zero on success otherwise a negative error code
977  * \warning Using of this function might be dangerous in the sense
978  *          of compatibility reasons. The prototype might be freely
979  *          changed in future.
980  */
981 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
982                           snd_config_t *root, snd_config_t *conf, 
983                           snd_pcm_stream_t stream, int mode)
984 {
985         snd_config_iterator_t i, next;
986         int err;
987         snd_pcm_t *spcm;
988         snd_config_t *slave = NULL, *sconf;
989         snd_config_t *control = NULL;
990         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
991         snd_ctl_elem_id_t *ctl_id;
992         int resolution = PRESET_RESOLUTION;
993         double min_dB = PRESET_MIN_DB;
994         double max_dB = ZERO_DB;
995         int card = -1, cchannels = 2;
996
997         snd_config_for_each(i, next, conf) {
998                 snd_config_t *n = snd_config_iterator_entry(i);
999                 const char *id;
1000                 if (snd_config_get_id(n, &id) < 0)
1001                         continue;
1002                 if (snd_pcm_conf_generic_id(id))
1003                         continue;
1004                 if (strcmp(id, "slave") == 0) {
1005                         slave = n;
1006                         continue;
1007                 }
1008                 if (strcmp(id, "control") == 0) {
1009                         control = n;
1010                         continue;
1011                 }
1012                 if (strcmp(id, "resolution") == 0) {
1013                         long v;
1014                         err = snd_config_get_integer(n, &v);
1015                         if (err < 0) {
1016                                 SNDERR("Invalid resolution value");
1017                                 return err;
1018                         }
1019                         resolution = v;
1020                         continue;
1021                 }
1022                 if (strcmp(id, "min_dB") == 0) {
1023                         err = snd_config_get_real(n, &min_dB);
1024                         if (err < 0) {
1025                                 SNDERR("Invalid min_dB value");
1026                                 return err;
1027                         }
1028                         continue;
1029                 }
1030                 if (strcmp(id, "max_dB") == 0) {
1031                         err = snd_config_get_real(n, &max_dB);
1032                         if (err < 0) {
1033                                 SNDERR("Invalid max_dB value");
1034                                 return err;
1035                         }
1036                         continue;
1037                 }
1038                 SNDERR("Unknown field %s", id);
1039                 return -EINVAL;
1040         }
1041         if (!slave) {
1042                 SNDERR("slave is not defined");
1043                 return -EINVAL;
1044         }
1045         if (!control) {
1046                 SNDERR("control is not defined");
1047                 return -EINVAL;
1048         }
1049         if (min_dB >= 0) {
1050                 SNDERR("min_dB must be a negative value");
1051                 return -EINVAL;
1052         }
1053         if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1054                 SNDERR("max_dB must be larger than min_dB and less than %d dB",
1055                        MAX_DB_UPPER_LIMIT);
1056                 return -EINVAL;
1057         }
1058         if (resolution <= 1 || resolution > 1024) {
1059                 SNDERR("Invalid resolution value %d", resolution);
1060                 return -EINVAL;
1061         }
1062         if (mode & SND_PCM_NO_SOFTVOL) {
1063                 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1064                 if (err < 0)
1065                         return err;
1066                 err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1067                                                mode, conf);
1068                 snd_config_delete(sconf);
1069         } else {
1070                 snd_ctl_elem_id_alloca(&ctl_id);
1071                 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1072                                          SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1073                 if (err < 0)
1074                         return err;
1075                 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1076                     sformat != SND_PCM_FORMAT_S16_LE &&
1077                     sformat != SND_PCM_FORMAT_S16_BE &&
1078                     sformat != SND_PCM_FORMAT_S24_3LE && 
1079                     sformat != SND_PCM_FORMAT_S32_LE &&
1080                     sformat != SND_PCM_FORMAT_S32_BE) {
1081                         SNDERR("only S16_LE, S16_BE, S24_3LE, S32_LE or S32_BE format "
1082                                "is supported");
1083                         snd_config_delete(sconf);
1084                         return -EINVAL;
1085                 }
1086                 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1087                 snd_config_delete(sconf);
1088                 if (err < 0)
1089                         return err;
1090                 if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) {
1091                         snd_pcm_close(spcm);
1092                         return err;
1093                 }
1094                 err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, cchannels,
1095                                            min_dB, max_dB, resolution, spcm, 1);
1096                 if (err < 0)
1097                         snd_pcm_close(spcm);
1098         }
1099         return err;
1100 }
1101 #ifndef DOC_HIDDEN
1102 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1103 #endif