~ubuntu-branches/ubuntu/natty/alsa-utils/natty

« back to all changes in this revision

Viewing changes to alsamixer/mixer_controls.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich, Daniel T Chen
  • Date: 2009-11-06 11:29:35 UTC
  • mfrom: (2.3.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091106112935-stzy6l7u9auw6hm0
Tags: 1.0.21-1ubuntu1
* Merge from debian unstable, remaining changes:
  - debian/init:
    + wait until /usr/bin and /var/lib/alsa exist
    + only display an error when dealing with alsactl if there is no card
      specified
    + Set sane level for 'Speaker' and 'Headphone', needed for Dell Mini 9
      and Dell E series
    + ute PC Beep on hda cards that support it during initial volume setup
    + update lsb header to indicate no running of the script unless the
      udev rule is run
    + Mute *Analog/Digital Control for Creative cards by default
    + Default Digital Input Source to be Digital Mic 1 so that users
      with digital mic will be able to use it out of the box
    + Make use of lsb-functions/log calls
    + Mute "IEC958 Optical Raw" by default (LP: #408370)
  - debian/rules:
    + ship udev rules file in /lib/udev/rules.d
    + Do not install start symlinks for the alsa-utils init script, it gets
      run from a udev rule
  - debian/udev.script: do not use hotplug functions
  - debian/README.init.cs4236: Include in /usr/share/doc/alsa-utils so that
    users of snd-cs4236 (e.g., ThinkPad 600) can have audible sound
  - debian/patches/unset_pulse_internal.patch: We don't want alsamixer to
    show the pulse mixer by default, since it can be controlled from
    pulseaudio itself.
  - debian/patches/fix_misspelling_speaker-test_man_page.patch: Fix
    misspelling in speaker-test(1)
  - Set sane level for headphone 1 for Dell Studio XPS with 2.6.30.
  - Remove alsaconf from build system and remove po files

[ Daniel T Chen ]
"The beginning of the great initscript-sectomy"

* debian/alsa-mixer-save.upstart: Create an upstart job specifically
  saving mixer levels to resolve race (LP: #454265)
* debian/control: Version build-dep to upstart-aware debhelper.
* debian/init:
  + Revert all initscript changes in 1.0.20-2ubuntu[456]. They were
    crackful.
  + Restore more sane behavior/compatibility with 8.10 by not mucking
    with sound card state if alsactl restore fails.
  + Don't wait for 1 second after alsactl store, which already is
    expensive. Also, if store is going to fail, this wait is useless.
* debian/preinst: Handle upgrades from upstart-unaware versions.
* debian/rules: Move the former initscript into /sbin. We now have an
  upstart job just for handling alsactl store.
* debian/NOTES:
  debian/README.Debian:
  debian/modprobe-post-install-part:
  debian/udev.script: Use the new script path.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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>
 
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
}