2
* mixer_controls.c - handles mixer controls and mapping from selems
3
* Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
4
* Jaroslav Kysela <perex@perex.cz>
5
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
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.
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.
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/>.
26
#include <alsa/asoundlib.h>
29
#include "mixer_display.h"
30
#include "mixer_widget.h"
31
#include "mixer_controls.h"
33
struct control *controls;
34
unsigned int controls_count;
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,
46
#define LAST_SUPPORTED_CHANNEL SND_MIXER_SCHN_SIDE_RIGHT
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 },
56
bool are_there_any_controls(void)
58
snd_mixer_elem_t *elem;
61
for (elem = snd_mixer_first_elem(mixer);
63
elem = snd_mixer_elem_next(elem)) {
64
if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
66
if (snd_mixer_selem_is_enumerated(elem))
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))
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]))
81
static bool has_more_than_front_capture_channels(snd_mixer_elem_t *elem)
85
for (i = 2; i < ARRAY_SIZE(supported_channels); ++i)
86
if (snd_mixer_selem_has_capture_channel(elem, supported_channels[i]))
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))
95
return has_channel(elem, channels[0]) ||
96
(channels[1] != SND_MIXER_SCHN_UNKNOWN && has_channel(elem, channels[1]));
99
static bool has_merged_cswitch(snd_mixer_elem_t *elem)
104
pvol = snd_mixer_selem_has_playback_volume(elem);
105
psw = snd_mixer_selem_has_playback_switch(elem);
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))
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))
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))
125
static unsigned int get_playback_controls_count(snd_mixer_elem_t *elem)
127
unsigned int count = 0;
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)
135
if ((!has_vol || snd_mixer_selem_has_playback_volume_joined(elem)) &&
136
(!has_sw || snd_mixer_selem_has_playback_switch_joined(elem)))
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])))
147
static unsigned int get_capture_controls_count(snd_mixer_elem_t *elem)
149
unsigned int count = 0;
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)))
158
if ((!has_vol || snd_mixer_selem_has_capture_volume_joined(elem)) &&
159
(!has_sw || snd_mixer_selem_has_capture_switch_joined(elem)))
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])))
170
static unsigned int get_controls_count_for_elem(snd_mixer_elem_t *elem)
174
if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
176
if (snd_mixer_selem_is_enumerated(elem)) {
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;
188
case VIEW_MODE_PLAYBACK:
189
return get_playback_controls_count(elem);
190
case VIEW_MODE_CAPTURE:
191
return get_capture_controls_count(elem);
194
p = get_playback_controls_count(elem);
195
c = get_capture_controls_count(elem);
196
return has_merged_cswitch(elem) ? p : p + c;
200
static void create_name(struct control *control)
205
index = snd_mixer_selem_get_index(control->elem);
207
control->name = casprintf("%s %u", snd_mixer_selem_get_name(control->elem), index);
209
control->name = cstrdup(snd_mixer_selem_get_name(control->elem));
211
while ((s = strstr(control->name, "IEC958")) != NULL)
212
memcpy(s, "S/PDIF", 6);
215
static unsigned int create_controls_for_elem(snd_mixer_elem_t *elem, struct control *control)
217
unsigned int count = 0;
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];
226
bool has_ch0, has_ch1;
228
if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
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)))
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);
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;
255
control->flags |= TYPE_PVOLUME | HAS_VOLUME_0;
256
control->volume_channels[0] = 0;
259
control->flags |= TYPE_PSWITCH | HAS_PSWITCH_0;
260
control->pswitch_channels[0] = 0;
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;
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];
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];
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];
283
if (snd_mixer_selem_is_active(control->elem))
284
control->flags |= IS_ACTIVE;
285
create_name(control);
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)
299
control->elem = elem;
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;
307
control->flags |= HAS_VOLUME_0;
308
control->volume_channels[0] = control_channels[i][0];
311
control->flags |= HAS_VOLUME_1;
312
control->volume_channels[1] = control_channels[i][1];
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;
323
control->flags |= HAS_PSWITCH_0;
324
control->pswitch_channels[0] = control_channels[i][0];
327
control->flags |= HAS_PSWITCH_1;
328
control->pswitch_channels[1] = control_channels[i][1];
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;
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];
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];
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];
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];
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];
361
if (snd_mixer_selem_is_active(control->elem))
362
control->flags |= IS_ACTIVE;
363
create_name(control);
365
front_control = control;
367
front_control->flags |= IS_MULTICH | 0;
368
control->flags |= IS_MULTICH | i;
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;
380
control->flags |= TYPE_CVOLUME | HAS_VOLUME_0;
381
control->volume_channels[0] = 0;
384
control->flags |= TYPE_CSWITCH | HAS_CSWITCH_0;
385
control->cswitch_channels[0] = 0;
387
if (snd_mixer_selem_is_active(control->elem))
388
control->flags |= IS_ACTIVE;
389
create_name(control);
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)
402
control->elem = elem;
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;
410
control->flags |= HAS_VOLUME_0;
411
control->volume_channels[0] = control_channels[i][0];
414
control->flags |= HAS_VOLUME_1;
415
control->volume_channels[1] = control_channels[i][1];
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;
426
control->flags |= HAS_CSWITCH_0;
427
control->cswitch_channels[0] = control_channels[i][0];
430
control->flags |= HAS_CSWITCH_1;
431
control->cswitch_channels[1] = control_channels[i][1];
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];
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];
443
if (snd_mixer_selem_is_active(control->elem))
444
control->flags |= IS_ACTIVE;
445
create_name(control);
447
front_control = control;
449
front_control->flags |= IS_MULTICH | 0;
450
control->flags |= IS_MULTICH | i;
460
static void search_for_focus_control(void)
462
snd_mixer_elem_t *elem;
465
elem = snd_mixer_find_selem(mixer, current_selem_id);
467
for (i = 0; i < controls_count; ++i)
468
if (controls[i].elem == elem) {
469
focus_control_index = i;
472
if (i >= controls_count || controls[i].elem != elem)
474
if (controls[i].flags == current_control_flags) {
475
focus_control_index = i;
480
focus_control_index = 0;
483
void free_controls(void)
487
for (i = 0; i < controls_count; ++i)
488
free(controls[i].name);
494
void create_controls(void)
496
snd_mixer_elem_t *elem;
497
struct control *control;
501
for (elem = snd_mixer_first_elem(mixer);
503
elem = snd_mixer_elem_next(elem))
504
controls_count += get_controls_count_for_elem(elem);
506
if (controls_count > 0) {
507
controls = ccalloc(controls_count, sizeof *controls);
509
for (elem = snd_mixer_first_elem(mixer);
511
elem = snd_mixer_elem_next(elem))
512
control += create_controls_for_elem(elem, control);
513
assert(control == controls + controls_count);
516
compute_controls_layout();
519
search_for_focus_control();