Select Git revision
-
Martin Mareš authored
With osdd2, the layout is spacious enough per se.
Martin Mareš authoredWith osdd2, the layout is spacious enough per se.
osd-alsa.c 5.31 KiB
/*
* A Simple ALSA Volume Control via OSD
*
* (c) 2012 Martin Mares <mj@ucw.cz>
*/
#undef DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include "util.h"
#include "osd.h"
static char *alsa_device = "default";
static char *mixer_control = "Master";
static int adjust_by;
static int want_mute = -1;
static snd_mixer_t *mixer;
static snd_mixer_elem_t *elem;
static void init_mixer(void)
{
int err;
if (err = snd_mixer_open(&mixer, 0))
die("snd_mixer_open failed: error %d", err);
if (err = snd_mixer_attach(mixer, alsa_device))
die("snd_mixer_attach failed: error %d", err);
if (err = snd_mixer_selem_register(mixer, NULL, NULL))
die("snd_mixer_selem_register failed: error %d", err);
if (err = snd_mixer_load(mixer))
die("snd_mixer_load: error %d", err);
for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem))
{
const char *name = snd_mixer_selem_get_name(elem);
int index = snd_mixer_selem_get_index(elem);
if (!strcmp(name, mixer_control) || index)
{
if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE)
die("Unable to handle non-simple mixer controls");
if (!snd_mixer_selem_is_active(elem))
die("Selected mixer control is not active");
DBG("Found mixer control %s[%d]\n", name, index);
return;
}
}
die("Unable to find mixer control %s", mixer_control);
}
static int get_mute(void)
{
int mute_on = 0, mute_off = 0;
if (snd_mixer_selem_has_playback_switch(elem))
{
for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
{
int val;
if (!snd_mixer_selem_get_playback_switch(elem, ch, &val))
{
if (val)
mute_off++;
else
mute_on++;
}
}
}
DBG("Mute: on=%d off=%d\n", mute_on, mute_off);
return !!mute_on;
}
static long get_volume(long *pmin, long *pmax)
{
long min, max, curr=0;
if (!snd_mixer_selem_has_playback_volume(elem) ||
snd_mixer_selem_get_playback_volume_range(elem, &min, &max))
{
*pmin = *pmax = 0;
return 0;
}
for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
{
long val;
if (!snd_mixer_selem_get_playback_volume(elem, ch, &val))
{
if (val > curr)
curr = val;
}
}
DBG("Volume: min=%ld max=%ld curr=%ld\n", min, max, curr);
*pmin = min;
*pmax = max;
return curr;
}
static int vol_to_perc(long curr, long min, long max)
{
return (100LL * (curr-min) + (max-min)/2) / (max-min);
}
static long perc_to_vol(int perc, long min, long max)
{
return ((long long) perc * (max-min) + 50) / 100;
}
static void show_mixer(void)
{
long min, max;
long curr = get_volume(&min, &max);
int muted = get_mute();
struct osd_msg *msg = osd_new_msg();
osd_add_line(msg, "min-duration", "0");
char buf[256];
snprintf(buf, sizeof(buf), "%s volume", mixer_control);
osd_add_line(msg, NULL, buf);
if (muted)
osd_add_line(msg, NULL, "[mute]");
else if (min < max)
{
snprintf(buf, sizeof(buf), "%d", vol_to_perc(curr, min, max));
osd_add_line(msg, "slider", buf);
}
osd_send(msg);
}
static void set_mute(void)
{
if (want_mute < 0)
return;
if (want_mute == 2)
want_mute = !get_mute();
if (snd_mixer_selem_has_playback_switch(elem))
{
for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
snd_mixer_selem_set_playback_switch(elem, ch, !want_mute);
}
}
static void set_volume(void)
{
if (!adjust_by)
return;
long min, max;
long curr = get_volume(&min, &max);
int perc = vol_to_perc(curr, min, max);
DBG("Volume: have %d %ld\n", perc, curr);
perc += adjust_by;
if (perc < 0)
perc = 0;
if (perc > 100)
perc = 100;
curr = perc_to_vol(perc, min, max);
curr = min + (((long long) perc * (max-min) + 50) / 100);
DBG("Volume: want %d %ld\n", perc, curr);
for (snd_mixer_selem_channel_id_t ch=0; ch < SND_MIXER_SCHN_LAST; ch++)
snd_mixer_selem_set_playback_volume(elem, ch, curr);
}
static void NONRET
usage(void)
{
fprintf(stderr, "\
Usage: osd-alsa <options>\n\
\n\
Options:\n\
-a, --adjust=<percent> Adjust the control by a given amount\n\
-D, --device=<name> ALSA device (default: `default')\n\
-m, --mixer=<name> Name of mixer control (default: `Master')\n\
-0, --mute Mute the control\n\
-t, --toggle Mute/unmute the control\n\
-1, --unmute Unmute the control\n\
");
exit(1);
}
static const char short_opts[] = "01a:c:D:t";
static const struct option long_opts[] = {
{ "adjust", required_argument, NULL, 'a' },
{ "device", required_argument, NULL, 'D' },
{ "mixer", required_argument, NULL, 'm' },
{ "mute", no_argument, NULL, '0' },
{ "toggle", no_argument, NULL, 't' },
{ "unmute", no_argument, NULL, '1' },
{ NULL, 0, NULL, 0 },
};
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
switch (opt)
{
case '0':
want_mute = 1;
break;
case '1':
want_mute = 0;
break;
case 'a':
adjust_by = atoi(optarg);
break;
case 'D':
alsa_device = optarg;
break;
case 'm':
mixer_control = optarg;
break;
case 't':
want_mute = 2;
break;
default:
usage();
}
if (optind < argc)
usage();
init_mixer();
osd_init();
set_mute();
set_volume();
show_mixer();
return 0;
}