]> mj.ucw.cz Git - osdd.git/blob - osd-alsa.c
New display code is able to show text messages
[osdd.git] / osd-alsa.c
1 /*
2  *      A Simple ALSA Volume Control via OSD
3  *
4  *      (c) 2012 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef DEBUG
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <getopt.h>
13 #include <alsa/asoundlib.h>
14
15 #include "util.h"
16 #include "osd.h"
17
18 static char *alsa_device = "default";
19 static char *mixer_control = "Master";
20 static int adjust_by;
21 static int want_mute = -1;
22
23 static snd_mixer_t *mixer;
24 static snd_mixer_elem_t *elem;
25
26 static void init_mixer(void)
27 {
28   int err;
29
30   if (err = snd_mixer_open(&mixer, 0))
31     die("snd_mixer_open failed: error %d", err);
32
33   if (err = snd_mixer_attach(mixer, alsa_device))
34     die("snd_mixer_attach failed: error %d", err);
35
36   if (err = snd_mixer_selem_register(mixer, NULL, NULL))
37     die("snd_mixer_selem_register failed: error %d", err);
38
39   if (err = snd_mixer_load(mixer))
40     die("snd_mixer_load: error %d", err);
41
42   for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem))
43     {
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)
47         {
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);
53           return;
54         }
55     }
56
57   die("Unable to find mixer control %s", mixer_control);
58 }
59
60 static int get_mute(void)
61 {
62   int mute_on = 0, mute_off = 0;
63
64   if (snd_mixer_selem_has_playback_switch(elem))
65     {
66       for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
67         {
68           int val;
69           if (snd_mixer_selem_get_playback_switch(elem, ch, &val))
70             {
71               if (val)
72                 mute_off++;
73               else
74                 mute_on++;
75             }
76         }
77     }
78   DBG("Mute: on=%d off=%d\n", mute_on, mute_off);
79   return !!mute_on;
80 }
81
82 static long get_volume(long *pmin, long *pmax)
83 {
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))
87     {
88       *pmin = *pmax = 0;
89       return 0;
90     }
91
92   for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
93     {
94       long val;
95       if (snd_mixer_selem_get_playback_volume(elem, ch, &val))
96         {
97           if (val > curr)
98             curr = val;
99         }
100     }
101
102   DBG("Volume: min=%ld max=%ld curr=%ld\n", min, max, curr);
103   *pmin = min;
104   *pmax = max;
105   return curr;
106 }
107
108 static int vol_to_perc(long curr, long min, long max)
109 {
110   return (100LL * (curr-min) + (max-min)/2) / (max-min);
111 }
112
113 static long perc_to_vol(int perc, long min, long max)
114 {
115   return ((long long) perc * (max-min) + 50) / 100;
116 }
117
118 static void show_mixer(void)
119 {
120   long min, max;
121   long curr = get_volume(&min, &max);
122   int muted = get_mute();
123
124   struct osd_msg *msg = osd_new_msg();
125   osd_add_line(msg, "min-duration", "0");
126   char buf[256];
127   snprintf(buf, sizeof(buf), "%s volume", mixer_control);
128   osd_add_line(msg, NULL, buf);
129   osd_add_line(msg, NULL, "");
130   if (muted)
131     osd_add_line(msg, NULL, "[mute]");
132   else if (min < max)
133     {
134       snprintf(buf, sizeof(buf), "%d", vol_to_perc(curr, min, max));
135       osd_add_line(msg, "slider", buf);
136     }
137   osd_send(msg);
138 }
139
140
141 static void set_mute(void)
142 {
143   if (want_mute < 0)
144     return;
145
146   if (want_mute == 2)
147     want_mute = !get_mute();
148
149   if (snd_mixer_selem_has_playback_switch(elem))
150     {
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);
153     }
154 }
155
156 static void set_volume(void)
157 {
158   if (!adjust_by)
159     return;
160
161   long min, max;
162   long curr = get_volume(&min, &max);
163   int perc = vol_to_perc(curr, min, max);
164
165   DBG("Volume: have %d %ld\n", perc, curr);
166   perc += adjust_by;
167   if (perc < 0)
168     perc = 0;
169   if (perc > 100)
170     perc = 100;
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);
174
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);
177 }
178
179 static void NONRET
180 usage(void)
181 {
182   fprintf(stderr, "\
183 Usage: osd-alsa <options>\n\
184 \n\
185 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\
192 ");
193   exit(1);
194 }
195
196 static const char short_opts[] = "01a:c:D:t";
197
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   },
206 };
207
208 int main(int argc, char **argv)
209 {
210   int opt;
211   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
212     switch (opt)
213       {
214       case '0':
215         want_mute = 1;
216         break;
217       case '1':
218         want_mute = 0;
219         break;
220       case 'a':
221         adjust_by = atoi(optarg);
222         break;
223       case 'D':
224         alsa_device = optarg;
225         break;
226       case 'm':
227         mixer_control = optarg;
228         break;
229       case 't':
230         want_mute = 2;
231         break;
232       default:
233         usage();
234       }
235   if (optind < argc)
236     usage();
237
238   init_mixer();
239   osd_init();
240   set_mute();
241   set_volume();
242   show_mixer();
243   return 0;
244 }