X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=osd-alsa.c;fp=osd-alsa.c;h=3d7bc2731c91eec80c47f2ca5355c2af342786bd;hb=476e4f464e0a94154aebb3f9f41d7e873304a8ed;hp=0000000000000000000000000000000000000000;hpb=fc0d5469ef0b6b13ff038023e83239375b74b607;p=osdd.git diff --git a/osd-alsa.c b/osd-alsa.c new file mode 100644 index 0000000..3d7bc27 --- /dev/null +++ b/osd-alsa.c @@ -0,0 +1,243 @@ +/* + * A Simple ALSA Volume Control via OSD + * + * (c) 2012 Martin Mares + */ + +#undef DEBUG + +#include +#include +#include +#include +#include + +#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); + osd_add_line(msg, NULL, ""); + 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 \n\ +\n\ +Options:\n\ +-a, --adjust= Adjust the control by a given amount\n\ +-D, --device= ALSA device (default: `default')\n\ +-m, --mixer= 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; +}