2 * A Simple ALSA Volume Control via OSD
4 * (c) 2012 Martin Mares <mj@ucw.cz>
13 #include <alsa/asoundlib.h>
18 static char *alsa_device = "default";
19 static char *mixer_control = "Master";
21 static int want_mute = -1;
23 static snd_mixer_t *mixer;
24 static snd_mixer_elem_t *elem;
26 static void init_mixer(void)
30 if (err = snd_mixer_open(&mixer, 0))
31 die("snd_mixer_open failed: error %d", err);
33 if (err = snd_mixer_attach(mixer, alsa_device))
34 die("snd_mixer_attach failed: error %d", err);
36 if (err = snd_mixer_selem_register(mixer, NULL, NULL))
37 die("snd_mixer_selem_register failed: error %d", err);
39 if (err = snd_mixer_load(mixer))
40 die("snd_mixer_load: error %d", err);
42 for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem))
44 const char *name = snd_mixer_selem_get_name(elem);
45 int index = snd_mixer_selem_get_index(elem);
46 if (!strcmp(name, mixer_control) || index)
48 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
49 die("Unable to handle non-simple mixer controls");
50 if (!snd_mixer_selem_is_active(elem))
51 die("Selected mixer control is not active");
52 DBG("Found mixer control %s[%d]\n", name, index);
57 die("Unable to find mixer control %s", mixer_control);
60 static int get_mute(void)
62 int mute_on = 0, mute_off = 0;
64 if (snd_mixer_selem_has_playback_switch(elem))
66 for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
69 if (snd_mixer_selem_get_playback_switch(elem, ch, &val))
78 DBG("Mute: on=%d off=%d\n", mute_on, mute_off);
82 static long get_volume(long *pmin, long *pmax)
84 long min, max, curr=0;
85 if (!snd_mixer_selem_has_playback_volume(elem) ||
86 snd_mixer_selem_get_playback_volume_range(elem, &min, &max))
92 for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
95 if (snd_mixer_selem_get_playback_volume(elem, ch, &val))
102 DBG("Volume: min=%ld max=%ld curr=%ld\n", min, max, curr);
108 static int vol_to_perc(long curr, long min, long max)
110 return (100LL * (curr-min) + (max-min)/2) / (max-min);
113 static long perc_to_vol(int perc, long min, long max)
115 return ((long long) perc * (max-min) + 50) / 100;
118 static void show_mixer(void)
121 long curr = get_volume(&min, &max);
122 int muted = get_mute();
124 struct osd_msg *msg = osd_new_msg();
125 osd_add_line(msg, "min-duration", "0");
127 snprintf(buf, sizeof(buf), "%s volume", mixer_control);
128 osd_add_line(msg, NULL, buf);
129 osd_add_line(msg, NULL, "");
131 osd_add_line(msg, NULL, "[mute]");
134 snprintf(buf, sizeof(buf), "%d", vol_to_perc(curr, min, max));
135 osd_add_line(msg, "slider", buf);
141 static void set_mute(void)
147 want_mute = !get_mute();
149 if (snd_mixer_selem_has_playback_switch(elem))
151 for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
152 snd_mixer_selem_set_playback_switch(elem, ch, !want_mute);
156 static void set_volume(void)
162 long curr = get_volume(&min, &max);
163 int perc = vol_to_perc(curr, min, max);
165 DBG("Volume: have %d %ld\n", perc, curr);
171 curr = perc_to_vol(perc, min, max);
172 curr = min + (((long long) perc * (max-min) + 50) / 100);
173 DBG("Volume: want %d %ld\n", perc, curr);
175 for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
176 snd_mixer_selem_set_playback_volume(elem, ch, curr);
183 Usage: osd-alsa <options>\n\
186 -a, --adjust=<percent> Adjust the control by a given amount\n\
187 -D, --device=<name> ALSA device (default: `default')\n\
188 -m, --mixer=<name> Name of mixer control (default: `Master')\n\
189 -0, --mute Mute the control\n\
190 -t, --toggle Mute/unmute the control\n\
191 -1, --unmute Unmute the control\n\
196 static const char short_opts[] = "01a:c:D:t";
198 static const struct option long_opts[] = {
199 { "adjust", required_argument, NULL, 'a' },
200 { "device", required_argument, NULL, 'D' },
201 { "mixer", required_argument, NULL, 'm' },
202 { "mute", no_argument, NULL, '0' },
203 { "toggle", no_argument, NULL, 't' },
204 { "unmute", no_argument, NULL, '1' },
205 { NULL, 0, NULL, 0 },
208 int main(int argc, char **argv)
211 while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
221 adjust_by = atoi(optarg);
224 alsa_device = optarg;
227 mixer_control = optarg;