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