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