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