]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mixer/mixer.c
sqlite3: Vendor import of sqlite3 3.39.2
[FreeBSD/FreeBSD.git] / usr.sbin / mixer / mixer.c
1 /*-
2  * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
3  *
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:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
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
20  * THE SOFTWARE.
21  *
22  * $FreeBSD$
23  */
24
25 #include <err.h>
26 #include <errno.h>
27 #include <mixer.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 enum {
34         C_VOL = 0,
35         C_MUT,
36         C_SRC,
37 };
38
39 static void usage(void) __dead2;
40 static void initctls(struct mixer *);
41 static void printall(struct mixer *, int);
42 static void printminfo(struct mixer *, int);
43 static void printdev(struct mixer *, int);
44 static void printrecsrc(struct mixer *, int); /* XXX: change name */
45 /* Control handlers */
46 static int mod_dunit(struct mix_dev *, void *);
47 static int mod_volume(struct mix_dev *, void *);
48 static int mod_mute(struct mix_dev *, void *);
49 static int mod_recsrc(struct mix_dev *, void *);
50 static int print_volume(struct mix_dev *, void *);
51 static int print_mute(struct mix_dev *, void *);
52 static int print_recsrc(struct mix_dev *, void *);
53
54 static const mix_ctl_t ctl_dunit = {
55         .parent_dev     = NULL,
56         .id             = -1,
57         .name           = "default_unit",
58         .mod            = mod_dunit,
59         .print          = NULL
60 };
61
62 int
63 main(int argc, char *argv[])
64 {
65         struct mixer *m;
66         mix_ctl_t *cp;
67         char *name = NULL, buf[NAME_MAX];
68         char *p, *q, *devstr, *ctlstr, *valstr = NULL;
69         int dunit, i, n, pall = 1, shorthand;
70         int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
71         int ch;
72
73         while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
74                 switch (ch) {
75                 case 'a':
76                         aflag = 1;
77                         break;
78                 case 'd':
79                         dunit = strtol(optarg, NULL, 10);
80                         if (errno == EINVAL || errno == ERANGE)
81                                 err(1, "strtol");
82                         dflag = 1;
83                         break;
84                 case 'f':
85                         name = optarg;
86                         break;
87                 case 'o':
88                         oflag = 1;
89                         break;
90                 case 's':
91                         sflag = 1;
92                         break;
93                 case 'h': /* FALLTHROUGH */
94                 case '?':
95                 default:
96                         usage();
97                 }
98         }
99         argc -= optind;
100         argv += optind;
101
102         /* Print all mixers and exit. */
103         if (aflag) {
104                 if ((n = mixer_get_nmixers()) < 0)
105                         err(1, "mixer_get_nmixers");
106                 for (i = 0; i < n; i++) {
107                         (void)snprintf(buf, sizeof(buf), "/dev/mixer%d", i);
108                         if ((m = mixer_open(buf)) == NULL)
109                                 err(1, "mixer_open: %s", buf);
110                         initctls(m);
111                         if (sflag)
112                                 printrecsrc(m, oflag);
113                         else {
114                                 printall(m, oflag);
115                                 if (oflag)
116                                         printf("\n");
117                         }
118                         (void)mixer_close(m);
119                 }
120                 return (0);
121         }
122
123         if ((m = mixer_open(name)) == NULL)
124                 err(1, "mixer_open: %s", name);
125
126         initctls(m);
127
128         if (dflag && ctl_dunit.mod(m->dev, &dunit) < 0)
129                 goto parse;
130         if (sflag) {
131                 printrecsrc(m, oflag);
132                 (void)mixer_close(m);
133                 return (0);
134         }
135
136 parse:
137         while (argc > 0) {
138                 if ((p = strdup(*argv)) == NULL)
139                         err(1, "strdup(%s)", *argv);
140
141                 /* Check if we're using the shorthand syntax for volume setting. */
142                 shorthand = 0;
143                 for (q = p; *q != '\0'; q++) {
144                         if (*q == '=') {
145                                 q++;
146                                 shorthand = ((*q >= '0' && *q <= '9') ||
147                                     *q == '+' || *q == '-' || *q == '.');
148                                 break;
149                         } else if (*q == '.')
150                                 break;
151                 }
152
153                 /* Split the string into device, control and value. */
154                 devstr = strsep(&p, ".=");
155                 if ((m->dev = mixer_get_dev_byname(m, devstr)) == NULL) {
156                         warnx("%s: no such device", devstr);
157                         goto next;
158                 }
159                 /* Input: `dev`. */
160                 if (p == NULL) {
161                         printdev(m, 1);
162                         pall = 0;
163                         goto next;
164                 } else if (shorthand) {
165                         /*
166                          * Input: `dev=N` -> shorthand for `dev.volume=N`.
167                          *
168                          * We don't care what the rest of the string contains as
169                          * long as we're sure the very beginning is right,
170                          * mod_volume() will take care of parsing it properly.
171                          */
172                         cp = mixer_get_ctl(m->dev, C_VOL);
173                         cp->mod(cp->parent_dev, p);
174                         goto next;
175                 }
176                 ctlstr = strsep(&p, "=");
177                 if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
178                         warnx("%s.%s: no such control", devstr, ctlstr);
179                         goto next;
180                 }
181                 /* Input: `dev.control`. */
182                 if (p == NULL) {
183                         (void)cp->print(cp->parent_dev, cp->name);
184                         pall = 0;
185                         goto next;
186                 }
187                 valstr = p;
188                 /* Input: `dev.control=val`. */
189                 cp->mod(cp->parent_dev, valstr);
190 next:
191                 free(p);
192                 argc--;
193                 argv++;
194         }
195
196         if (pall)
197                 printall(m, oflag);
198         (void)mixer_close(m);
199
200         return (0);
201 }
202
203 static void __dead2
204 usage(void)
205 {
206         fprintf(stderr, "usage: %1$s [-f device] [-d unit] [-os] [dev[.control[=value]]] ...\n"
207             "       %1$s [-d unit] [-os] -a\n"
208             "       %1$s -h\n", getprogname());
209         exit(1);
210 }
211
212 static void
213 initctls(struct mixer *m)
214 {
215         struct mix_dev *dp;
216         int rc = 0;
217
218         TAILQ_FOREACH(dp, &m->devs, devs) {
219                 rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, print_volume);
220                 rc += mixer_add_ctl(dp, C_MUT, "mute", mod_mute, print_mute);
221                 rc += mixer_add_ctl(dp, C_SRC, "recsrc", mod_recsrc, print_recsrc);
222         }
223         if (rc) {
224                 (void)mixer_close(m);
225                 err(1, "cannot make controls");
226         }
227 }
228
229 static void
230 printall(struct mixer *m, int oflag)
231 {
232         struct mix_dev *dp;
233
234         printminfo(m, oflag);
235         TAILQ_FOREACH(dp, &m->devs, devs) {
236                 m->dev = dp;
237                 printdev(m, oflag);
238         }
239 }
240
241 static void
242 printminfo(struct mixer *m, int oflag)
243 {
244         int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
245
246         if (oflag)
247                 return;
248         printf("%s:", m->mi.name);
249         if (*m->ci.longname != '\0')
250                 printf(" <%s>", m->ci.longname);
251         if (*m->ci.hw_info != '\0')
252                 printf(" %s", m->ci.hw_info);
253
254         if (m->mode != 0)
255                 printf(" (");
256         if (m->mode & MIX_MODE_PLAY)
257                 printf("play");
258         if ((m->mode & playrec) == playrec)
259                 printf("/");
260         if (m->mode & MIX_MODE_REC)
261                 printf("rec");
262         if (m->mode != 0)
263                 printf(")");
264
265         if (m->f_default)
266                 printf(" (default)");
267         printf("\n");
268 }
269
270 static void
271 printdev(struct mixer *m, int oflag)
272 {
273         struct mix_dev *d = m->dev;
274         mix_ctl_t *cp;
275
276         if (!oflag) {
277                 printf("    %-10s= %.2f:%.2f    ",
278                     d->name, d->vol.left, d->vol.right);
279                 if (!MIX_ISREC(m, d->devno))
280                         printf(" pbk");
281                 if (MIX_ISREC(m, d->devno))
282                         printf(" rec");
283                 if (MIX_ISRECSRC(m, d->devno))
284                         printf(" src");
285                 if (MIX_ISMUTE(m, d->devno))
286                         printf(" mute");
287                 printf("\n");
288         } else {
289                 TAILQ_FOREACH(cp, &d->ctls, ctls) {
290                         (void)cp->print(cp->parent_dev, cp->name);
291                 }
292         }
293 }
294
295 static void
296 printrecsrc(struct mixer *m, int oflag)
297 {
298         struct mix_dev *dp;
299         int n = 0;
300
301         if (!m->recmask)
302                 return;
303         if (!oflag)
304                 printf("%s: ", m->mi.name);
305         TAILQ_FOREACH(dp, &m->devs, devs) {
306                 if (MIX_ISRECSRC(m, dp->devno)) {
307                         if (n++ && !oflag)
308                                 printf(", ");
309                         printf("%s", dp->name);
310                         if (oflag)
311                                 printf(".%s=+%s",
312                                     mixer_get_ctl(dp, C_SRC)->name, n ? " " : "");
313                 }
314         }
315         printf("\n");
316 }
317
318 static int
319 mod_dunit(struct mix_dev *d, void *p)
320 {
321         int dunit = *((int *)p);
322         int n;
323
324         if ((n = mixer_get_dunit()) < 0) {
325                 warn("cannot get default unit");
326                 return (-1);
327         }
328         if (mixer_set_dunit(d->parent_mixer, dunit) < 0) {
329                 warn("cannot set default unit to: %d", dunit);
330                 return (-1);
331         }
332         printf("%s: %d -> %d\n", ctl_dunit.name, n, dunit);
333
334         return (0);
335 }
336
337 static int
338 mod_volume(struct mix_dev *d, void *p)
339 {
340         struct mixer *m;
341         mix_ctl_t *cp;
342         mix_volume_t v;
343         const char *val;
344         char *endp, lstr[8], rstr[8];
345         float lprev, rprev, lrel, rrel;
346         int n;
347
348         m = d->parent_mixer;
349         cp = mixer_get_ctl(m->dev, C_VOL);
350         val = p;
351         n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
352         if (n == EOF) {
353                 warnx("invalid volume value: %s", val);
354                 return (-1);
355         }
356         lrel = rrel = 0;
357         if (n > 0) {
358                 if (*lstr == '+' || *lstr == '-')
359                         lrel = 1;
360                 v.left = strtof(lstr, &endp);
361                 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
362                         warnx("invalid volume value: %s", lstr);
363                         return (-1);
364                 }
365
366                 if (*endp == '%')
367                         v.left /= 100.0f;
368         }
369         if (n > 1) {
370                 if (*rstr == '+' || *rstr == '-')
371                         rrel = 1;
372                 v.right = strtof(rstr, &endp);
373                 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
374                         warnx("invalid volume value: %s", rstr);
375                         return (-1);
376                 }
377
378                 if (*endp == '%')
379                         v.right /= 100.0f;
380         }
381         switch (n) {
382         case 1:
383                 v.right = v.left; /* FALLTHROUGH */
384                 rrel = lrel;
385         case 2:
386                 if (lrel)
387                         v.left += m->dev->vol.left;
388                 if (rrel)
389                         v.right += m->dev->vol.right;
390
391                 if (v.left < MIX_VOLMIN)
392                         v.left = MIX_VOLMIN;
393                 else if (v.left > MIX_VOLMAX)
394                         v.left = MIX_VOLMAX;
395                 if (v.right < MIX_VOLMIN)
396                         v.right = MIX_VOLMIN;
397                 else if (v.right > MIX_VOLMAX)
398                         v.right = MIX_VOLMAX;
399
400                 lprev = m->dev->vol.left;
401                 rprev = m->dev->vol.right;
402                 if (mixer_set_vol(m, v) < 0)
403                         warn("%s.%s=%.2f:%.2f",
404                             m->dev->name, cp->name, v.left, v.right);
405                 else
406                         printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
407                            m->dev->name, cp->name, lprev, rprev, v.left, v.right);
408         }
409
410         return (0);
411 }
412
413 static int
414 mod_mute(struct mix_dev *d, void *p)
415 {
416         struct mixer *m;
417         mix_ctl_t *cp;
418         const char *val;
419         int n, opt = -1;
420
421         m = d->parent_mixer;
422         cp = mixer_get_ctl(m->dev, C_MUT);
423         val = p;
424         switch (*val) {
425         case '0':
426                 opt = MIX_UNMUTE;
427                 break;
428         case '1':
429                 opt = MIX_MUTE;
430                 break;
431         case '^':
432                 opt = MIX_TOGGLEMUTE;
433                 break;
434         default:
435                 warnx("%c: no such modifier", *val);
436                 return (-1);
437         }
438         n = MIX_ISMUTE(m, m->dev->devno);
439         if (mixer_set_mute(m, opt) < 0)
440                 warn("%s.%s=%c", m->dev->name, cp->name, *val);
441         else
442                 printf("%s.%s: %d -> %d\n",
443                     m->dev->name, cp->name, n, MIX_ISMUTE(m, m->dev->devno));
444
445         return (0);
446 }
447
448 static int
449 mod_recsrc(struct mix_dev *d, void *p)
450 {
451         struct mixer *m;
452         mix_ctl_t *cp;
453         const char *val;
454         int n, opt = -1;
455
456         m = d->parent_mixer;
457         cp = mixer_get_ctl(m->dev, C_SRC);
458         val = p;
459         switch (*val) {
460         case '+':
461                 opt = MIX_ADDRECSRC;
462                 break;
463         case '-':
464                 opt = MIX_REMOVERECSRC;
465                 break;
466         case '=':
467                 opt = MIX_SETRECSRC;
468                 break;
469         case '^':
470                 opt = MIX_TOGGLERECSRC;
471                 break;
472         default:
473                 warnx("%c: no such modifier", *val);
474                 return (-1);
475         }
476         n = MIX_ISRECSRC(m, m->dev->devno);
477         if (mixer_mod_recsrc(m, opt) < 0)
478                 warn("%s.%s=%c", m->dev->name, cp->name, *val);
479         else
480                 printf("%s.%s: %d -> %d\n",
481                     m->dev->name, cp->name, n, MIX_ISRECSRC(m, m->dev->devno));
482
483         return (0);
484 }
485
486 static int
487 print_volume(struct mix_dev *d, void *p)
488 {
489         struct mixer *m = d->parent_mixer;
490         const char *ctl_name = p;
491
492         printf("%s.%s=%.2f:%.2f\n",
493             m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
494
495         return (0);
496 }
497
498 static int
499 print_mute(struct mix_dev *d, void *p)
500 {
501         struct mixer *m = d->parent_mixer;
502         const char *ctl_name = p;
503
504         printf("%s.%s=%d\n", m->dev->name, ctl_name, MIX_ISMUTE(m, m->dev->devno));
505
506         return (0);
507 }
508
509 static int
510 print_recsrc(struct mix_dev *d, void *p)
511 {
512         struct mixer *m = d->parent_mixer;
513         const char *ctl_name = p;
514
515         if (!MIX_ISRECSRC(m, m->dev->devno))
516                 return (-1);
517         printf("%s.%s=+\n", m->dev->name, ctl_name);
518
519         return (0);
520 }