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

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 }