]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mixer/mixer.c
bsddialog: import snapshot 2021-12-05
[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 <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include <mixer.h>
33
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 *);
48
49 static const mix_ctl_t ctl_dunit = {
50         .parent_dev     = NULL,
51         .id             = -1,
52         .name           = "default_unit",
53         .mod            = mod_dunit,
54         .print          = NULL
55 };
56
57 int
58 main(int argc, char *argv[])
59 {
60         struct mixer *m;
61         mix_ctl_t *cp;
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;
66         int ch;
67
68         while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
69                 switch (ch) {
70                 case 'a':
71                         aflag = 1;
72                         break;
73                 case 'd':
74                         dunit = strtol(optarg, NULL, 10);
75                         if (errno == EINVAL || errno == ERANGE)
76                                 err(1, "strtol");
77                         dflag = 1;
78                         break;
79                 case 'f':
80                         name = optarg;
81                         break;
82                 case 'o':
83                         oflag = 1;
84                         break;
85                 case 's':
86                         sflag = 1;
87                         break;
88                 case 'h': /* FALLTROUGH */
89                 case '?':
90                 default:
91                         usage();
92                 }
93         }
94         argc -= optind;
95         argv += optind;
96
97         /* Print all mixers and exit. */
98         if (aflag) {
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);
105                         initctls(m);
106                         if (sflag)
107                                 printrecsrc(m, oflag);
108                         else {
109                                 printall(m, oflag);
110                                 if (oflag)
111                                         printf("\n");
112                         }
113                         (void)mixer_close(m);
114                 }
115                 return (0);
116         }
117
118         if ((m = mixer_open(name)) == NULL)
119                 err(1, "mixer_open: %s", name);
120
121         initctls(m);
122
123         if (dflag && ctl_dunit.mod(m->dev, &dunit) < 0)
124                 goto parse;
125         if (sflag) {
126                 printrecsrc(m, oflag);
127                 (void)mixer_close(m);
128                 return (0);
129         }
130
131 parse:
132         while (argc > 0) {
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);
139                         goto next;
140                 }
141                 /* Input: `dev`. */
142                 if (p == NULL) {
143                         printdev(m, 1);
144                         pall = 0;
145                         goto next;
146                 }
147                 ctlstr = strsep(&p, "=");
148                 if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
149                         warnx("%s.%s: no such control", devstr, ctlstr);
150                         goto next;
151                 }
152
153                 /* Input: `dev.control`. */
154                 if (p == NULL) {
155                         (void)cp->print(cp->parent_dev, cp->name);
156                         pall = 0;
157                         goto next;
158                 }
159                 valstr = p;
160                 /* Input: `dev.control=val`. */
161                 cp->mod(cp->parent_dev, valstr);
162 next:
163                 free(p);
164                 argc--;
165                 argv++;
166         }
167
168         if (pall)
169                 printall(m, oflag);
170         (void)mixer_close(m);
171
172         return (0);
173 }
174
175 static void __dead2
176 usage(void)
177 {
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());
181         exit(1);
182 }
183
184 static void
185 initctls(struct mixer *m)
186 {
187         struct mix_dev *dp;
188         int rc = 0;
189
190 #define C_VOL 0
191 #define C_MUT 1
192 #define C_SRC 2
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);
197         }
198         if (rc) {
199                 (void)mixer_close(m);
200                 err(1, "cannot make controls");
201         }
202 }
203
204 static void
205 printall(struct mixer *m, int oflag)
206 {
207         struct mix_dev *dp;
208
209         printminfo(m, oflag);
210         TAILQ_FOREACH(dp, &m->devs, devs) {
211                 m->dev = dp;
212                 printdev(m, oflag);
213         }
214 }
215
216 static void
217 printminfo(struct mixer *m, int oflag)
218 {
219         int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
220
221         if (oflag)
222                 return;
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);
228
229         if (m->mode != 0)
230                 printf(" (");
231         if (m->mode & MIX_MODE_PLAY)
232                 printf("play");
233         if ((m->mode & playrec) == playrec)
234                 printf("/");
235         if (m->mode & MIX_MODE_REC)
236                 printf("rec");
237         if (m->mode != 0)
238                 printf(")");
239
240         if (m->f_default)
241                 printf(" (default)");
242         printf("\n");
243 }
244
245 static void
246 printdev(struct mixer *m, int oflag)
247 {
248         struct mix_dev *d = m->dev;
249         mix_ctl_t *cp;
250
251         if (!oflag) {
252                 char buffer[32];
253                 (void)snprintf(buffer, sizeof(buffer),
254                     "%s.%s", d->name, "volume");
255
256                 printf("    %-16s= %.2f:%.2f\t",
257                     buffer, d->vol.left, d->vol.right);
258                 if (!MIX_ISREC(m, d->devno))
259                         printf(" pbk");
260                 if (MIX_ISREC(m, d->devno))
261                         printf(" rec");
262                 if (MIX_ISRECSRC(m, d->devno))
263                         printf(" src");
264                 if (MIX_ISMUTE(m, d->devno))
265                         printf(" mute");
266                 printf("\n");
267         } else {
268                 TAILQ_FOREACH(cp, &d->ctls, ctls) {
269                         (void)cp->print(cp->parent_dev, cp->name);
270                 }
271         }
272 }
273
274 static void
275 printrecsrc(struct mixer *m, int oflag)
276 {
277         struct mix_dev *dp;
278         int n = 0;
279
280         if (!m->recmask)
281                 return;
282         if (!oflag)
283                 printf("%s: ", m->mi.name);
284         TAILQ_FOREACH(dp, &m->devs, devs) {
285                 if (MIX_ISRECSRC(m, dp->devno)) {
286                         if (n++ && !oflag)
287                                 printf(", ");
288                         printf("%s", dp->name);
289                         if (oflag)
290                                 printf(".%s=+%s",
291                                     mixer_get_ctl(dp, C_SRC)->name,
292                                     n ? " " : "");
293                 }
294         }
295         printf("\n");
296 }
297
298 static int
299 mod_dunit(struct mix_dev *d, void *p)
300 {
301         int dunit = *((int *)p);
302         int n;
303
304         if ((n = mixer_get_dunit()) < 0) {
305                 warn("cannot get default unit");
306                 return (-1);
307         }
308         if (mixer_set_dunit(d->parent_mixer, dunit) < 0) {
309                 warn("cannot set default unit to: %d", dunit);
310                 return (-1);
311         }
312         printf("%s: %d -> %d\n", ctl_dunit.name, n, dunit);
313
314         return (0);
315 }
316
317 static int
318 mod_volume(struct mix_dev *d, void *p)
319 {
320         struct mixer *m;
321         mix_ctl_t *cp;
322         mix_volume_t v;
323         const char *val;
324         char lstr[8], rstr[8];
325         float lprev, rprev, lrel, rrel;
326         int n;
327
328         m = d->parent_mixer;
329         cp = mixer_get_ctl(m->dev, C_VOL);
330         val = p;
331         n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
332         if (n == EOF) {
333                 warnx("invalid volume value: %s", val);
334                 return (-1);
335         }
336         lrel = rrel = 0;
337         if (n > 0) {
338                 if (*lstr == '+' || *lstr == '-')
339                         lrel = rrel = 1;
340                 v.left = strtof(lstr, NULL);
341
342                 /* be backwards compatible */
343                 if (strstr(lstr, ".") == NULL)
344                         v.left /= 100.0f;
345         }
346         if (n > 1) {
347                 if (*rstr == '+' || *rstr == '-')
348                         rrel = 1;
349                 v.right = strtof(rstr, NULL);
350
351                 /* be backwards compatible */
352                 if (strstr(rstr, ".") == NULL)
353                         v.right /= 100.0f;
354         }
355         switch (n) {
356         case 1:
357                 v.right = v.left; /* FALLTHROUGH */
358         case 2:
359                 if (lrel)
360                         v.left += m->dev->vol.left;
361                 if (rrel)
362                         v.right += m->dev->vol.right;
363
364                 if (v.left < MIX_VOLMIN)
365                         v.left = MIX_VOLMIN;
366                 else if (v.left > MIX_VOLMAX)
367                         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;
372
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);
378                 else
379                         printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
380                            m->dev->name, cp->name, lprev, rprev, v.left, v.right);
381         }
382
383         return (0);
384 }
385
386 static int
387 mod_mute(struct mix_dev *d, void *p)
388 {
389         struct mixer *m;
390         mix_ctl_t *cp;
391         const char *val;
392         int n, opt = -1;
393
394         m = d->parent_mixer;
395         cp = mixer_get_ctl(m->dev, C_MUT);
396         val = p;
397         switch (*val) {
398         case '0':
399                 opt = MIX_UNMUTE;
400                 break;
401         case '1':
402                 opt = MIX_MUTE;
403                 break;
404         case '^':
405                 opt = MIX_TOGGLEMUTE;
406                 break;
407         default:
408                 warnx("%c: no such modifier", *val);
409                 return (-1);
410         }
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);
414         else
415                 printf("%s.%s: %d -> %d\n",
416                     m->dev->name, cp->name, n, MIX_ISMUTE(m, m->dev->devno));
417
418         return (0);
419 }
420
421 static int
422 mod_recsrc(struct mix_dev *d, void *p)
423 {
424         struct mixer *m;
425         mix_ctl_t *cp;
426         const char *val;
427         int n, opt = -1;
428
429         m = d->parent_mixer;
430         cp = mixer_get_ctl(m->dev, C_SRC);
431         val = p;
432         switch (*val) {
433         case '+':
434                 opt = MIX_ADDRECSRC;
435                 break;
436         case '-':
437                 opt = MIX_REMOVERECSRC;
438                 break;
439         case '=':
440                 opt = MIX_SETRECSRC;
441                 break;
442         case '^':
443                 opt = MIX_TOGGLERECSRC;
444                 break;
445         default:
446                 warnx("%c: no such modifier", *val);
447                 return (-1);
448         }
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);
452         else
453                 printf("%s.%s: %d -> %d\n",
454                     m->dev->name, cp->name, n, MIX_ISRECSRC(m, m->dev->devno));
455
456         return (0);
457 }
458
459 static int
460 print_volume(struct mix_dev *d, void *p)
461 {
462         struct mixer *m = d->parent_mixer;
463         const char *ctl_name = p;
464
465         printf("%s.%s=%.2f:%.2f\n",
466             m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
467
468         return (0);
469 }
470
471 static int
472 print_mute(struct mix_dev *d, void *p)
473 {
474         struct mixer *m = d->parent_mixer;
475         const char *ctl_name = p;
476
477         printf("%s.%s=%d\n", m->dev->name, ctl_name, MIX_ISMUTE(m, m->dev->devno));
478
479         return (0);
480 }
481
482 static int
483 print_recsrc(struct mix_dev *d, void *p)
484 {
485         struct mixer *m = d->parent_mixer;
486         const char *ctl_name = p;
487
488         if (!MIX_ISRECSRC(m, m->dev->devno))
489                 return (-1);
490         printf("%s.%s=+\n", m->dev->name, ctl_name);
491
492         return (0);
493 }