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)710 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
711 {
712 	unsigned int tlv[4];
713 	tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
714 	tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
715 	tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
716 	tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
717 		(int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
718 	return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
719 }
720 
add_user_ctl(snd_pcm_softvol_t * svol,snd_ctl_elem_info_t * cinfo,int count)721 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
722 			int count)
723 {
724 	int err;
725 	int i;
726 	unsigned int def_val;
727 
728 	if (svol->max_val == 1)
729 		err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
730 	else
731 		err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
732 						   0, svol->max_val, 0);
733 	if (err < 0)
734 		return err;
735 	if (svol->max_val == 1)
736 		def_val = 1;
737 	else {
738 		add_tlv_info(svol, cinfo);
739 		/* set zero dB value as default, or max_val if
740 		   there is no 0 dB setting */
741 		def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
742 	}
743 	for (i = 0; i < count; i++)
744 		svol->elem.value.integer.value[i] = def_val;
745 	return snd_ctl_elem_write(svol->ctl, &svol->elem);
746 }
747 
748 /*
749  * load and set up user-control
750  * returns 0 if the user-control is found or created,
751  * returns 1 if the control is a hw control,
752  * or a negative error code
753  */
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)754 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
755 				int ctl_card, snd_ctl_elem_id_t *ctl_id,
756 				int cchannels, double min_dB, double max_dB,
757 				int resolution)
758 {
759 	char tmp_name[32];
760 	snd_pcm_info_t info = {0};
761 	snd_ctl_elem_info_t cinfo = {0};
762 	int err;
763 	unsigned int i;
764 
765 	if (ctl_card < 0) {
766 		err = snd_pcm_info(pcm, &info);
767 		if (err < 0)
768 			return err;
769 		ctl_card = snd_pcm_info_get_card(&info);
770 		if (ctl_card < 0) {
771 			SNDERR("No card defined for softvol control");
772 			return -EINVAL;
773 		}
774 	}
775 	sprintf(tmp_name, "hw:%d", ctl_card);
776 	err = snd_ctl_open(&svol->ctl, tmp_name, 0);
777 	if (err < 0) {
778 		SNDERR("Cannot open CTL %s", tmp_name);
779 		return err;
780 	}
781 
782 	svol->elem.id = *ctl_id;
783 	svol->max_val = resolution - 1;
784 	svol->min_dB = min_dB;
785 	svol->max_dB = max_dB;
786 	if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
787 		svol->zero_dB_val = svol->max_val;
788 	else if (svol->max_dB < 0)
789 		svol->zero_dB_val = 0; /* there is no 0 dB setting */
790 	else
791 		svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
792 								svol->max_val;
793 
794 	snd_ctl_elem_info_set_id(&cinfo, ctl_id);
795 	if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
796 		if (err != -ENOENT) {
797 			SNDERR("Cannot get info for CTL %s", tmp_name);
798 			return err;
799 		}
800 		err = add_user_ctl(svol, &cinfo, cchannels);
801 		if (err < 0) {
802 			SNDERR("Cannot add a control");
803 			return err;
804 		}
805 	} else {
806 		if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
807 			/* hardware control exists */
808 			return 1; /* notify */
809 
810 		} else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
811 			    cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
812 			   cinfo.count != (unsigned int)cchannels ||
813 			   cinfo.value.integer.min != 0 ||
814 			   cinfo.value.integer.max != resolution - 1) {
815 			err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
816 			if (err < 0) {
817 				SNDERR("Control %s mismatch", tmp_name);
818 				return err;
819 			}
820 			/* reset numid */
821 			snd_ctl_elem_info_set_id(&cinfo, ctl_id);
822 			if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
823 				SNDERR("Cannot add a control");
824 				return err;
825 			}
826 		} else if (svol->max_val > 1) {
827 			/* check TLV availability */
828 			unsigned int tlv[4];
829 			err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
830 						    sizeof(tlv));
831 			if (err < 0)
832 				add_tlv_info(svol, &cinfo);
833 		}
834 	}
835 
836 	if (svol->max_val == 1)
837 		return 0;
838 
839 	/* set up dB table */
840 	if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
841 						resolution == PRESET_RESOLUTION)
842 		svol->dB_value = (unsigned int*)preset_dB_value;
843 	else {
844 #ifndef HAVE_SOFT_FLOAT
845 		svol->dB_value = calloc(resolution, sizeof(unsigned int));
846 		if (! svol->dB_value) {
847 			SNDERR("cannot allocate dB table");
848 			return -ENOMEM;
849 		}
850 		svol->min_dB = min_dB;
851 		svol->max_dB = max_dB;
852 		for (i = 0; i <= svol->max_val; i++) {
853 			double db = svol->min_dB +
854 				(i * (svol->max_dB - svol->min_dB)) /
855 					svol->max_val;
856 			double v = (pow(10.0, db / 20.0) *
857 					(double)(1 << VOL_SCALE_SHIFT));
858 			svol->dB_value[i] = (unsigned int)v;
859 		}
860 		if (svol->zero_dB_val)
861 			svol->dB_value[svol->zero_dB_val] = 65535;
862 #else
863 		SNDERR("Cannot handle the given dB range and resolution");
864 		return -EINVAL;
865 #endif
866 	}
867 	return 0;
868 }
869 
870 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
871 	.close = snd_pcm_softvol_close,
872 	.info = snd_pcm_generic_info,
873 	.hw_refine = snd_pcm_softvol_hw_refine,
874 	.hw_params = snd_pcm_softvol_hw_params,
875 	.hw_free = snd_pcm_generic_hw_free,
876 	.sw_params = snd_pcm_generic_sw_params,
877 	.channel_info = snd_pcm_generic_channel_info,
878 	.dump = snd_pcm_softvol_dump,
879 	.nonblock = snd_pcm_generic_nonblock,
880 	.async = snd_pcm_generic_async,
881 	.mmap = snd_pcm_generic_mmap,
882 	.munmap = snd_pcm_generic_munmap,
883 	.query_chmaps = snd_pcm_generic_query_chmaps,
884 	.get_chmap = snd_pcm_generic_get_chmap,
885 	.set_chmap = snd_pcm_generic_set_chmap,
886 };
887 
888 /**
889  * \brief Creates a new SoftVolume PCM
890  * \param pcmp Returns created PCM handle
891  * \param name Name of PCM
892  * \param sformat Slave format
893  * \param ctl_card card index of the control
894  * \param ctl_id The control element
895  * \param cchannels PCM channels
896  * \param min_dB minimal dB value
897  * \param max_dB maximal dB value
898  * \param resolution resolution of control
899  * \param slave Slave PCM handle
900  * \param close_slave When set, the slave PCM handle is closed with copy PCM
901  * \retval zero on success otherwise a negative error code
902  * \warning Using of this function might be dangerous in the sense
903  *          of compatibility reasons. The prototype might be freely
904  *          changed in future.
905  */
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)906 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
907 			 snd_pcm_format_t sformat,
908 			 int ctl_card, snd_ctl_elem_id_t *ctl_id,
909 			 int cchannels,
910 			 double min_dB, double max_dB, int resolution,
911 			 snd_pcm_t *slave, int close_slave)
912 {
913 	snd_pcm_t *pcm;
914 	snd_pcm_softvol_t *svol;
915 	int err;
916 	assert(pcmp && slave);
917 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
918 	    sformat != SND_PCM_FORMAT_S16_LE &&
919 	    sformat != SND_PCM_FORMAT_S16_BE &&
920 	    sformat != SND_PCM_FORMAT_S24_3LE &&
921 	    sformat != SND_PCM_FORMAT_S24_LE &&
922 	    sformat != SND_PCM_FORMAT_S32_LE &&
923 	    sformat != SND_PCM_FORMAT_S32_BE)
924 		return -EINVAL;
925 	svol = calloc(1, sizeof(*svol));
926 	if (! svol)
927 		return -ENOMEM;
928 	err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
929 				   min_dB, max_dB, resolution);
930 	if (err < 0) {
931 		softvol_free(svol);
932 		return err;
933 	}
934 	if (err > 0) { /* hardware control - no need for softvol! */
935 		softvol_free(svol);
936 		*pcmp = slave; /* just pass the slave */
937 		if (!slave->name && name)
938 			slave->name = strdup(name);
939 		return 0;
940 	}
941 
942 	/* do softvol */
943 	snd_pcm_plugin_init(&svol->plug);
944 	svol->sformat = sformat;
945 	svol->cchannels = cchannels;
946 	svol->plug.read = snd_pcm_softvol_read_areas;
947 	svol->plug.write = snd_pcm_softvol_write_areas;
948 	svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
949 	svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
950 	svol->plug.gen.slave = slave;
951 	svol->plug.gen.close_slave = close_slave;
952 
953 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
954 	if (err < 0) {
955 		softvol_free(svol);
956 		return err;
957 	}
958 	pcm->ops = &snd_pcm_softvol_ops;
959 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
960 	pcm->private_data = svol;
961 	pcm->poll_fd = slave->poll_fd;
962 	pcm->poll_events = slave->poll_events;
963 	/*
964 	 * Since the softvol converts on the place, and the format/channels
965 	 * must be identical between source and destination, we don't need
966 	 * an extra buffer.
967 	 */
968 	pcm->mmap_shadow = 1;
969 	pcm->tstamp_type = slave->tstamp_type;
970 	snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
971 	snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
972 	*pcmp = pcm;
973 
974 	return 0;
975 }
976 
977 /* in pcm_misc.c */
978 int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
979 			     int *cchannelsp, int *hwctlp);
980 
981 /*! \page pcm_plugins
982 
983 \section pcm_plugins_softvol Plugin: Soft Volume
984 
985 This plugin applies the software volume attenuation.
986 The format, rate and channels must match for both of source and destination.
987 
988 When the control is stereo (count=2), the channels are assumed to be either
989 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
990 
991 If the control already exists and it's a system control (i.e. no
992 user-defined control), the plugin simply passes its slave without
993 any changes.
994 
995 \code
996 pcm.name {
997         type softvol            # Soft Volume conversion PCM
998         slave STR               # Slave name
999         # or
1000         slave {                 # Slave definition
1001                 pcm STR         # Slave PCM name
1002                 # or
1003                 pcm { }         # Slave PCM definition
1004                 [format STR]    # Slave format
1005         }
1006         control {
1007 	        name STR        # control element id string
1008 		[card STR]      # control card index
1009 		[iface STR]     # interface of the element
1010 		[index INT]     # index of the element
1011 		[device INT]    # device number of the element
1012 		[subdevice INT] # subdevice number of the element
1013 		[count INT]     # control channels 1 or 2 (default: 2)
1014 	}
1015 	[min_dB REAL]           # minimal dB value (default: -51.0)
1016 	[max_dB REAL]           # maximal dB value (default:   0.0)
1017 	[resolution INT]        # resolution (default: 256)
1018 				# resolution = 2 means a mute switch
1019 }
1020 \endcode
1021 
1022 \subsection pcm_plugins_softvol_funcref Function reference
1023 
1024 <UL>
1025   <LI>snd_pcm_softvol_open()
1026   <LI>_snd_pcm_softvol_open()
1027 </UL>
1028 
1029 */
1030 
1031 /**
1032  * \brief Creates a new Soft Volume PCM
1033  * \param pcmp Returns created PCM handle
1034  * \param name Name of PCM
1035  * \param root Root configuration node
1036  * \param conf Configuration node with Soft Volume PCM description
1037  * \param stream Stream type
1038  * \param mode Stream mode
1039  * \retval zero on success otherwise a negative error code
1040  * \warning Using of this function might be dangerous in the sense
1041  *          of compatibility reasons. The prototype might be freely
1042  *          changed in future.
1043  */
_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)1044 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
1045 			  snd_config_t *root, snd_config_t *conf,
1046 			  snd_pcm_stream_t stream, int mode)
1047 {
1048 	snd_config_iterator_t i, next;
1049 	int err;
1050 	snd_pcm_t *spcm;
1051 	snd_config_t *slave = NULL, *sconf;
1052 	snd_config_t *control = NULL;
1053 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1054 	snd_ctl_elem_id_t ctl_id = {0};
1055 	int resolution = PRESET_RESOLUTION;
1056 	double min_dB = PRESET_MIN_DB;
1057 	double max_dB = ZERO_DB;
1058 	int card = -1, cchannels = 2;
1059 
1060 	snd_config_for_each(i, next, conf) {
1061 		snd_config_t *n = snd_config_iterator_entry(i);
1062 		const char *id;
1063 		if (snd_config_get_id(n, &id) < 0)
1064 			continue;
1065 		if (snd_pcm_conf_generic_id(id))
1066 			continue;
1067 		if (strcmp(id, "slave") == 0) {
1068 			slave = n;
1069 			continue;
1070 		}
1071 		if (strcmp(id, "control") == 0) {
1072 			control = n;
1073 			continue;
1074 		}
1075 		if (strcmp(id, "resolution") == 0) {
1076 			long v;
1077 			err = snd_config_get_integer(n, &v);
1078 			if (err < 0) {
1079 				SNDERR("Invalid resolution value");
1080 				return err;
1081 			}
1082 			resolution = v;
1083 			continue;
1084 		}
1085 		if (strcmp(id, "min_dB") == 0) {
1086 			err = snd_config_get_real(n, &min_dB);
1087 			if (err < 0) {
1088 				SNDERR("Invalid min_dB value");
1089 				return err;
1090 			}
1091 			continue;
1092 		}
1093 		if (strcmp(id, "max_dB") == 0) {
1094 			err = snd_config_get_real(n, &max_dB);
1095 			if (err < 0) {
1096 				SNDERR("Invalid max_dB value");
1097 				return err;
1098 			}
1099 			continue;
1100 		}
1101 		SNDERR("Unknown field %s", id);
1102 		return -EINVAL;
1103 	}
1104 	if (!slave) {
1105 		SNDERR("slave is not defined");
1106 		return -EINVAL;
1107 	}
1108 	if (!control) {
1109 		SNDERR("control is not defined");
1110 		return -EINVAL;
1111 	}
1112 	if (min_dB >= 0) {
1113 		SNDERR("min_dB must be a negative value");
1114 		return -EINVAL;
1115 	}
1116 	if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
1117 		SNDERR("max_dB must be larger than min_dB and less than %d dB",
1118 		       MAX_DB_UPPER_LIMIT);
1119 		return -EINVAL;
1120 	}
1121 	if (resolution <= 1 || resolution > 1024) {
1122 		SNDERR("Invalid resolution value %d", resolution);
1123 		return -EINVAL;
1124 	}
1125 	if (mode & SND_PCM_NO_SOFTVOL) {
1126 		err = snd_pcm_slave_conf(root, slave, &sconf, 0);
1127 		if (err < 0)
1128 			return err;
1129 		err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
1130 					       mode, conf);
1131 		snd_config_delete(sconf);
1132 	} else {
1133 		err = snd_pcm_slave_conf(root, slave, &sconf, 1,
1134 					 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
1135 		if (err < 0)
1136 			return err;
1137 		if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1138 		    sformat != SND_PCM_FORMAT_S16_LE &&
1139 		    sformat != SND_PCM_FORMAT_S16_BE &&
1140 		    sformat != SND_PCM_FORMAT_S24_3LE &&
1141 		    sformat != SND_PCM_FORMAT_S24_LE &&
1142 		    sformat != SND_PCM_FORMAT_S32_LE &&
1143 		    sformat != SND_PCM_FORMAT_S32_BE) {
1144 			SNDERR("only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
1145 			snd_config_delete(sconf);
1146 			return -EINVAL;
1147 		}
1148 		err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1149 		snd_config_delete(sconf);
1150 		if (err < 0)
1151 			return err;
1152 		err = snd_pcm_parse_control_id(control, &ctl_id, &card,
1153 					       &cchannels, NULL);
1154 		if (err < 0) {
1155 			snd_pcm_close(spcm);
1156 			return err;
1157 		}
1158 		err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
1159 					   cchannels, min_dB, max_dB,
1160 					   resolution, spcm, 1);
1161 		if (err < 0)
1162 			snd_pcm_close(spcm);
1163 	}
1164 	return err;
1165 }
1166 #ifndef DOC_HIDDEN
1167 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
1168 #endif
1169