mixer.c revision 177195
1/*
2 *	This is an example of a mixer program for Linux
3 *
4 *	updated 1/1/93 to add stereo, level query, broken
5 *      	devmask kludge - cmetz@thor.tjhsst.edu
6 *
7 * (C) Craig Metz and Hannu Savolainen 1993.
8 *
9 * You may do anything you wish with this program.
10 *
11 * ditto for my modifications (John-Mark Gurney, 1997)
12 */
13
14#include <sys/cdefs.h>
15__FBSDID("$FreeBSD: head/usr.sbin/mixer/mixer.c 177195 2008-03-15 01:09:47Z jkim $");
16
17#include <err.h>
18#include <fcntl.h>
19#include <libgen.h>
20#include <limits.h>
21#include <stdio.h>
22#include <string.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <sys/soundcard.h>
26
27const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
28
29void usage(int devmask, int recmask);
30int res_name(const char *name, int mask);
31void print_recsrc(int recsrc, int recmask, int shortflag);
32
33void
34usage(int devmask, int recmask)
35{
36	int i, n;
37
38	printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n"
39	       "       mixer [-f device] [-s | -S] recsrc ...\n"
40	       "       mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ... \n");
41	if (devmask != 0) {
42		printf(" devices: ");
43		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
44			if ((1 << i) & devmask)  {
45				if (n)
46					printf(", ");
47				printf("%s", names[i]);
48				n = 1;
49			}
50	}
51	if (recmask != 0) {
52		printf("\n rec devices: ");
53		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
54			if ((1 << i) & recmask)  {
55				if (n)
56					printf(", ");
57				printf("%s", names[i]);
58				n = 1;
59			}
60	}
61	printf("\n");
62	exit(1);
63}
64
65int
66res_name(const char *name, int mask)
67{
68	int foo;
69
70	for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
71		if ((1 << foo) & mask && !strcmp(names[foo], name))
72			break;
73
74	return foo == SOUND_MIXER_NRDEVICES ? -1 : foo;
75}
76
77void
78print_recsrc(int recsrc, int recmask, int shortflag)
79{
80	int i, n = 0;
81
82	if (recmask == 0)
83		return;
84
85	if (!shortflag)
86		printf("Recording source: ");
87
88	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
89		if ((1 << i) & recsrc) {
90			if (shortflag) {
91				if (n)
92					printf(" +rec ");
93				else
94					printf("=rec ");
95			} else if (n)
96				printf(", ");
97			printf("%s", names[i]);
98			n = 1;
99		}
100	if (!shortflag)
101		printf("\n");
102}
103
104int
105main(int argc, char *argv[])
106{
107	char mixer[PATH_MAX] = "/dev/mixer";
108	char *name, *eptr;
109	int foo, bar, baz, dev;
110	int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
111	int dusage = 0, drecsrc = 0, shortflag = 0, Shortflag = 0;
112	int l = 0, r = 0, t = 0;
113	char lstr[5], rstr[5];
114	int n, lrel = 0, rrel = 0;
115	char ch;
116
117	if ((name = strdup(basename(argv[0]))) == NULL)
118		err(1, "strdup()");
119	if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
120		n = strtol(name + 5, &eptr, 10) - 1;
121		if (n > 0 && *eptr == '\0')
122			snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
123	}
124	free(name);
125	name = mixer;
126
127	n = 1;
128	for (;;) {
129		if (n >= argc || *argv[n] != '-')
130			break;
131		if (strlen(argv[n]) != 2) {
132			if (strcmp(argv[n] + 1, "rec") != 0)
133				dusage = 1;
134			break;
135		}
136		ch = *(argv[n] + 1);
137		if (ch == 'f' && n < argc - 1) {
138			name = argv[n + 1];
139			n += 2;
140		} else if (ch == 's') {
141			shortflag = 1;
142			n++;
143		} else if (ch == 'S') {
144			Shortflag = 1;
145			n++;
146		} else {
147			dusage = 1;
148			break;
149		}
150	}
151	if (shortflag && Shortflag)
152		dusage = 1;
153
154	argc -= n - 1;
155	argv += n - 1;
156
157	if ((baz = open(name, O_RDWR)) < 0)
158		err(1, "%s", name);
159	if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
160		err(1, "SOUND_MIXER_READ_DEVMASK");
161	if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
162		err(1, "SOUND_MIXER_READ_RECMASK");
163	if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
164		err(1, "SOUND_MIXER_READ_RECSRC");
165	orecsrc = recsrc;
166
167	if ((argc == 1) && (dusage == 0)) {
168		for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
169			if (!((1 << foo) & devmask))
170				continue;
171			if (ioctl(baz, MIXER_READ(foo),&bar)== -1) {
172			   	warn("MIXER_READ");
173				continue;
174			}
175			if (Shortflag)
176				printf("%s:%d:%d ", names[foo], bar & 0x7f,
177				       (bar >> 8) & 0x7f);
178			else if (shortflag)
179				printf("%s %d:%d ", names[foo], bar & 0x7f,
180				       (bar >> 8) & 0x7f);
181			else
182				printf("Mixer %-8s is currently set to %3d:%d\n",
183				       names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
184		}
185		if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
186			err(1, "SOUND_MIXER_READ_RECSRC");
187		print_recsrc(recsrc, recmask, shortflag || Shortflag);
188		return(0);
189	}
190
191	argc--; argv++;
192
193	while ((argc > 0) && (dusage == 0)) {
194		if (!strcmp("recsrc", *argv)) {
195			drecsrc = 1;
196			argc--; argv++;
197			continue;
198		} else if (argc > 1 && !strcmp("rec", *argv + 1)) {
199			if (**argv != '+' && **argv != '-' &&
200			    **argv != '=' && **argv != '^') {
201				warnx("unknown modifier: %c", **argv);
202				dusage = 1;
203				break;
204			}
205			if ((dev = res_name(argv[1], recmask)) == -1) {
206				warnx("unknown recording device: %s", argv[1]);
207				dusage = 1;
208				break;
209			}
210			switch(**argv) {
211			case '+':
212				recsrc |= (1 << dev);
213				break;
214			case '-':
215				recsrc &= ~(1 << dev);
216				break;
217			case '=':
218				recsrc = (1 << dev);
219				break;
220			case '^':
221				recsrc ^= (1 << dev);
222				break;
223			}
224			drecsrc = 1;
225			argc -= 2; argv += 2;
226			continue;
227		}
228
229		if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0) {
230			dev = 0;
231		}
232		else if((dev = res_name(*argv, devmask)) == -1) {
233			warnx("unknown device: %s", *argv);
234			dusage = 1;
235			break;
236		}
237
238#define	issign(c)	(((c) == '+') || ((c) == '-'))
239
240		if (argc > 1) {
241			n = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
242			if (n > 0) {
243				if (issign(lstr[0]))
244					lrel = rrel = 1;
245				l = atoi(lstr);
246			}
247			if (n > 1) {
248				rrel = 0;
249				if (issign(rstr[0]))
250					rrel = 1;
251				r = atoi(rstr);
252			}
253		}
254
255		switch(argc > 1 ? n : t) {
256		case 0:
257			if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
258				warn("MIXER_READ");
259				argc--; argv++;
260				continue;
261			}
262			if (Shortflag)
263				printf("%s:%d:%d ", names[dev], bar & 0x7f,
264				       (bar >> 8) & 0x7f);
265			else if (shortflag)
266				printf("%s %d:%d ", names[dev], bar & 0x7f,
267				       (bar >> 8) & 0x7f);
268			else
269				printf("Mixer %-8s is currently set to %3d:%d\n",
270				  names[dev], bar & 0x7f, (bar >> 8) & 0x7f);
271
272			argc--; argv++;
273			break;
274		case 1:
275			r = l;
276		case 2:
277			if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
278				warn("MIXER_READ");
279				argc--; argv++;
280				continue;
281			}
282
283			if (lrel)
284				l = (bar & 0x7f) + l;
285			if (rrel)
286				r = ((bar >> 8) & 0x7f) + r;
287
288			if (l < 0)
289				l = 0;
290			else if (l > 100)
291				l = 100;
292			if (r < 0)
293				r = 0;
294			else if (r > 100)
295				r = 100;
296
297			if (!Shortflag)
298				printf("Setting the mixer %s from %d:%d to %d:%d.\n",
299				       names[dev], bar & 0x7f, (bar >> 8) & 0x7f, l, r);
300
301			l |= r << 8;
302			if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
303				warn("WRITE_MIXER");
304
305			argc -= 2; argv += 2;
306 			break;
307		}
308	}
309
310	if (dusage) {
311		close(baz);
312		usage(devmask, recmask);
313		/* Not reached */
314	}
315
316	if (orecsrc != recsrc) {
317		if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
318			err(1, "SOUND_MIXER_WRITE_RECSRC");
319		if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
320			err(1, "SOUND_MIXER_READ_RECSRC");
321	}
322
323	if (drecsrc)
324		print_recsrc(recsrc, recmask, shortflag || Shortflag);
325
326	close(baz);
327
328	exit(0);
329}
330