• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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