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