• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
are_there_any_controls(void)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 
has_more_than_front_capture_channels(snd_mixer_elem_t * elem)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 
has_any_control_channel(snd_mixer_elem_t * elem,const snd_mixer_selem_channel_id_t channels[2],int (* has_channel)(snd_mixer_elem_t *,snd_mixer_selem_channel_id_t))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 
has_merged_cswitch(snd_mixer_elem_t * elem)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 
get_playback_controls_count(snd_mixer_elem_t * elem)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 
get_capture_controls_count(snd_mixer_elem_t * elem)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 
get_controls_count_for_elem(snd_mixer_elem_t * elem)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 
create_name(struct control * control)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 
create_controls_for_elem(snd_mixer_elem_t * elem,struct control * control)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 enum_index;
220 	struct control *front_control = NULL;
221 	bool has_pvol, has_psw;
222 	bool has_cvol, has_csw;
223 	bool has_channel[LAST_SUPPORTED_CHANNEL + 1];
224 	bool merged_cswitch;
225 	bool has_ch0, has_ch1;
226 
227 	if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
228 		return 0;
229 	if (snd_mixer_selem_is_enumerated(elem)) {
230 		if ((view_mode == VIEW_MODE_PLAYBACK && snd_mixer_selem_is_enum_capture(elem)) ||
231 		    (view_mode == VIEW_MODE_CAPTURE && !snd_mixer_selem_is_enum_capture(elem)))
232 			return 0;
233 		control->elem = elem;
234 		control->flags = TYPE_ENUM;
235 		control->enum_channel_bits = 0;
236 		for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
237 			if (snd_mixer_selem_get_enum_item(control->elem, (snd_mixer_selem_channel_id_t)i, &enum_index) >= 0)
238 				control->enum_channel_bits |= 1 << i;
239 		if (snd_mixer_selem_is_active(control->elem))
240 			control->flags |= IS_ACTIVE;
241 		create_name(control);
242 		return 1;
243 	}
244 	has_pvol = snd_mixer_selem_has_playback_volume(elem);
245 	has_psw = snd_mixer_selem_has_playback_switch(elem);
246 	has_cvol = snd_mixer_selem_has_capture_volume(elem);
247 	has_csw = snd_mixer_selem_has_capture_switch(elem);
248 	merged_cswitch = view_mode == VIEW_MODE_ALL && has_merged_cswitch(elem);
249 	if (view_mode != VIEW_MODE_CAPTURE && (has_pvol || has_psw)) {
250 		if ((!has_pvol || snd_mixer_selem_has_playback_volume_joined(elem)) &&
251 		    (!has_psw || snd_mixer_selem_has_playback_switch_joined(elem))) {
252 			control->elem = elem;
253 			if (has_pvol) {
254 				control->flags |= TYPE_PVOLUME | HAS_VOLUME_0;
255 				control->volume_channels[0] = 0;
256 			}
257 			if (has_psw) {
258 				control->flags |= TYPE_PSWITCH | HAS_PSWITCH_0;
259 				control->pswitch_channels[0] = 0;
260 			}
261 			if (merged_cswitch) {
262 				control->flags |= TYPE_CSWITCH;
263 				if (snd_mixer_selem_has_capture_switch_joined(elem)) {
264 					control->flags |= HAS_CSWITCH_0;
265 					control->cswitch_channels[0] = 0;
266 				} else {
267 					if (snd_mixer_selem_has_capture_channel(elem, control_channels[0][0])) {
268 						control->flags |= HAS_CSWITCH_0;
269 						control->cswitch_channels[0] = control_channels[0][0];
270 					}
271 					if (control_channels[0][1] != SND_MIXER_SCHN_UNKNOWN &&
272 					    snd_mixer_selem_has_capture_channel(elem, control_channels[0][1])) {
273 						control->flags |= HAS_CSWITCH_1;
274 						control->cswitch_channels[1] = control_channels[0][1];
275 					}
276 				}
277 				if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) {
278 					control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1;
279 					control->cswitch_channels[0] = control->cswitch_channels[1];
280 				}
281 			}
282 			if (snd_mixer_selem_is_active(control->elem))
283 				control->flags |= IS_ACTIVE;
284 			create_name(control);
285 			++control;
286 			++count;
287 		} else {
288 			for (i = 0; i < ARRAY_SIZE(supported_channels); ++i)
289 				has_channel[supported_channels[i]] =
290 					snd_mixer_selem_has_playback_channel(elem, supported_channels[i]);
291 			for (i = 0; i < ARRAY_SIZE(control_channels); ++i) {
292 				has_ch0 = has_channel[control_channels[i][0]];
293 				has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN &&
294 					has_channel[control_channels[i][1]];
295 				if (!has_ch0 && !has_ch1)
296 					continue;
297 				control->elem = elem;
298 				if (has_pvol) {
299 					control->flags |= TYPE_PVOLUME;
300 					if (snd_mixer_selem_has_playback_volume_joined(elem)) {
301 						control->flags |= HAS_VOLUME_0;
302 						control->volume_channels[0] = 0;
303 					} else {
304 						if (has_ch0) {
305 							control->flags |= HAS_VOLUME_0;
306 							control->volume_channels[0] = control_channels[i][0];
307 						}
308 						if (has_ch1) {
309 							control->flags |= HAS_VOLUME_1;
310 							control->volume_channels[1] = control_channels[i][1];
311 						}
312 					}
313 				}
314 				if (has_psw) {
315 					control->flags |= TYPE_PSWITCH;
316 					if (snd_mixer_selem_has_playback_switch_joined(elem)) {
317 						control->flags |= HAS_PSWITCH_0;
318 						control->pswitch_channels[0] = 0;
319 					} else {
320 						if (has_ch0) {
321 							control->flags |= HAS_PSWITCH_0;
322 							control->pswitch_channels[0] = control_channels[i][0];
323 						}
324 						if (has_ch1) {
325 							control->flags |= HAS_PSWITCH_1;
326 							control->pswitch_channels[1] = control_channels[i][1];
327 						}
328 					}
329 				}
330 				if (merged_cswitch) {
331 					control->flags |= TYPE_CSWITCH;
332 					if (snd_mixer_selem_has_capture_switch_joined(elem)) {
333 						control->flags |= HAS_CSWITCH_0;
334 						control->cswitch_channels[0] = 0;
335 					} else {
336 						if (snd_mixer_selem_has_capture_channel(elem, control_channels[i][0])) {
337 							control->flags |= HAS_CSWITCH_0;
338 							control->cswitch_channels[0] = control_channels[i][0];
339 						}
340 						if (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN &&
341 						    snd_mixer_selem_has_capture_channel(elem, control_channels[i][1])) {
342 							control->flags |= HAS_CSWITCH_1;
343 							control->cswitch_channels[1] = control_channels[i][1];
344 						}
345 					}
346 				}
347 				if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) {
348 					control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1;
349 					control->volume_channels[0] = control->volume_channels[1];
350 				}
351 				if ((control->flags & (HAS_PSWITCH_0 | HAS_PSWITCH_1)) == HAS_PSWITCH_1) {
352 					control->flags ^= HAS_PSWITCH_0 | HAS_PSWITCH_1;
353 					control->pswitch_channels[0] = control->pswitch_channels[1];
354 				}
355 				if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) {
356 					control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1;
357 					control->cswitch_channels[0] = control->cswitch_channels[1];
358 				}
359 				if (snd_mixer_selem_is_active(control->elem))
360 					control->flags |= IS_ACTIVE;
361 				create_name(control);
362 				if (i == 0)
363 					front_control = control;
364 				else {
365 					front_control->flags |= IS_MULTICH | 0;
366 					control->flags |= IS_MULTICH | i;
367 				}
368 				++control;
369 				++count;
370 			}
371 		}
372 	}
373 	if (view_mode != VIEW_MODE_PLAYBACK && (has_cvol || has_csw) && !merged_cswitch) {
374 		if ((!has_cvol || snd_mixer_selem_has_capture_volume_joined(elem)) &&
375 		    (!has_csw || snd_mixer_selem_has_capture_switch_joined(elem))) {
376 			control->elem = elem;
377 			if (has_cvol) {
378 				control->flags |= TYPE_CVOLUME | HAS_VOLUME_0;
379 				control->volume_channels[0] = 0;
380 			}
381 			if (has_csw) {
382 				control->flags |= TYPE_CSWITCH | HAS_CSWITCH_0;
383 				control->cswitch_channels[0] = 0;
384 			}
385 			if (snd_mixer_selem_is_active(control->elem))
386 				control->flags |= IS_ACTIVE;
387 			create_name(control);
388 			++control;
389 			++count;
390 		} else {
391 			for (i = 0; i < ARRAY_SIZE(supported_channels); ++i)
392 				has_channel[supported_channels[i]] =
393 					snd_mixer_selem_has_capture_channel(elem, supported_channels[i]);
394 			for (i = 0; i < ARRAY_SIZE(control_channels); ++i) {
395 				has_ch0 = has_channel[control_channels[i][0]];
396 				has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN &&
397 					has_channel[control_channels[i][1]];
398 				if (!has_ch0 && !has_ch1)
399 					continue;
400 				control->elem = elem;
401 				if (has_cvol) {
402 					control->flags |= TYPE_CVOLUME;
403 					if (snd_mixer_selem_has_capture_volume_joined(elem)) {
404 						control->flags |= HAS_VOLUME_0;
405 						control->volume_channels[0] = 0;
406 					} else {
407 						if (has_ch0) {
408 							control->flags |= HAS_VOLUME_0;
409 							control->volume_channels[0] = control_channels[i][0];
410 						}
411 						if (has_ch1) {
412 							control->flags |= HAS_VOLUME_1;
413 							control->volume_channels[1] = control_channels[i][1];
414 						}
415 					}
416 				}
417 				if (has_csw) {
418 					control->flags |= TYPE_CSWITCH;
419 					if (snd_mixer_selem_has_capture_switch_joined(elem)) {
420 						control->flags |= HAS_CSWITCH_0;
421 						control->cswitch_channels[0] = 0;
422 					} else {
423 						if (has_ch0) {
424 							control->flags |= HAS_CSWITCH_0;
425 							control->cswitch_channels[0] = control_channels[i][0];
426 						}
427 						if (has_ch1) {
428 							control->flags |= HAS_CSWITCH_1;
429 							control->cswitch_channels[1] = control_channels[i][1];
430 						}
431 					}
432 				}
433 				if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) {
434 					control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1;
435 					control->volume_channels[0] = control->volume_channels[1];
436 				}
437 				if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) {
438 					control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1;
439 					control->cswitch_channels[0] = control->cswitch_channels[1];
440 				}
441 				if (snd_mixer_selem_is_active(control->elem))
442 					control->flags |= IS_ACTIVE;
443 				create_name(control);
444 				if (i == 0)
445 					front_control = control;
446 				else {
447 					front_control->flags |= IS_MULTICH | 0;
448 					control->flags |= IS_MULTICH | i;
449 				}
450 				++control;
451 				++count;
452 			}
453 		}
454 	}
455 	return count;
456 }
457 
search_for_focus_control(void)458 static void search_for_focus_control(void)
459 {
460 	snd_mixer_elem_t *elem;
461 	unsigned int i;
462 
463 	elem = snd_mixer_find_selem(mixer, current_selem_id);
464 	if (elem)
465 		for (i = 0; i < controls_count; ++i)
466 			if (controls[i].elem == elem) {
467 				focus_control_index = i;
468 				for (;;) {
469 					++i;
470 					if (i >= controls_count || controls[i].elem != elem)
471 						return;
472 					if (controls[i].flags == current_control_flags) {
473 						focus_control_index = i;
474 						return;
475 					}
476 				}
477 			}
478 	focus_control_index = 0;
479 }
480 
free_controls(void)481 void free_controls(void)
482 {
483 	unsigned int i;
484 
485 	for (i = 0; i < controls_count; ++i)
486 		free(controls[i].name);
487 	free(controls);
488 	controls = NULL;
489 	controls_count = 0;
490 }
491 
create_controls(void)492 void create_controls(void)
493 {
494 	snd_mixer_elem_t *elem;
495 	struct control *control;
496 
497 	free_controls();
498 
499 	for (elem = snd_mixer_first_elem(mixer);
500 	     elem;
501 	     elem = snd_mixer_elem_next(elem))
502 		controls_count += get_controls_count_for_elem(elem);
503 
504 	if (controls_count > 0) {
505 		controls = ccalloc(controls_count, sizeof *controls);
506 		control = controls;
507 		for (elem = snd_mixer_first_elem(mixer);
508 		     elem;
509 		     elem = snd_mixer_elem_next(elem))
510 			control += create_controls_for_elem(elem, control);
511 		assert(control == controls + controls_count);
512 	}
513 
514 	compute_controls_layout();
515 	display_view_mode();
516 
517 	search_for_focus_control();
518 	refocus_control();
519 }
520