Skip to content
Snippets Groups Projects
Commit 476e4f46 authored by Martin Mareš's avatar Martin Mareš
Browse files

osd-alsa: A new client for adjusting ALSA mixer controls

parent fc0d5469
Branches
No related tags found
No related merge requests found
......@@ -3,20 +3,24 @@ ARCHIVE=osdd-$(VERSION).tar.gz
CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99
all: osdd osdc osd-batt
all: osdd osdc osd-batt osd-alsa
osdd: osdd.o util.o
osdc: osdc.o util.o client.o
osd-batt: osd-batt.o util.o client.o
osd-alsa: osd-alsa.o util.o client.o
osdd.o: CFLAGS+=$(shell xosd-config --cflags)
osdd: LDFLAGS+=$(shell xosd-config --libs)
osdd: LDLIBS+=$(shell xosd-config --libs)
osdc: LDFLAGS+=-lX11
osd-batt: LDFLAGS+=-lX11
osdc: LDLIBS+=-lX11
osd-batt: LDLIBS+=-lX11
osd-alsa.o: CFLAGS+=$(shell pkg-config --cflags alsa)
osd-alsa: LDLIBS+=$(shell pkg-config --libs alsa) -lX11
clean:
rm -f *~ *.o TAGS core osdd osdc osd-batt
rm -f *~ *.o TAGS core osdd osdc osd-batt osd-alsa
release:
git tag v$(VERSION)
......
......@@ -69,3 +69,5 @@ osd-batt A slightly more complex client written in C, showing
the current status of laptop batteries. It can be asked
to show status immediately or to run in background and
croak whenever the battery is low.
osd-alsa Adjust volume of an ALSA mixer channel. Also written in C.
/*
* 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 "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 <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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment