xref: /freebsd/usr.sbin/mixer/mixer.c (revision a0ee8cc6)
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$");
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 
27 static const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
28 
29 static void	usage(int devmask, int recmask);
30 static int	res_name(const char *name, int mask);
31 static void	print_recsrc(int recsrc, int recmask, int sflag);
32 
33 static void
34 usage(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++;
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++;
59 			}
60 	}
61 	printf("\n");
62 	exit(1);
63 }
64 
65 static int
66 res_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) == 0)
72 			break;
73 
74 	return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo);
75 }
76 
77 static void
78 print_recsrc(int recsrc, int recmask, int sflag)
79 {
80 	int	i, n;
81 
82 	if (recmask == 0)
83 		return;
84 
85 	if (!sflag)
86 		printf("Recording source: ");
87 
88 	for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
89 		if ((1 << i) & recsrc) {
90 			if (sflag)
91 				printf("%srec ", n ? " +" : "=");
92 			else if (n)
93 				printf(", ");
94 			printf("%s", names[i]);
95 			n++;
96 		}
97 	if (!sflag)
98 		printf("\n");
99 }
100 
101 int
102 main(int argc, char *argv[])
103 {
104 	char	mixer[PATH_MAX] = "/dev/mixer";
105 	char	lstr[5], rstr[5];
106 	char	*name, *eptr;
107 	int	devmask = 0, recmask = 0, recsrc = 0, orecsrc;
108 	int	dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0;
109 	int	l, r, lrel, rrel;
110 	int	ch, foo, bar, baz, dev, m, n, t;
111 
112 	if ((name = strdup(basename(argv[0]))) == NULL)
113 		err(1, "strdup()");
114 	if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
115 		n = strtol(name + 5, &eptr, 10) - 1;
116 		if (n > 0 && *eptr == '\0')
117 			snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
118 	}
119 	free(name);
120 	name = mixer;
121 
122 	n = 1;
123 	for (;;) {
124 		if (n >= argc || *argv[n] != '-')
125 			break;
126 		if (strlen(argv[n]) != 2) {
127 			if (strcmp(argv[n] + 1, "rec") != 0)
128 				dusage = 1;
129 			break;
130 		}
131 		ch = *(argv[n] + 1);
132 		if (ch == 'f' && n < argc - 1) {
133 			name = argv[n + 1];
134 			n += 2;
135 		} else if (ch == 's') {
136 			sflag = 1;
137 			n++;
138 		} else if (ch == 'S') {
139 			Sflag = 1;
140 			n++;
141 		} else {
142 			dusage = 1;
143 			break;
144 		}
145 	}
146 	if (sflag && Sflag)
147 		dusage = 1;
148 
149 	argc -= n - 1;
150 	argv += n - 1;
151 
152 	if ((baz = open(name, O_RDWR)) < 0)
153 		err(1, "%s", name);
154 	if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
155 		err(1, "SOUND_MIXER_READ_DEVMASK");
156 	if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
157 		err(1, "SOUND_MIXER_READ_RECMASK");
158 	if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
159 		err(1, "SOUND_MIXER_READ_RECSRC");
160 	orecsrc = recsrc;
161 
162 	if (argc == 1 && dusage == 0) {
163 		for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
164 			if (!((1 << foo) & devmask))
165 				continue;
166 			if (ioctl(baz, MIXER_READ(foo),&bar) == -1) {
167 			   	warn("MIXER_READ");
168 				continue;
169 			}
170 			if (Sflag || sflag) {
171 				printf("%s%s%c%d:%d", n ? " " : "",
172 				    names[foo], Sflag ? ':' : ' ',
173 				    bar & 0x7f, (bar >> 8) & 0x7f);
174 				n++;
175 			} else
176 				printf("Mixer %-8s is currently set to "
177 				    "%3d:%d\n", names[foo], bar & 0x7f,
178 				    (bar >> 8) & 0x7f);
179 		}
180 		if (n && recmask)
181 			printf(" ");
182 		print_recsrc(recsrc, recmask, Sflag || sflag);
183 		return (0);
184 	}
185 
186 	argc--;
187 	argv++;
188 
189 	n = 0;
190 	while (argc > 0 && dusage == 0) {
191 		if (strcmp("recsrc", *argv) == 0) {
192 			drecsrc = 1;
193 			argc--;
194 			argv++;
195 			continue;
196 		} else if (strcmp("rec", *argv + 1) == 0) {
197 			if (**argv != '+' && **argv != '-' &&
198 			    **argv != '=' && **argv != '^') {
199 				warnx("unknown modifier: %c", **argv);
200 				dusage = 1;
201 				break;
202 			}
203 			if (argc <= 1) {
204 				warnx("no recording device specified");
205 				dusage = 1;
206 				break;
207 			}
208 			if ((dev = res_name(argv[1], recmask)) == -1) {
209 				warnx("unknown recording device: %s", argv[1]);
210 				dusage = 1;
211 				break;
212 			}
213 			switch (**argv) {
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 			case '^':
224 				recsrc ^= (1 << dev);
225 				break;
226 			}
227 			drecsrc = 1;
228 			argc -= 2;
229 			argv += 2;
230 			continue;
231 		}
232 
233 		if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0)
234 			dev = 0;
235 		else if ((dev = res_name(*argv, devmask)) == -1) {
236 			warnx("unknown device: %s", *argv);
237 			dusage = 1;
238 			break;
239 		}
240 
241 		lrel = rrel = 0;
242 		if (argc > 1) {
243 			m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
244 			if (m > 0) {
245 				if (*lstr == '+' || *lstr == '-')
246 					lrel = rrel = 1;
247 				l = strtol(lstr, NULL, 10);
248 			}
249 			if (m > 1) {
250 				if (*rstr == '+' || *rstr == '-')
251 					rrel = 1;
252 				r = strtol(rstr, NULL, 10);
253 			}
254 		}
255 
256 		switch (argc > 1 ? m : t) {
257 		case 0:
258 			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
259 				warn("MIXER_READ");
260 				argc--;
261 				argv++;
262 				continue;
263 			}
264 			if (Sflag || sflag) {
265 				printf("%s%s%c%d:%d", n ? " " : "",
266 				    names[dev], Sflag ? ':' : ' ',
267 				    bar & 0x7f, (bar >> 8) & 0x7f);
268 				n++;
269 			} else
270 				printf("Mixer %-8s is currently set to "
271 				    "%3d:%d\n", names[dev], bar & 0x7f,
272 				    (bar >> 8) & 0x7f);
273 
274 			argc--;
275 			argv++;
276 			break;
277 		case 1:
278 			r = l;
279 			/* FALLTHROUGH */
280 		case 2:
281 			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
282 				warn("MIXER_READ");
283 				argc--;
284 				argv++;
285 				continue;
286 			}
287 
288 			if (lrel)
289 				l = (bar & 0x7f) + l;
290 			if (rrel)
291 				r = ((bar >> 8) & 0x7f) + r;
292 
293 			if (l < 0)
294 				l = 0;
295 			else if (l > 100)
296 				l = 100;
297 			if (r < 0)
298 				r = 0;
299 			else if (r > 100)
300 				r = 100;
301 
302 			if (!Sflag)
303 				printf("Setting the mixer %s from %d:%d to "
304 				    "%d:%d.\n", names[dev], bar & 0x7f,
305 				    (bar >> 8) & 0x7f, l, r);
306 
307 			l |= r << 8;
308 			if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
309 				warn("WRITE_MIXER");
310 
311 			argc -= 2;
312 			argv += 2;
313  			break;
314 		}
315 	}
316 
317 	if (dusage) {
318 		close(baz);
319 		usage(devmask, recmask);
320 		/* NOTREACHED */
321 	}
322 
323 	if (orecsrc != recsrc) {
324 		if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
325 			err(1, "SOUND_MIXER_WRITE_RECSRC");
326 		if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
327 			err(1, "SOUND_MIXER_READ_RECSRC");
328 	}
329 
330 	if (drecsrc)
331 		print_recsrc(recsrc, recmask, Sflag || sflag);
332 
333 	close(baz);
334 
335 	return (0);
336 }
337