xref: /openbsd-src/usr.bin/mixerctl/mixerctl.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: mixerctl.c,v 1.5 1999/07/19 20:54:21 mickey Exp $	*/
2 /*	$NetBSD: mixerctl.c,v 1.11 1998/04/27 16:55:23 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 1997 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * Author: Lennart Augustsson, with some code and ideas from Chuck Cranor.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <err.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #include <sys/audioio.h>
47 
48 char *catstr __P((char *p, char *q));
49 struct field *findfield __P((char *name));
50 void prfield __P((struct field *p, char *sep, int prvalset));
51 int rdfield __P((struct field *p, char *q));
52 int main(int argc, char **argv);
53 
54 FILE *out = stdout;
55 
56 char *prog;
57 
58 struct field {
59 	char *name;
60 	mixer_ctrl_t *valp;
61 	mixer_devinfo_t *infp;
62 	char changed;
63 } *fields, *rfields;
64 
65 mixer_ctrl_t *values;
66 mixer_devinfo_t *infos;
67 
68 char *
69 catstr(p, q)
70 	char *p;
71         char *q;
72 {
73 	char *r = malloc(strlen(p) + strlen(q) + 2);
74 	strcpy(r, p);
75 	strcat(r, ".");
76 	strcat(r, q);
77 	return r;
78 }
79 
80 struct field *
81 findfield(name)
82 	char *name;
83 {
84 	int i;
85 	for(i = 0; fields[i].name; i++)
86 		if (strcmp(fields[i].name, name) == 0)
87 			return &fields[i];
88 	return 0;
89 }
90 
91 void
92 prfield(p, sep, prvalset)
93 	struct field *p;
94         char *sep;
95         int prvalset;
96 {
97 	mixer_ctrl_t *m;
98 	int i, n;
99 
100 	if (sep)
101 		fprintf(out, "%s%s", p->name, sep);
102 	m = p->valp;
103 	switch(m->type) {
104 	case AUDIO_MIXER_ENUM:
105 		for(i = 0; i < p->infp->un.e.num_mem; i++)
106 			if (p->infp->un.e.member[i].ord == m->un.ord)
107 				fprintf(out, "%s",
108 					p->infp->un.e.member[i].label.name);
109 		if (prvalset) {
110 			fprintf(out, "  [ ");
111 			for(i = 0; i < p->infp->un.e.num_mem; i++)
112 				fprintf(out, "%s ", p->infp->un.e.member[i].label.name);
113 			fprintf(out, "]");
114 		}
115 		break;
116 	case AUDIO_MIXER_SET:
117 		for(n = i = 0; i < p->infp->un.s.num_mem; i++)
118 			if (m->un.mask & p->infp->un.s.member[i].mask)
119 				fprintf(out, "%s%s", n++ ? "," : "",
120 					p->infp->un.s.member[i].label.name);
121 		if (prvalset) {
122 			fprintf(out, "  { ");
123 			for(i = 0; i < p->infp->un.s.num_mem; i++)
124 				fprintf(out, "%s ", p->infp->un.s.member[i].label.name);
125 			fprintf(out, "}");
126 		}
127 		break;
128 	case AUDIO_MIXER_VALUE:
129 		if (m->un.value.num_channels == 1)
130 			fprintf(out, "%d", m->un.value.level[0]);
131 		else
132 			fprintf(out, "%d,%d", m->un.value.level[0],
133 			       m->un.value.level[1]);
134 		if (prvalset)
135 			fprintf(out, " %s", p->infp->un.v.units.name);
136 		break;
137 	default:
138 		printf("\n");
139 		errx(1, "Invalid format.");
140 	}
141 }
142 
143 int
144 rdfield(p, q)
145 	struct field *p;
146         char *q;
147 {
148 	mixer_ctrl_t *m;
149 	int v, v0, v1, mask;
150 	int i;
151 	char *s;
152 
153 	m = p->valp;
154 	switch(m->type) {
155 	case AUDIO_MIXER_ENUM:
156 		for(i = 0; i < p->infp->un.e.num_mem; i++)
157 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
158 				break;
159 		if (i < p->infp->un.e.num_mem)
160 			m->un.ord = p->infp->un.e.member[i].ord;
161 		else {
162 			warnx("Bad enum value %s", q);
163 			return 0;
164 		}
165 		break;
166 	case AUDIO_MIXER_SET:
167 		mask = 0;
168 		for(v = 0; q && *q; q = s) {
169 			s = strchr(q, ',');
170 			if (s)
171 				*s++ = 0;
172 			for (i = 0; i < p->infp->un.s.num_mem; i++)
173 				if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
174 					break;
175 			if (i < p->infp->un.s.num_mem) {
176 				mask |= p->infp->un.s.member[i].mask;
177 			} else {
178 				warnx("Bad set value %s", q);
179 				return 0;
180 			}
181 		}
182 		m->un.mask = mask;
183 		break;
184 	case AUDIO_MIXER_VALUE:
185 		if (m->un.value.num_channels == 1) {
186 			if (sscanf(q, "%d", &v) == 1) {
187 				switch (*q) {
188 				case '+':
189 				case '-':
190 					m->un.value.level[0] += v;
191 					break;
192 				default:
193 					m->un.value.level[0] = v;
194 					break;
195 				}
196 			} else {
197 				warnx("Bad number %s", q);
198 				return 0;
199 			}
200 		} else {
201 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
202 				switch (*q) {
203 				case '+':
204 				case '-':
205 					m->un.value.level[0] += v0;
206 					break;
207 				default:
208 					m->un.value.level[0] = v0;
209 					break;
210 				}
211 				s = strchr(q, ',') + 1;
212 				switch (*s) {
213 				case '+':
214 				case '-':
215 					m->un.value.level[1] += v1;
216 					break;
217 				default:
218 					m->un.value.level[1] = v1;
219 					break;
220 				}
221 			} else if (sscanf(q, "%d", &v) == 1) {
222 				switch (*q) {
223 				case '+':
224 				case '-':
225 					m->un.value.level[0] += v;
226 					m->un.value.level[1] += v;
227 					break;
228 				default:
229 					m->un.value.level[0] =
230 					    m->un.value.level[1] = v;
231 					break;
232 				}
233 			} else {
234 				warnx("Bad numbers %s", q);
235 				return 0;
236 			}
237 		}
238 		break;
239 	default:
240 		errx(1, "Invalid format.");
241 	}
242 	p->changed = 1;
243 	return 1;
244 }
245 
246 int
247 main(argc, argv)
248 	int argc;
249         char **argv;
250 {
251 	int fd, i, j, ch, pos;
252 	int aflag = 0, wflag = 0, vflag = 0;
253 	char *file;
254 	char *sep = "=";
255 	mixer_devinfo_t dinfo;
256 	mixer_ctrl_t val;
257 	int ndev;
258 
259 	file = getenv("MIXERDEVICE");
260 	if (file == 0)
261 		file = "/dev/mixer";
262 
263 	prog = *argv;
264 
265 	while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
266 		switch(ch) {
267 		case 'a':
268 			aflag++;
269 			break;
270 		case 'w':
271 			wflag++;
272 			break;
273 		case 'v':
274 			vflag++;
275 			break;
276 		case 'n':
277 			sep = 0;
278 			break;
279 		case 'f':
280 			file = optarg;
281 			break;
282 		case '?':
283 		default:
284 		usage:
285 		fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
286 		fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
287 		fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
288 		exit(0);
289 		}
290 	}
291 	argc -= optind;
292 	argv += optind;
293 
294 	fd = open(file, O_RDWR);
295 	if (fd < 0)
296 		err(1, "%s", file);
297 
298 	for(ndev = 0; ; ndev++) {
299 		dinfo.index = ndev;
300 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
301 			break;
302 	}
303 
304 	if (ndev == 0)
305 		errx(1, "no mixer devices configured");
306 
307 	rfields = calloc(ndev, sizeof *rfields);
308 	fields = calloc(ndev, sizeof *fields);
309 	infos = calloc(ndev, sizeof *infos);
310 	values = calloc(ndev, sizeof *values);
311 
312 	for(i = 0; i < ndev; i++) {
313 		infos[i].index = i;
314 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
315 	}
316 
317 	for(i = 0; i < ndev; i++) {
318 		rfields[i].name = infos[i].label.name;
319 		rfields[i].valp = &values[i];
320 		rfields[i].infp = &infos[i];
321 	}
322 
323 	for(i = 0; i < ndev; i++) {
324 		values[i].dev = i;
325 		values[i].type = infos[i].type;
326 		if (infos[i].type != AUDIO_MIXER_CLASS) {
327 			values[i].un.value.num_channels = 2;
328 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
329 				values[i].un.value.num_channels = 1;
330 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
331 					err(1, "AUDIO_MIXER_READ");
332 			}
333 		}
334 	}
335 
336 	for(j = i = 0; i < ndev; i++) {
337 		if (infos[i].type != AUDIO_MIXER_CLASS &&
338 		    infos[i].type != -1) {
339 			fields[j++] = rfields[i];
340 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
341 			    pos = infos[pos].next) {
342 				fields[j] = rfields[pos];
343 				fields[j].name = catstr(rfields[i].name,
344 							infos[pos].label.name);
345 				infos[pos].type = -1;
346 				j++;
347 			}
348 		}
349 	}
350 
351 	for(i = 0; i < j; i++) {
352 		int cls = fields[i].infp->mixer_class;
353 		if (cls >= 0 && cls < ndev)
354 			fields[i].name = catstr(infos[cls].label.name,
355 						fields[i].name);
356 	}
357 
358 	if (argc == 0 && aflag && !wflag) {
359 		for(i = 0; fields[i].name; i++) {
360 			prfield(&fields[i], sep, vflag);
361 			fprintf(out, "\n");
362 		}
363 	} else if (argc > 0 && !aflag) {
364 		struct field *p;
365 		if (wflag) {
366 			while(argc--) {
367 				char *q;
368 
369 				q = strchr(*argv, '=');
370 				if (q) {
371 					*q++ = 0;
372 					p = findfield(*argv);
373 					if (p == 0)
374 						warnx("field %s does not exist", *argv);
375 					else {
376 						val = *p->valp;
377 						if (rdfield(p, q)) {
378 							if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
379 								warn("AUDIO_MIXER_WRITE");
380 							else if (sep) {
381 								*p->valp = val;
382 								prfield(p, ": ", 0);
383 								ioctl(fd, AUDIO_MIXER_READ, p->valp);
384 								printf(" -> ");
385 								prfield(p, 0, 0);
386 								printf("\n");
387 							}
388 						}
389 					}
390 				} else {
391 					warnx("No `=' in %s", *argv);
392 				}
393 				argv++;
394 			}
395 		} else {
396 			while(argc--) {
397 				p = findfield(*argv);
398 				if (p == 0)
399 					warnx("field %s does not exist", *argv);
400 				else
401 					prfield(p, sep, vflag), fprintf(out, "\n");
402 				argv++;
403 			}
404 		}
405 	} else
406 		goto usage;
407 	exit(0);
408 }
409