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