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