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
34 static void usage(void) __dead2;
35 static void initctls(struct mixer *);
36 static void printall(struct mixer *, int);
37 static void printminfo(struct mixer *, int);
38 static void printdev(struct mixer *, int);
39 static void printrecsrc(struct mixer *, int); /* XXX: change name */
40 /* Control handlers */
41 static int mod_dunit(struct mix_dev *, void *);
42 static int mod_volume(struct mix_dev *, void *);
43 static int mod_mute(struct mix_dev *, void *);
44 static int mod_recsrc(struct mix_dev *, void *);
45 static int print_volume(struct mix_dev *, void *);
46 static int print_mute(struct mix_dev *, void *);
47 static int print_recsrc(struct mix_dev *, void *);
49 static const mix_ctl_t ctl_dunit = {
52 .name = "default_unit",
58 main(int argc, char *argv[])
62 char *name = NULL, buf[NAME_MAX];
63 char *p, *bufp, *devstr, *ctlstr, *valstr = NULL;
64 int dunit, i, n, pall = 1;
65 int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
68 while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
74 dunit = strtol(optarg, NULL, 10);
75 if (errno == EINVAL || errno == ERANGE)
88 case 'h': /* FALLTROUGH */
97 /* Print all mixers and exit. */
99 if ((n = mixer_get_nmixers()) < 0)
100 err(1, "mixer_get_nmixers");
101 for (i = 0; i < n; i++) {
102 (void)snprintf(buf, sizeof(buf), "/dev/mixer%d", i);
103 if ((m = mixer_open(buf)) == NULL)
104 err(1, "mixer_open: %s", buf);
107 printrecsrc(m, oflag);
113 (void)mixer_close(m);
118 if ((m = mixer_open(name)) == NULL)
119 err(1, "mixer_open: %s", name);
123 if (dflag && ctl_dunit.mod(m->dev, &dunit) < 0)
126 printrecsrc(m, oflag);
127 (void)mixer_close(m);
133 if ((p = bufp = strdup(*argv)) == NULL)
134 err(1, "strdup(%s)", *argv);
135 /* Split the string into device, control and value. */
136 devstr = strsep(&p, ".");
137 if ((m->dev = mixer_get_dev_byname(m, devstr)) == NULL) {
138 warnx("%s: no such device", devstr);
147 ctlstr = strsep(&p, "=");
148 if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
149 warnx("%s.%s: no such control", devstr, ctlstr);
153 /* Input: `dev.control`. */
155 (void)cp->print(cp->parent_dev, cp->name);
160 /* Input: `dev.control=val`. */
161 cp->mod(cp->parent_dev, valstr);
170 (void)mixer_close(m);
178 fprintf(stderr, "usage: %1$s [-f device] [-d unit] [-os] [dev[.control[=value]]] ...\n"
179 " %1$s [-d unit] [-os] -a\n"
180 " %1$s -h\n", getprogname());
185 initctls(struct mixer *m)
193 TAILQ_FOREACH(dp, &m->devs, devs) {
194 rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, print_volume);
195 rc += mixer_add_ctl(dp, C_MUT, "mute", mod_mute, print_mute);
196 rc += mixer_add_ctl(dp, C_SRC, "recsrc", mod_recsrc, print_recsrc);
199 (void)mixer_close(m);
200 err(1, "cannot make controls");
205 printall(struct mixer *m, int oflag)
209 printminfo(m, oflag);
210 TAILQ_FOREACH(dp, &m->devs, devs) {
217 printminfo(struct mixer *m, int oflag)
219 int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
223 printf("%s:", m->mi.name);
224 if (*m->ci.longname != '\0')
225 printf(" <%s>", m->ci.longname);
226 if (*m->ci.hw_info != '\0')
227 printf(" %s", m->ci.hw_info);
231 if (m->mode & MIX_MODE_PLAY)
233 if ((m->mode & playrec) == playrec)
235 if (m->mode & MIX_MODE_REC)
241 printf(" (default)");
246 printdev(struct mixer *m, int oflag)
248 struct mix_dev *d = m->dev;
253 (void)snprintf(buffer, sizeof(buffer),
254 "%s.%s", d->name, "volume");
256 printf(" %-16s= %.2f:%.2f\t",
257 buffer, d->vol.left, d->vol.right);
258 if (!MIX_ISREC(m, d->devno))
260 if (MIX_ISREC(m, d->devno))
262 if (MIX_ISRECSRC(m, d->devno))
264 if (MIX_ISMUTE(m, d->devno))
268 TAILQ_FOREACH(cp, &d->ctls, ctls) {
269 (void)cp->print(cp->parent_dev, cp->name);
275 printrecsrc(struct mixer *m, int oflag)
283 printf("%s: ", m->mi.name);
284 TAILQ_FOREACH(dp, &m->devs, devs) {
285 if (MIX_ISRECSRC(m, dp->devno)) {
288 printf("%s", dp->name);
291 mixer_get_ctl(dp, C_SRC)->name,
299 mod_dunit(struct mix_dev *d, void *p)
301 int dunit = *((int *)p);
304 if ((n = mixer_get_dunit()) < 0) {
305 warn("cannot get default unit");
308 if (mixer_set_dunit(d->parent_mixer, dunit) < 0) {
309 warn("cannot set default unit to: %d", dunit);
312 printf("%s: %d -> %d\n", ctl_dunit.name, n, dunit);
318 mod_volume(struct mix_dev *d, void *p)
324 char lstr[8], rstr[8];
325 float lprev, rprev, lrel, rrel;
329 cp = mixer_get_ctl(m->dev, C_VOL);
331 n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
333 warnx("invalid volume value: %s", val);
338 if (*lstr == '+' || *lstr == '-')
340 v.left = strtof(lstr, NULL);
342 /* be backwards compatible */
343 if (strstr(lstr, ".") == NULL)
347 if (*rstr == '+' || *rstr == '-')
349 v.right = strtof(rstr, NULL);
351 /* be backwards compatible */
352 if (strstr(rstr, ".") == NULL)
357 v.right = v.left; /* FALLTHROUGH */
360 v.left += m->dev->vol.left;
362 v.right += m->dev->vol.right;
364 if (v.left < MIX_VOLMIN)
366 else if (v.left > MIX_VOLMAX)
368 if (v.right < MIX_VOLMIN)
369 v.right = MIX_VOLMIN;
370 else if (v.right > MIX_VOLMAX)
371 v.right = MIX_VOLMAX;
373 lprev = m->dev->vol.left;
374 rprev = m->dev->vol.right;
375 if (mixer_set_vol(m, v) < 0)
376 warn("%s.%s=%.2f:%.2f",
377 m->dev->name, cp->name, v.left, v.right);
379 printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
380 m->dev->name, cp->name, lprev, rprev, v.left, v.right);
387 mod_mute(struct mix_dev *d, void *p)
395 cp = mixer_get_ctl(m->dev, C_MUT);
405 opt = MIX_TOGGLEMUTE;
408 warnx("%c: no such modifier", *val);
411 n = MIX_ISMUTE(m, m->dev->devno);
412 if (mixer_set_mute(m, opt) < 0)
413 warn("%s.%s=%c", m->dev->name, cp->name, *val);
415 printf("%s.%s: %d -> %d\n",
416 m->dev->name, cp->name, n, MIX_ISMUTE(m, m->dev->devno));
422 mod_recsrc(struct mix_dev *d, void *p)
430 cp = mixer_get_ctl(m->dev, C_SRC);
437 opt = MIX_REMOVERECSRC;
443 opt = MIX_TOGGLERECSRC;
446 warnx("%c: no such modifier", *val);
449 n = MIX_ISRECSRC(m, m->dev->devno);
450 if (mixer_mod_recsrc(m, opt) < 0)
451 warn("%s.%s=%c", m->dev->name, cp->name, *val);
453 printf("%s.%s: %d -> %d\n",
454 m->dev->name, cp->name, n, MIX_ISRECSRC(m, m->dev->devno));
460 print_volume(struct mix_dev *d, void *p)
462 struct mixer *m = d->parent_mixer;
463 const char *ctl_name = p;
465 printf("%s.%s=%.2f:%.2f\n",
466 m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
472 print_mute(struct mix_dev *d, void *p)
474 struct mixer *m = d->parent_mixer;
475 const char *ctl_name = p;
477 printf("%s.%s=%d\n", m->dev->name, ctl_name, MIX_ISMUTE(m, m->dev->devno));
483 print_recsrc(struct mix_dev *d, void *p)
485 struct mixer *m = d->parent_mixer;
486 const char *ctl_name = p;
488 if (!MIX_ISRECSRC(m, m->dev->devno))
490 printf("%s.%s=+\n", m->dev->name, ctl_name);