/*
** mix.c - Command-line driven volume control for Linux.
** Copyright (C) 2003 Ryan McCabe <ryan@numb.org>
**
** xmms provides a means to control it (pause, stop, play, fwd, back, etc.)
** via the command line.  I have key bindings set in my window manager to do
** these things.  It doesn't let you adjust the volume via the command line
** interface.  I'd like to be able to do that with window manager key binds.
** Hence, this program.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License, version 2,
** as published by the Free Software Foundation.
*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/soundcard.h>

int open_mixer(void) {
	return (open("/dev/mixer", O_RDWR | O_NONBLOCK));
}

int get_level(int fd, int device) {
	int level = -1;

	if (ioctl(fd, MIXER_READ(device), &level) != 0)
		return (-1);

	return (level / 257);
}

int set_level(int fd, int device, int level) {
	if (level > 100)
		level = 100;
	else if (level < 0)
		level = 0;

	level *= 257;

	return (ioctl(fd, MIXER_WRITE(device), &level));
}

void print_help(void) {
	printf("Syntax: mix [options]\n\
-i <integer>  Increment by the specified percentage when raising or lowering the volume.\n\
-u            Raise the volume.\n\
-d            Lower the volume.\n\
-p            Use the PCM volume level instead of the master volume level.\n\
-s <integer>  Set the volume level to the specified percent level.\n\
-h            Print this help text and exit.\n\
-v            Print version information and exit.\n");
}

int main(int argc, char **argv) {
	int fd;
	int opt;
	int device = SOUND_MIXER_VOLUME;
	int level = -1;
	int adjustment = 0;
	int increment = 5;

	while ((opt = getopt(argc, argv, "hvi:udps:")) != EOF) {
		switch (opt) {
			case 'i':
			{
				char *end;

				if (optarg == NULL) {
					fprintf(stderr, "No increment given");
					exit(-1);
				}

				increment = strtol(optarg, &end, 10);
				if (increment == -1) {
					fprintf(stderr, "Invalid increment given: %s\n", optarg);
					exit(-1);
				}

				break;
			}

			case 'u':
				adjustment = 1;
				break;

			case 'd':
				adjustment = -1;
				break;

			case 'p':
				device = SOUND_MIXER_PCM;
				break;

			case 's':
			{
				char *end;

				if (optarg == NULL) {
					fprintf(stderr, "No volume level specified\n");
					exit(-1);
				}

				level = strtol(optarg, &end, 10);
				if (*end != '\0') {
					fprintf(stderr, "Invalid volume level: %s\n", optarg);
					exit(-1);
				}

				break;
			}

			case 'h':
				print_help();
				exit(0);

			case 'v':
				printf("mix v1.0 by Ryan McCabe <ryan@numb.org>\n");
				exit(0);
		}
	}

	fd = open_mixer();
	if (fd == -1)
		exit(-1);

	if (adjustment != 0) {
		level = get_level(fd, device);
		if (level == -1)
			exit(-1);

		level += adjustment * increment;
	}

	if (level != -1)
		exit(set_level(fd, device, level));

	exit(0);
}
