2 * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37 static void usage(void) __dead2;
38 static void initctls(struct mixer *);
39 static void printall(struct mixer *, int);
40 static void printminfo(struct mixer *, int);
41 static void printdev(struct mixer *, int);
42 static void printrecsrc(struct mixer *, int); /* XXX: change name */
43 static int set_dunit(struct mixer *, int);
44 /* Control handlers */
45 static int mod_volume(struct mix_dev *, void *);
46 static int mod_mute(struct mix_dev *, void *);
47 static int mod_recsrc(struct mix_dev *, void *);
48 static int print_volume(struct mix_dev *, void *);
49 static int print_mute(struct mix_dev *, void *);
50 static int print_recsrc(struct mix_dev *, void *);
53 main(int argc, char *argv[])
57 char *name = NULL, buf[NAME_MAX];
58 char *p, *q, *devstr, *ctlstr, *valstr = NULL;
59 int dunit, i, n, pall = 1, shorthand;
60 int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
63 while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
69 dunit = strtol(optarg, NULL, 10);
70 if (errno == EINVAL || errno == ERANGE)
83 case 'h': /* FALLTHROUGH */
92 /* Print all mixers and exit. */
94 if ((n = mixer_get_nmixers()) < 0)
95 err(1, "mixer_get_nmixers");
96 for (i = 0; i < n; i++) {
97 (void)snprintf(buf, sizeof(buf), "/dev/mixer%d", i);
98 if ((m = mixer_open(buf)) == NULL)
99 err(1, "mixer_open: %s", buf);
102 printrecsrc(m, oflag);
108 (void)mixer_close(m);
113 if ((m = mixer_open(name)) == NULL)
114 err(1, "mixer_open: %s", name);
118 if (dflag && set_dunit(m, dunit) < 0)
121 printrecsrc(m, oflag);
122 (void)mixer_close(m);
128 if ((p = strdup(*argv)) == NULL)
129 err(1, "strdup(%s)", *argv);
131 /* Check if we're using the shorthand syntax for volume setting. */
133 for (q = p; *q != '\0'; q++) {
136 shorthand = ((*q >= '0' && *q <= '9') ||
137 *q == '+' || *q == '-' || *q == '.');
139 } else if (*q == '.')
143 /* Split the string into device, control and value. */
144 devstr = strsep(&p, ".=");
145 if ((m->dev = mixer_get_dev_byname(m, devstr)) == NULL) {
146 warnx("%s: no such device", devstr);
154 } else if (shorthand) {
156 * Input: `dev=N` -> shorthand for `dev.volume=N`.
158 * We don't care what the rest of the string contains as
159 * long as we're sure the very beginning is right,
160 * mod_volume() will take care of parsing it properly.
162 cp = mixer_get_ctl(m->dev, C_VOL);
163 cp->mod(cp->parent_dev, p);
166 ctlstr = strsep(&p, "=");
167 if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
168 warnx("%s.%s: no such control", devstr, ctlstr);
171 /* Input: `dev.control`. */
173 (void)cp->print(cp->parent_dev, cp->name);
178 /* Input: `dev.control=val`. */
179 cp->mod(cp->parent_dev, valstr);
188 (void)mixer_close(m);
196 fprintf(stderr, "usage: %1$s [-f device] [-d unit] [-os] [dev[.control[=value]]] ...\n"
197 " %1$s [-d unit] [-os] -a\n"
198 " %1$s -h\n", getprogname());
203 initctls(struct mixer *m)
208 TAILQ_FOREACH(dp, &m->devs, devs) {
209 rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, print_volume);
210 rc += mixer_add_ctl(dp, C_MUT, "mute", mod_mute, print_mute);
211 rc += mixer_add_ctl(dp, C_SRC, "recsrc", mod_recsrc, print_recsrc);
214 (void)mixer_close(m);
215 err(1, "cannot make controls");
220 printall(struct mixer *m, int oflag)
224 printminfo(m, oflag);
225 TAILQ_FOREACH(dp, &m->devs, devs) {
232 printminfo(struct mixer *m, int oflag)
234 int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
238 printf("%s:", m->mi.name);
239 if (*m->ci.longname != '\0')
240 printf(" <%s>", m->ci.longname);
241 if (*m->ci.hw_info != '\0')
242 printf(" %s", m->ci.hw_info);
246 if (m->mode & MIX_MODE_PLAY)
248 if ((m->mode & playrec) == playrec)
250 if (m->mode & MIX_MODE_REC)
256 printf(" (default)");
261 printdev(struct mixer *m, int oflag)
263 struct mix_dev *d = m->dev;
267 printf(" %-10s= %.2f:%.2f ",
268 d->name, d->vol.left, d->vol.right);
269 if (!MIX_ISREC(m, d->devno))
271 if (MIX_ISREC(m, d->devno))
273 if (MIX_ISRECSRC(m, d->devno))
275 if (MIX_ISMUTE(m, d->devno))
279 TAILQ_FOREACH(cp, &d->ctls, ctls) {
280 (void)cp->print(cp->parent_dev, cp->name);
286 printrecsrc(struct mixer *m, int oflag)
294 printf("%s: ", m->mi.name);
295 TAILQ_FOREACH(dp, &m->devs, devs) {
296 if (MIX_ISRECSRC(m, dp->devno)) {
299 printf("%s", dp->name);
302 mixer_get_ctl(dp, C_SRC)->name, n ? " " : "");
309 set_dunit(struct mixer *m, int dunit)
313 if ((n = mixer_get_dunit()) < 0) {
314 warn("cannot get default unit");
317 if (mixer_set_dunit(m, dunit) < 0) {
318 warn("cannot set default unit to: %d", dunit);
321 printf("default_unit: %d -> %d\n", n, dunit);
327 mod_volume(struct mix_dev *d, void *p)
333 char *endp, lstr[8], rstr[8];
334 float lprev, rprev, lrel, rrel;
338 cp = mixer_get_ctl(m->dev, C_VOL);
340 n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
342 warnx("invalid volume value: %s", val);
347 if (*lstr == '+' || *lstr == '-')
349 v.left = strtof(lstr, &endp);
350 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
351 warnx("invalid volume value: %s", lstr);
359 if (*rstr == '+' || *rstr == '-')
361 v.right = strtof(rstr, &endp);
362 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
363 warnx("invalid volume value: %s", rstr);
372 v.right = v.left; /* FALLTHROUGH */
376 v.left += m->dev->vol.left;
378 v.right += m->dev->vol.right;
380 if (v.left < MIX_VOLMIN)
382 else if (v.left > MIX_VOLMAX)
384 if (v.right < MIX_VOLMIN)
385 v.right = MIX_VOLMIN;
386 else if (v.right > MIX_VOLMAX)
387 v.right = MIX_VOLMAX;
389 lprev = m->dev->vol.left;
390 rprev = m->dev->vol.right;
391 if (mixer_set_vol(m, v) < 0)
392 warn("%s.%s=%.2f:%.2f",
393 m->dev->name, cp->name, v.left, v.right);
395 printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
396 m->dev->name, cp->name, lprev, rprev, v.left, v.right);
403 mod_mute(struct mix_dev *d, void *p)
411 cp = mixer_get_ctl(m->dev, C_MUT);
421 opt = MIX_TOGGLEMUTE;
424 warnx("%c: no such modifier", *val);
427 n = MIX_ISMUTE(m, m->dev->devno);
428 if (mixer_set_mute(m, opt) < 0)
429 warn("%s.%s=%c", m->dev->name, cp->name, *val);
431 printf("%s.%s: %d -> %d\n",
432 m->dev->name, cp->name, n, MIX_ISMUTE(m, m->dev->devno));
438 mod_recsrc(struct mix_dev *d, void *p)
446 cp = mixer_get_ctl(m->dev, C_SRC);
453 opt = MIX_REMOVERECSRC;
459 opt = MIX_TOGGLERECSRC;
462 warnx("%c: no such modifier", *val);
465 n = MIX_ISRECSRC(m, m->dev->devno);
466 if (mixer_mod_recsrc(m, opt) < 0)
467 warn("%s.%s=%c", m->dev->name, cp->name, *val);
469 printf("%s.%s: %d -> %d\n",
470 m->dev->name, cp->name, n, MIX_ISRECSRC(m, m->dev->devno));
476 print_volume(struct mix_dev *d, void *p)
478 struct mixer *m = d->parent_mixer;
479 const char *ctl_name = p;
481 printf("%s.%s=%.2f:%.2f\n",
482 m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
488 print_mute(struct mix_dev *d, void *p)
490 struct mixer *m = d->parent_mixer;
491 const char *ctl_name = p;
493 printf("%s.%s=%d\n", m->dev->name, ctl_name, MIX_ISMUTE(m, m->dev->devno));
499 print_recsrc(struct mix_dev *d, void *p)
501 struct mixer *m = d->parent_mixer;
502 const char *ctl_name = p;
504 if (!MIX_ISRECSRC(m, m->dev->devno))
506 printf("%s.%s=+\n", m->dev->name, ctl_name);