mixer

FreeBSD OSS mixer library implementation and a complete rewrite of mixer(8)
git clone git://git.margiolis.net/mixer.git
Log | Files | Refs | README | LICENSE

mixer.c (10380B)


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