mixer_controls.c (16789B)
1 /* 2 * mixer_controls.c - handles mixer controls and mapping from selems 3 * Copyright (c) 1998,1999 Tim Janik 4 * Jaroslav Kysela <perex@perex.cz> 5 * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "aconfig.h" 22 #include <stdlib.h> 23 #include <string.h> 24 #include <assert.h> 25 #include CURSESINC 26 #include <alsa/asoundlib.h> 27 #include "utils.h" 28 #include "mem.h" 29 #include "mixer_display.h" 30 #include "mixer_widget.h" 31 #include "mixer_controls.h" 32 33 struct control *controls; 34 unsigned int controls_count; 35 36 static const snd_mixer_selem_channel_id_t supported_channels[] = { 37 SND_MIXER_SCHN_FRONT_LEFT, 38 SND_MIXER_SCHN_FRONT_RIGHT, 39 SND_MIXER_SCHN_REAR_LEFT, 40 SND_MIXER_SCHN_REAR_RIGHT, 41 SND_MIXER_SCHN_FRONT_CENTER, 42 SND_MIXER_SCHN_WOOFER, 43 SND_MIXER_SCHN_SIDE_LEFT, 44 SND_MIXER_SCHN_SIDE_RIGHT, 45 }; 46 #define LAST_SUPPORTED_CHANNEL SND_MIXER_SCHN_SIDE_RIGHT 47 48 static const snd_mixer_selem_channel_id_t control_channels[][2] = { 49 { SND_MIXER_SCHN_FRONT_LEFT, SND_MIXER_SCHN_FRONT_RIGHT }, 50 { SND_MIXER_SCHN_REAR_LEFT, SND_MIXER_SCHN_REAR_RIGHT }, 51 { SND_MIXER_SCHN_FRONT_CENTER, SND_MIXER_SCHN_UNKNOWN }, 52 { SND_MIXER_SCHN_WOOFER, SND_MIXER_SCHN_UNKNOWN }, 53 { SND_MIXER_SCHN_SIDE_LEFT, SND_MIXER_SCHN_SIDE_RIGHT }, 54 }; 55 56 bool are_there_any_controls(void) 57 { 58 snd_mixer_elem_t *elem; 59 unsigned int i; 60 61 for (elem = snd_mixer_first_elem(mixer); 62 elem; 63 elem = snd_mixer_elem_next(elem)) { 64 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 65 continue; 66 if (snd_mixer_selem_is_enumerated(elem)) 67 return TRUE; 68 if (snd_mixer_selem_has_playback_volume_joined(elem) || 69 snd_mixer_selem_has_capture_volume_joined(elem) || 70 snd_mixer_selem_has_playback_switch_joined(elem) || 71 snd_mixer_selem_has_capture_switch_joined(elem)) 72 return TRUE; 73 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 74 if (snd_mixer_selem_has_playback_channel(elem, supported_channels[i]) || 75 snd_mixer_selem_has_capture_channel(elem, supported_channels[i])) 76 return TRUE; 77 } 78 return FALSE; 79 } 80 81 static bool has_more_than_front_capture_channels(snd_mixer_elem_t *elem) 82 { 83 unsigned int i; 84 85 for (i = 2; i < ARRAY_SIZE(supported_channels); ++i) 86 if (snd_mixer_selem_has_capture_channel(elem, supported_channels[i])) 87 return TRUE; 88 return FALSE; 89 } 90 91 static bool has_any_control_channel(snd_mixer_elem_t *elem, 92 const snd_mixer_selem_channel_id_t channels[2], 93 int (*has_channel)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t)) 94 { 95 return has_channel(elem, channels[0]) || 96 (channels[1] != SND_MIXER_SCHN_UNKNOWN && has_channel(elem, channels[1])); 97 } 98 99 static bool has_merged_cswitch(snd_mixer_elem_t *elem) 100 { 101 bool pvol, psw; 102 unsigned int i; 103 104 pvol = snd_mixer_selem_has_playback_volume(elem); 105 psw = snd_mixer_selem_has_playback_switch(elem); 106 if ((pvol || psw) && 107 snd_mixer_selem_has_capture_switch(elem) && 108 !snd_mixer_selem_has_capture_volume(elem)) { 109 if (snd_mixer_selem_has_capture_switch_joined(elem)) 110 return TRUE; 111 else if (((pvol && snd_mixer_selem_has_playback_volume_joined(elem)) || 112 (psw && snd_mixer_selem_has_playback_switch_joined(elem))) && 113 has_more_than_front_capture_channels(elem)) 114 return FALSE; 115 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 116 if (has_any_control_channel(elem, control_channels[i], snd_mixer_selem_has_capture_channel) && 117 !has_any_control_channel(elem, control_channels[i], snd_mixer_selem_has_playback_channel)) 118 return FALSE; 119 } 120 return TRUE; 121 } 122 return FALSE; 123 } 124 125 static unsigned int get_playback_controls_count(snd_mixer_elem_t *elem) 126 { 127 unsigned int count = 0; 128 unsigned int i; 129 int has_vol, has_sw; 130 131 has_vol = snd_mixer_selem_has_playback_volume(elem); 132 has_sw = snd_mixer_selem_has_playback_switch(elem); 133 if (!has_vol && !has_sw) 134 return 0; 135 if ((!has_vol || snd_mixer_selem_has_playback_volume_joined(elem)) && 136 (!has_sw || snd_mixer_selem_has_playback_switch_joined(elem))) 137 return 1; 138 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 139 if (snd_mixer_selem_has_playback_channel(elem, control_channels[i][0]) || 140 (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 141 snd_mixer_selem_has_playback_channel(elem, control_channels[i][1]))) 142 ++count; 143 } 144 return count; 145 } 146 147 static unsigned int get_capture_controls_count(snd_mixer_elem_t *elem) 148 { 149 unsigned int count = 0; 150 unsigned int i; 151 int has_vol, has_sw; 152 153 has_vol = snd_mixer_selem_has_capture_volume(elem); 154 has_sw = snd_mixer_selem_has_capture_switch(elem); 155 if ((!has_vol && !has_sw) || 156 (view_mode == VIEW_MODE_ALL && has_merged_cswitch(elem))) 157 return 0; 158 if ((!has_vol || snd_mixer_selem_has_capture_volume_joined(elem)) && 159 (!has_sw || snd_mixer_selem_has_capture_switch_joined(elem))) 160 return 1; 161 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 162 if (snd_mixer_selem_has_capture_channel(elem, control_channels[i][0]) || 163 (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 164 snd_mixer_selem_has_capture_channel(elem, control_channels[i][1]))) 165 ++count; 166 } 167 return count; 168 } 169 170 static unsigned int get_controls_count_for_elem(snd_mixer_elem_t *elem) 171 { 172 unsigned int p, c; 173 174 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 175 return 0; 176 if (snd_mixer_selem_is_enumerated(elem)) { 177 switch (view_mode) { 178 case VIEW_MODE_PLAYBACK: 179 return snd_mixer_selem_is_enum_capture(elem) ? 0 : 1; 180 case VIEW_MODE_CAPTURE: 181 return snd_mixer_selem_is_enum_capture(elem) ? 1 : 0; 182 case VIEW_MODE_ALL: 183 default: 184 return 1; 185 } 186 } 187 switch (view_mode) { 188 case VIEW_MODE_PLAYBACK: 189 return get_playback_controls_count(elem); 190 case VIEW_MODE_CAPTURE: 191 return get_capture_controls_count(elem); 192 case VIEW_MODE_ALL: 193 default: 194 p = get_playback_controls_count(elem); 195 c = get_capture_controls_count(elem); 196 return has_merged_cswitch(elem) ? p : p + c; 197 } 198 } 199 200 static void create_name(struct control *control) 201 { 202 unsigned int index; 203 char *s; 204 205 index = snd_mixer_selem_get_index(control->elem); 206 if (index > 0) 207 control->name = casprintf("%s %u", snd_mixer_selem_get_name(control->elem), index); 208 else 209 control->name = cstrdup(snd_mixer_selem_get_name(control->elem)); 210 211 while ((s = strstr(control->name, "IEC958")) != NULL) 212 memcpy(s, "S/PDIF", 6); 213 } 214 215 static unsigned int create_controls_for_elem(snd_mixer_elem_t *elem, struct control *control) 216 { 217 unsigned int count = 0; 218 unsigned int i; 219 unsigned int multich_flag; 220 unsigned int enum_index; 221 struct control *front_control = NULL; 222 bool has_pvol, has_psw; 223 bool has_cvol, has_csw; 224 bool has_channel[LAST_SUPPORTED_CHANNEL + 1]; 225 bool merged_cswitch; 226 bool has_ch0, has_ch1; 227 228 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 229 return 0; 230 if (snd_mixer_selem_is_enumerated(elem)) { 231 if ((view_mode == VIEW_MODE_PLAYBACK && snd_mixer_selem_is_enum_capture(elem)) || 232 (view_mode == VIEW_MODE_CAPTURE && !snd_mixer_selem_is_enum_capture(elem))) 233 return 0; 234 control->elem = elem; 235 control->flags = TYPE_ENUM; 236 control->enum_channel_bits = 0; 237 for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i) 238 if (snd_mixer_selem_get_enum_item(control->elem, (snd_mixer_selem_channel_id_t)i, &enum_index) >= 0) 239 control->enum_channel_bits |= 1 << i; 240 if (snd_mixer_selem_is_active(control->elem)) 241 control->flags |= IS_ACTIVE; 242 create_name(control); 243 return 1; 244 } 245 has_pvol = snd_mixer_selem_has_playback_volume(elem); 246 has_psw = snd_mixer_selem_has_playback_switch(elem); 247 has_cvol = snd_mixer_selem_has_capture_volume(elem); 248 has_csw = snd_mixer_selem_has_capture_switch(elem); 249 merged_cswitch = view_mode == VIEW_MODE_ALL && has_merged_cswitch(elem); 250 if (view_mode != VIEW_MODE_CAPTURE && (has_pvol || has_psw)) { 251 if ((!has_pvol || snd_mixer_selem_has_playback_volume_joined(elem)) && 252 (!has_psw || snd_mixer_selem_has_playback_switch_joined(elem))) { 253 control->elem = elem; 254 if (has_pvol) { 255 control->flags |= TYPE_PVOLUME | HAS_VOLUME_0; 256 control->volume_channels[0] = 0; 257 } 258 if (has_psw) { 259 control->flags |= TYPE_PSWITCH | HAS_PSWITCH_0; 260 control->pswitch_channels[0] = 0; 261 } 262 if (merged_cswitch) { 263 control->flags |= TYPE_CSWITCH; 264 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 265 control->flags |= HAS_CSWITCH_0; 266 control->cswitch_channels[0] = 0; 267 } else { 268 if (snd_mixer_selem_has_capture_channel(elem, control_channels[0][0])) { 269 control->flags |= HAS_CSWITCH_0; 270 control->cswitch_channels[0] = control_channels[0][0]; 271 } 272 if (control_channels[0][1] != SND_MIXER_SCHN_UNKNOWN && 273 snd_mixer_selem_has_capture_channel(elem, control_channels[0][1])) { 274 control->flags |= HAS_CSWITCH_1; 275 control->cswitch_channels[1] = control_channels[0][1]; 276 } 277 } 278 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 279 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 280 control->cswitch_channels[0] = control->cswitch_channels[1]; 281 } 282 } 283 if (snd_mixer_selem_is_active(control->elem)) 284 control->flags |= IS_ACTIVE; 285 create_name(control); 286 ++control; 287 ++count; 288 } else { 289 multich_flag = 0; 290 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 291 has_channel[supported_channels[i]] = 292 snd_mixer_selem_has_playback_channel(elem, supported_channels[i]); 293 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 294 has_ch0 = has_channel[control_channels[i][0]]; 295 has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 296 has_channel[control_channels[i][1]]; 297 if (!has_ch0 && !has_ch1) 298 continue; 299 control->elem = elem; 300 if (has_pvol) { 301 control->flags |= TYPE_PVOLUME; 302 if (snd_mixer_selem_has_playback_volume_joined(elem)) { 303 control->flags |= HAS_VOLUME_0; 304 control->volume_channels[0] = 0; 305 } else { 306 if (has_ch0) { 307 control->flags |= HAS_VOLUME_0; 308 control->volume_channels[0] = control_channels[i][0]; 309 } 310 if (has_ch1) { 311 control->flags |= HAS_VOLUME_1; 312 control->volume_channels[1] = control_channels[i][1]; 313 } 314 } 315 } 316 if (has_psw) { 317 control->flags |= TYPE_PSWITCH; 318 if (snd_mixer_selem_has_playback_switch_joined(elem)) { 319 control->flags |= HAS_PSWITCH_0; 320 control->pswitch_channels[0] = 0; 321 } else { 322 if (has_ch0) { 323 control->flags |= HAS_PSWITCH_0; 324 control->pswitch_channels[0] = control_channels[i][0]; 325 } 326 if (has_ch1) { 327 control->flags |= HAS_PSWITCH_1; 328 control->pswitch_channels[1] = control_channels[i][1]; 329 } 330 } 331 } 332 if (merged_cswitch) { 333 control->flags |= TYPE_CSWITCH; 334 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 335 control->flags |= HAS_CSWITCH_0; 336 control->cswitch_channels[0] = 0; 337 } else { 338 if (snd_mixer_selem_has_capture_channel(elem, control_channels[i][0])) { 339 control->flags |= HAS_CSWITCH_0; 340 control->cswitch_channels[0] = control_channels[i][0]; 341 } 342 if (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 343 snd_mixer_selem_has_capture_channel(elem, control_channels[i][1])) { 344 control->flags |= HAS_CSWITCH_1; 345 control->cswitch_channels[1] = control_channels[i][1]; 346 } 347 } 348 } 349 if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) { 350 control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1; 351 control->volume_channels[0] = control->volume_channels[1]; 352 } 353 if ((control->flags & (HAS_PSWITCH_0 | HAS_PSWITCH_1)) == HAS_PSWITCH_1) { 354 control->flags ^= HAS_PSWITCH_0 | HAS_PSWITCH_1; 355 control->pswitch_channels[0] = control->pswitch_channels[1]; 356 } 357 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 358 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 359 control->cswitch_channels[0] = control->cswitch_channels[1]; 360 } 361 if (snd_mixer_selem_is_active(control->elem)) 362 control->flags |= IS_ACTIVE; 363 create_name(control); 364 if (i == 0) 365 front_control = control; 366 else { 367 front_control->flags |= IS_MULTICH | 0; 368 control->flags |= IS_MULTICH | i; 369 } 370 ++control; 371 ++count; 372 } 373 } 374 } 375 if (view_mode != VIEW_MODE_PLAYBACK && (has_cvol || has_csw) && !merged_cswitch) { 376 if ((!has_cvol || snd_mixer_selem_has_capture_volume_joined(elem)) && 377 (!has_csw || snd_mixer_selem_has_capture_switch_joined(elem))) { 378 control->elem = elem; 379 if (has_cvol) { 380 control->flags |= TYPE_CVOLUME | HAS_VOLUME_0; 381 control->volume_channels[0] = 0; 382 } 383 if (has_csw) { 384 control->flags |= TYPE_CSWITCH | HAS_CSWITCH_0; 385 control->cswitch_channels[0] = 0; 386 } 387 if (snd_mixer_selem_is_active(control->elem)) 388 control->flags |= IS_ACTIVE; 389 create_name(control); 390 ++control; 391 ++count; 392 } else { 393 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 394 has_channel[supported_channels[i]] = 395 snd_mixer_selem_has_capture_channel(elem, supported_channels[i]); 396 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 397 has_ch0 = has_channel[control_channels[i][0]]; 398 has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 399 has_channel[control_channels[i][1]]; 400 if (!has_ch0 && !has_ch1) 401 continue; 402 control->elem = elem; 403 if (has_cvol) { 404 control->flags |= TYPE_CVOLUME; 405 if (snd_mixer_selem_has_capture_volume_joined(elem)) { 406 control->flags |= HAS_VOLUME_0; 407 control->volume_channels[0] = 0; 408 } else { 409 if (has_ch0) { 410 control->flags |= HAS_VOLUME_0; 411 control->volume_channels[0] = control_channels[i][0]; 412 } 413 if (has_ch1) { 414 control->flags |= HAS_VOLUME_1; 415 control->volume_channels[1] = control_channels[i][1]; 416 } 417 } 418 } 419 if (has_csw) { 420 control->flags |= TYPE_CSWITCH; 421 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 422 control->flags |= HAS_CSWITCH_0; 423 control->cswitch_channels[0] = 0; 424 } else { 425 if (has_ch0) { 426 control->flags |= HAS_CSWITCH_0; 427 control->cswitch_channels[0] = control_channels[i][0]; 428 } 429 if (has_ch1) { 430 control->flags |= HAS_CSWITCH_1; 431 control->cswitch_channels[1] = control_channels[i][1]; 432 } 433 } 434 } 435 if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) { 436 control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1; 437 control->volume_channels[0] = control->volume_channels[1]; 438 } 439 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 440 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 441 control->cswitch_channels[0] = control->cswitch_channels[1]; 442 } 443 if (snd_mixer_selem_is_active(control->elem)) 444 control->flags |= IS_ACTIVE; 445 create_name(control); 446 if (i == 0) 447 front_control = control; 448 else { 449 front_control->flags |= IS_MULTICH | 0; 450 control->flags |= IS_MULTICH | i; 451 } 452 ++control; 453 ++count; 454 } 455 } 456 } 457 return count; 458 } 459 460 static void search_for_focus_control(void) 461 { 462 snd_mixer_elem_t *elem; 463 unsigned int i; 464 465 elem = snd_mixer_find_selem(mixer, current_selem_id); 466 if (elem) 467 for (i = 0; i < controls_count; ++i) 468 if (controls[i].elem == elem) { 469 focus_control_index = i; 470 for (;;) { 471 ++i; 472 if (i >= controls_count || controls[i].elem != elem) 473 return; 474 if (controls[i].flags == current_control_flags) { 475 focus_control_index = i; 476 return; 477 } 478 } 479 } 480 focus_control_index = 0; 481 } 482 483 void free_controls(void) 484 { 485 unsigned int i; 486 487 for (i = 0; i < controls_count; ++i) 488 free(controls[i].name); 489 free(controls); 490 controls = NULL; 491 controls_count = 0; 492 } 493 494 void create_controls(void) 495 { 496 snd_mixer_elem_t *elem; 497 struct control *control; 498 499 free_controls(); 500 501 for (elem = snd_mixer_first_elem(mixer); 502 elem; 503 elem = snd_mixer_elem_next(elem)) 504 controls_count += get_controls_count_for_elem(elem); 505 506 if (controls_count > 0) { 507 controls = ccalloc(controls_count, sizeof *controls); 508 control = controls; 509 for (elem = snd_mixer_first_elem(mixer); 510 elem; 511 elem = snd_mixer_elem_next(elem)) 512 control += create_controls_for_elem(elem, control); 513 assert(control == controls + controls_count); 514 } 515 516 compute_controls_layout(); 517 display_view_mode(); 518 519 search_for_focus_control(); 520 refocus_control(); 521 }