16448Sache/*
26448Sache *	This is an example of a mixer program for Linux
36448Sache *
46448Sache *	updated 1/1/93 to add stereo, level query, broken
58857Srgrimes *      	devmask kludge - cmetz@thor.tjhsst.edu
66448Sache *
76448Sache * (C) Craig Metz and Hannu Savolainen 1993.
86448Sache *
96448Sache * You may do anything you wish with this program.
1029612Sjmg *
1129612Sjmg * ditto for my modifications (John-Mark Gurney, 1997)
126448Sache */
136448Sache
14114601Sobrien#include <sys/cdefs.h>
15114601Sobrien__FBSDID("$FreeBSD$");
1629963Scharnier
1729963Scharnier#include <err.h>
1829963Scharnier#include <fcntl.h>
19177195Sjkim#include <libgen.h>
20177195Sjkim#include <limits.h>
216448Sache#include <stdio.h>
226448Sache#include <string.h>
2329612Sjmg#include <stdlib.h>
2429612Sjmg#include <unistd.h>
256448Sache#include <sys/soundcard.h>
266448Sache
27177197Sjkimconst char	*names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
286448Sache
29177197Sjkimstatic void	usage(int devmask, int recmask);
30177197Sjkimstatic int	res_name(const char *name, int mask);
31177197Sjkimstatic void	print_recsrc(int recsrc, int recmask, int sflag);
326448Sache
33177197Sjkimstatic void
3429612Sjmgusage(int devmask, int recmask)
356448Sache{
36177197Sjkim	int	i, n;
376448Sache
38144999Smdodd	printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n"
39177197Sjkim	    "       mixer [-f device] [-s | -S] recsrc ...\n"
40177197Sjkim	    "       mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ...\n");
41177195Sjkim	if (devmask != 0) {
42177195Sjkim		printf(" devices: ");
43177195Sjkim		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
44177195Sjkim			if ((1 << i) & devmask)  {
45177195Sjkim				if (n)
46177195Sjkim					printf(", ");
47177195Sjkim				printf("%s", names[i]);
48177197Sjkim				n++;
49177195Sjkim			}
50177195Sjkim	}
51177195Sjkim	if (recmask != 0) {
52177195Sjkim		printf("\n rec devices: ");
53177195Sjkim		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
54177195Sjkim			if ((1 << i) & recmask)  {
55177195Sjkim				if (n)
56177195Sjkim					printf(", ");
57177195Sjkim				printf("%s", names[i]);
58177197Sjkim				n++;
59177195Sjkim			}
60177195Sjkim	}
6129612Sjmg	printf("\n");
626448Sache	exit(1);
636448Sache}
646448Sache
65177197Sjkimstatic int
6629612Sjmgres_name(const char *name, int mask)
676448Sache{
68177197Sjkim	int	foo;
6929612Sjmg
7029612Sjmg	for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
71177197Sjkim		if ((1 << foo) & mask && strcmp(names[foo], name) == 0)
7229612Sjmg			break;
7329612Sjmg
74177197Sjkim	return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo);
7529612Sjmg}
7629612Sjmg
77177197Sjkimstatic void
78177197Sjkimprint_recsrc(int recsrc, int recmask, int sflag)
7929612Sjmg{
80177197Sjkim	int	i, n;
816448Sache
82177195Sjkim	if (recmask == 0)
83177195Sjkim		return;
84177195Sjkim
85177197Sjkim	if (!sflag)
86153953Sariff		printf("Recording source: ");
87153953Sariff
88177197Sjkim	for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
896448Sache		if ((1 << i) & recsrc) {
90177197Sjkim			if (sflag)
91177197Sjkim				printf("%srec ", n ? " +" : "=");
92177197Sjkim			else if (n)
93153953Sariff				printf(", ");
94153953Sariff			printf("%s", names[i]);
95177197Sjkim			n++;
966448Sache		}
97177197Sjkim	if (!sflag)
98153953Sariff		printf("\n");
996448Sache}
1006448Sache
1018857Srgrimesint
1026448Sachemain(int argc, char *argv[])
1036448Sache{
104177197Sjkim	char	mixer[PATH_MAX] = "/dev/mixer";
105177197Sjkim	char	lstr[5], rstr[5];
106177197Sjkim	char	*name, *eptr;
107177197Sjkim	int	devmask = 0, recmask = 0, recsrc = 0, orecsrc;
108177197Sjkim	int	dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0;
109177197Sjkim	int	l, r, lrel, rrel;
110177197Sjkim	int	ch, foo, bar, baz, dev, m, n, t;
1116448Sache
112177195Sjkim	if ((name = strdup(basename(argv[0]))) == NULL)
113177195Sjkim		err(1, "strdup()");
114177195Sjkim	if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
115177195Sjkim		n = strtol(name + 5, &eptr, 10) - 1;
116177195Sjkim		if (n > 0 && *eptr == '\0')
117177195Sjkim			snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
118177195Sjkim	}
119177195Sjkim	free(name);
120177195Sjkim	name = mixer;
1216448Sache
122177195Sjkim	n = 1;
123177195Sjkim	for (;;) {
124177195Sjkim		if (n >= argc || *argv[n] != '-')
125177195Sjkim			break;
126177195Sjkim		if (strlen(argv[n]) != 2) {
127177195Sjkim			if (strcmp(argv[n] + 1, "rec") != 0)
12864657Ssobomax				dusage = 1;
129177195Sjkim			break;
13064657Ssobomax		}
131177195Sjkim		ch = *(argv[n] + 1);
132177195Sjkim		if (ch == 'f' && n < argc - 1) {
133177195Sjkim			name = argv[n + 1];
134177195Sjkim			n += 2;
135177195Sjkim		} else if (ch == 's') {
136177197Sjkim			sflag = 1;
137177195Sjkim			n++;
138177195Sjkim		} else if (ch == 'S') {
139177197Sjkim			Sflag = 1;
140177195Sjkim			n++;
141177195Sjkim		} else {
142177195Sjkim			dusage = 1;
143177195Sjkim			break;
144177195Sjkim		}
145177195Sjkim	}
146177197Sjkim	if (sflag && Sflag)
147177195Sjkim		dusage = 1;
14829612Sjmg
149177195Sjkim	argc -= n - 1;
150177195Sjkim	argv += n - 1;
151177195Sjkim
15229963Scharnier	if ((baz = open(name, O_RDWR)) < 0)
15329963Scharnier		err(1, "%s", name);
15429963Scharnier	if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
15530028Scharnier		err(1, "SOUND_MIXER_READ_DEVMASK");
15629963Scharnier	if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
15730028Scharnier		err(1, "SOUND_MIXER_READ_RECMASK");
15829963Scharnier	if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
15930028Scharnier		err(1, "SOUND_MIXER_READ_RECSRC");
16029612Sjmg	orecsrc = recsrc;
1616448Sache
162177197Sjkim	if (argc == 1 && dusage == 0) {
163177197Sjkim		for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
164177197Sjkim			if (!((1 << foo) & devmask))
16513803Smpp				continue;
166177197Sjkim			if (ioctl(baz, MIXER_READ(foo),&bar) == -1) {
16729963Scharnier			   	warn("MIXER_READ");
16813803Smpp				continue;
16913803Smpp			}
170177197Sjkim			if (Sflag || sflag) {
171177197Sjkim				printf("%s%s%c%d:%d", n ? " " : "",
172177197Sjkim				    names[foo], Sflag ? ':' : ' ',
173177197Sjkim				    bar & 0x7f, (bar >> 8) & 0x7f);
174177197Sjkim				n++;
175177197Sjkim			} else
176177197Sjkim				printf("Mixer %-8s is currently set to "
177177197Sjkim				    "%3d:%d\n", names[foo], bar & 0x7f,
178177197Sjkim				    (bar >> 8) & 0x7f);
17913803Smpp		}
180177197Sjkim		if (n && recmask)
181177197Sjkim			printf(" ");
182177198Sjkim		print_recsrc(recsrc, recmask, Sflag || sflag);
183177197Sjkim		return (0);
18413803Smpp	}
18513803Smpp
186177197Sjkim	argc--;
187177197Sjkim	argv++;
1886448Sache
189177197Sjkim	n = 0;
190177197Sjkim	while (argc > 0 && dusage == 0) {
191177197Sjkim		if (strcmp("recsrc", *argv) == 0) {
19229612Sjmg			drecsrc = 1;
193177197Sjkim			argc--;
194177197Sjkim			argv++;
19529612Sjmg			continue;
196230982Smav		} else if (strcmp("rec", *argv + 1) == 0) {
19729612Sjmg			if (**argv != '+' && **argv != '-' &&
19829612Sjmg			    **argv != '=' && **argv != '^') {
19964657Ssobomax				warnx("unknown modifier: %c", **argv);
20029612Sjmg				dusage = 1;
20164657Ssobomax				break;
20213435Smpp			}
203230982Smav			if (argc <= 1) {
204230982Smav				warnx("no recording device specified");
205230982Smav				dusage = 1;
206230982Smav				break;
207230982Smav			}
20829612Sjmg			if ((dev = res_name(argv[1], recmask)) == -1) {
20964657Ssobomax				warnx("unknown recording device: %s", argv[1]);
21029612Sjmg				dusage = 1;
21164657Ssobomax				break;
2126448Sache			}
213177197Sjkim			switch (**argv) {
21429612Sjmg			case '+':
2156448Sache				recsrc |= (1 << dev);
21629612Sjmg				break;
21729612Sjmg			case '-':
2186448Sache				recsrc &= ~(1 << dev);
21929612Sjmg				break;
22029612Sjmg			case '=':
22129612Sjmg				recsrc = (1 << dev);
22229612Sjmg				break;
22329612Sjmg			case '^':
22429612Sjmg				recsrc ^= (1 << dev);
22529612Sjmg				break;
22629612Sjmg			}
22729612Sjmg			drecsrc = 1;
228177197Sjkim			argc -= 2;
229177197Sjkim			argv += 2;
23029612Sjmg			continue;
23129612Sjmg		}
2326448Sache
233177197Sjkim		if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0)
23475334Sgreid			dev = 0;
235177197Sjkim		else if ((dev = res_name(*argv, devmask)) == -1) {
23664657Ssobomax			warnx("unknown device: %s", *argv);
23729612Sjmg			dusage = 1;
23864657Ssobomax			break;
23929612Sjmg		}
24029612Sjmg
241177197Sjkim		lrel = rrel = 0;
242113299Smdodd		if (argc > 1) {
243177197Sjkim			m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
244177197Sjkim			if (m > 0) {
245177197Sjkim				if (*lstr == '+' || *lstr == '-')
246113299Smdodd					lrel = rrel = 1;
247177197Sjkim				l = strtol(lstr, NULL, 10);
248113299Smdodd			}
249177197Sjkim			if (m > 1) {
250177197Sjkim				if (*rstr == '+' || *rstr == '-')
251113299Smdodd					rrel = 1;
252177197Sjkim				r = strtol(rstr, NULL, 10);
253113299Smdodd			}
254113299Smdodd		}
255113299Smdodd
256177197Sjkim		switch (argc > 1 ? m : t) {
25729612Sjmg		case 0:
258177197Sjkim			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
25929963Scharnier				warn("MIXER_READ");
260177197Sjkim				argc--;
261177197Sjkim				argv++;
26229612Sjmg				continue;
2636448Sache			}
264177197Sjkim			if (Sflag || sflag) {
265177197Sjkim				printf("%s%s%c%d:%d", n ? " " : "",
266177197Sjkim				    names[dev], Sflag ? ':' : ' ',
267177197Sjkim				    bar & 0x7f, (bar >> 8) & 0x7f);
268177197Sjkim				n++;
269177197Sjkim			} else
270177197Sjkim				printf("Mixer %-8s is currently set to "
271177197Sjkim				    "%3d:%d\n", names[dev], bar & 0x7f,
272177197Sjkim				    (bar >> 8) & 0x7f);
2736448Sache
274177197Sjkim			argc--;
275177197Sjkim			argv++;
27629612Sjmg			break;
27729612Sjmg		case 1:
27829612Sjmg			r = l;
279177197Sjkim			/* FALLTHROUGH */
28029612Sjmg		case 2:
281177197Sjkim			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
282113299Smdodd				warn("MIXER_READ");
283177197Sjkim				argc--;
284177197Sjkim				argv++;
285113299Smdodd				continue;
286113299Smdodd			}
287113299Smdodd
288113299Smdodd			if (lrel)
289113299Smdodd				l = (bar & 0x7f) + l;
290113299Smdodd			if (rrel)
291113299Smdodd				r = ((bar >> 8) & 0x7f) + r;
292113299Smdodd
29329612Sjmg			if (l < 0)
29429612Sjmg				l = 0;
29529612Sjmg			else if (l > 100)
29629612Sjmg				l = 100;
29729612Sjmg			if (r < 0)
29829612Sjmg				r = 0;
29929612Sjmg			else if (r > 100)
30029612Sjmg				r = 100;
3018857Srgrimes
302177197Sjkim			if (!Sflag)
303177197Sjkim				printf("Setting the mixer %s from %d:%d to "
304177197Sjkim				    "%d:%d.\n", names[dev], bar & 0x7f,
305177197Sjkim				    (bar >> 8) & 0x7f, l, r);
306108421Sjmallett
30729612Sjmg			l |= r << 8;
30829612Sjmg			if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
30929963Scharnier				warn("WRITE_MIXER");
3106448Sache
311177197Sjkim			argc -= 2;
312177197Sjkim			argv += 2;
31329612Sjmg 			break;
3146448Sache		}
3156448Sache	}
3166448Sache
31764657Ssobomax	if (dusage) {
31864657Ssobomax		close(baz);
31964657Ssobomax		usage(devmask, recmask);
320177197Sjkim		/* NOTREACHED */
32164657Ssobomax	}
32264657Ssobomax
323177195Sjkim	if (orecsrc != recsrc) {
32429963Scharnier		if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
32530028Scharnier			err(1, "SOUND_MIXER_WRITE_RECSRC");
32629963Scharnier		if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
32730028Scharnier			err(1, "SOUND_MIXER_READ_RECSRC");
32829612Sjmg	}
329177197Sjkim
330177195Sjkim	if (drecsrc)
331177197Sjkim		print_recsrc(recsrc, recmask, Sflag || sflag);
33229612Sjmg
3336448Sache	close(baz);
33429612Sjmg
335177197Sjkim	return (0);
3366448Sache}
337