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