tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

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 }