mix.c (5735B)
1 #include <err.h> 2 #include <stdio.h> 3 4 #include "util.h" 5 6 #ifdef __OpenBSD__ 7 #include <errno.h> 8 #include <poll.h> 9 #include <sndio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #define MAX_CHANNELS 16 14 15 static struct sioctl_hdl *hdl = NULL; 16 static struct pollfd *pfds = NULL; 17 18 static struct { 19 unsigned int addr; 20 int val; 21 } channel[MAX_CHANNELS]; 22 static size_t nchannels = 0; 23 24 static int 25 get_output(void) 26 { 27 size_t i; 28 int val; 29 30 if (nchannels == 0) 31 return 0; 32 33 val = 0; 34 for (i = 0; i < nchannels; i++) 35 val += channel[i].val; 36 return 100 * ((val / (double)nchannels) / 255.0); 37 } 38 39 static void 40 ondesc(void *arg, struct sioctl_desc *desc, int val) 41 { 42 size_t i; 43 44 if (desc == NULL) 45 return; 46 47 if (desc->type != SIOCTL_NUM || 48 strcmp(desc->func, "level") != 0 || 49 strcmp(desc->node0.name, "output") != 0) 50 return; 51 52 for (i = 0; i < nchannels; i++) 53 if (channel[i].addr == desc->addr) 54 break; 55 56 if (i < nchannels) { 57 channel[i].val = val; 58 return; 59 } 60 61 if (nchannels >= MAX_CHANNELS) { 62 warnx("too many channels"); 63 return; 64 } 65 channel[i].addr = desc->addr; 66 channel[i].val = val; 67 nchannels++; 68 } 69 70 static void 71 onval(void *arg, unsigned int addr, unsigned int val) 72 { 73 size_t i; 74 75 for (i = 0; i < nchannels; i++) 76 if (channel[i].addr == addr) { 77 channel[i].val = val; 78 break; 79 } 80 } 81 82 static int 83 do_init(void) 84 { 85 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); 86 if (hdl == NULL) { 87 warnx("sioctl_open %s", SIO_DEVANY); 88 return 0; 89 } 90 if (!sioctl_ondesc(hdl, ondesc, NULL)) { 91 warnx("sioctl_ondesc"); 92 sioctl_close(hdl); 93 return 0; 94 } 95 sioctl_onval(hdl, onval, NULL); 96 97 return 1; 98 } 99 100 static int 101 poll_peek(void) 102 { 103 int nfds, revents; 104 105 if (pfds == NULL) { 106 pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl)); 107 if (pfds == NULL) { 108 warnx("out of memory"); 109 goto out; 110 } 111 } 112 113 nfds = sioctl_pollfd(hdl, pfds, POLLIN); 114 if (nfds == 0) 115 return 1; 116 while (poll(pfds, nfds, 0) == -1) 117 if (errno != EINTR) { 118 warn("sioctl poll"); 119 goto out; 120 } 121 revents = sioctl_revents(hdl, pfds); 122 if (revents & POLLHUP) { 123 warnx("sioctl disconnected"); 124 goto out; 125 } 126 127 return 1; 128 129 out: 130 free(pfds); 131 pfds = NULL; 132 sioctl_close(hdl); 133 134 return 0; 135 } 136 137 int 138 mixread(void *arg, char *buf, size_t len) 139 { 140 static int init_done = 0; 141 struct pollfd *pfds; 142 int nfds; 143 144 if (!init_done) { 145 if (!do_init()) 146 return -1; 147 init_done = 1; 148 } 149 150 init_done = poll_peek(); 151 snprintf(buf, len, "%d%%", get_output()); 152 153 return 0; 154 } 155 #elif __linux__ && !USE_TINYALSA 156 #include <alsa/asoundlib.h> 157 158 static int active; 159 static int master; 160 161 int 162 mixer_elem_cb(snd_mixer_elem_t *elem, unsigned int mask) 163 { 164 long min, max, vol; 165 int r; 166 167 r = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_UNKNOWN, &active); 168 if (r < 0) { 169 warnx("snd_mixer_selem_get_playback_switch: %s", 170 snd_strerror(r)); 171 return -1; 172 } 173 DPRINTF_D(active); 174 r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max); 175 if (r < 0) { 176 warnx("snd_mixer_selem_get_playback_volume_range: %s", 177 snd_strerror(r)); 178 return -1; 179 } 180 r = snd_mixer_selem_get_playback_volume(elem, 181 SND_MIXER_SCHN_UNKNOWN, &vol); 182 if (r < 0) { 183 warnx("snd_mixer_selem_get_playback_volume: %s", 184 snd_strerror(r)); 185 return -1; 186 } 187 /* compute percentage */ 188 vol -= min; 189 max -= min; 190 if (max == 0) 191 master = 0; 192 else 193 master = 100 * vol / max; 194 DPRINTF_D(master); 195 return 0; 196 } 197 198 int 199 mixread(void *arg, char *buf, size_t len) 200 { 201 snd_mixer_selem_id_t *id; 202 snd_mixer_elem_t *elem; 203 static snd_mixer_t *mixerp; 204 struct pollfd pfd[1]; 205 int r; 206 207 snd_mixer_selem_id_alloca(&id); 208 snd_mixer_selem_id_set_name(id, "Master"); 209 snd_mixer_selem_id_set_index(id, 0); 210 211 if (mixerp != NULL) 212 goto readvol; 213 214 r = snd_mixer_open(&mixerp, O_RDONLY); 215 if (r < 0) { 216 warnx("snd_mixer_open: %s", snd_strerror(r)); 217 return -1; 218 } 219 r = snd_mixer_attach(mixerp, "default"); 220 if (r < 0) { 221 warnx("snd_mixer_attach: %s", snd_strerror(r)); 222 goto out; 223 } 224 r = snd_mixer_selem_register(mixerp, NULL, NULL); 225 if (r < 0) { 226 warnx("snd_mixer_selem_register: %s", snd_strerror(r)); 227 goto out; 228 } 229 r = snd_mixer_load(mixerp); 230 if (r < 0) { 231 warnx("snd_mixer_load: %s", snd_strerror(r)); 232 goto out; 233 } 234 elem = snd_mixer_find_selem(mixerp, id); 235 if (elem == NULL) { 236 warnx("could not find mixer element"); 237 goto out; 238 } 239 snd_mixer_elem_set_callback(elem, mixer_elem_cb); 240 /* force the callback the first time around */ 241 r = mixer_elem_cb(elem, 0); 242 if (r < 0) 243 goto out; 244 readvol: 245 r = snd_mixer_poll_descriptors(mixerp, pfd, LEN(pfd)); 246 if (r < 0) { 247 warnx("snd_mixer_poll_descriptors: %s", snd_strerror(r)); 248 goto out; 249 } 250 r = snd_mixer_handle_events(mixerp); 251 if (r < 0) { 252 warnx("snd_mixer_handle_events: %s", snd_strerror(r)); 253 goto out; 254 } 255 if (active) 256 snprintf(buf, len, "%d%%", master); 257 else 258 snprintf(buf, len, "!%d%%", master); 259 return 0; 260 out: 261 snd_mixer_free(mixerp); 262 snd_mixer_close(mixerp); 263 mixerp = NULL; 264 return -1; 265 } 266 #elif __linux__ && USE_TINYALSA 267 #include <tinyalsa/asoundlib.h> 268 269 int 270 mixread(void *arg, char *buf, size_t len) 271 { 272 static struct mixer *mixer; 273 struct mixer_ctl *ctl; 274 int cur, max; 275 276 if (mixer == NULL && (mixer = mixer_open(0)) == NULL) { 277 warnx("mixer_open() failed"); 278 return -1; 279 } 280 281 if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Switch")) 282 == NULL) { 283 warnx("mixer_get_ctl_by_name() failed"); 284 goto out; 285 } 286 if (!mixer_ctl_get_value(ctl, 0)) { 287 snprintf(buf, len, "mute"); 288 return 0; 289 } 290 291 if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Volume")) 292 == NULL) { 293 warnx("mixer_get_ctl_by_name() failed"); 294 goto out; 295 } 296 297 cur = mixer_ctl_get_value(ctl, 0); 298 max = mixer_ctl_get_range_max(ctl); 299 snprintf(buf, len, "%d%%", cur * 100 / max); 300 return 0; 301 302 out: 303 mixer_close(mixer); 304 mixer = NULL; 305 return -1; 306 } 307 #endif