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 (11391B)


      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 <sys/types.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/sysctl.h>
     28 
     29 #include <errno.h>
     30 #include <fcntl.h>
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <unistd.h>
     35 
     36 #include "mixer.h"
     37 
     38 #define	BASEPATH "/dev/mixer"
     39 
     40 static int _mixer_readvol(struct mixer *, struct mix_dev *);
     41 
     42 /*
     43  * Fetch volume from the device.
     44  */
     45 static int
     46 _mixer_readvol(struct mixer *m, struct mix_dev *dev)
     47 {
     48 	int v;
     49 
     50 	if (ioctl(m->fd, MIXER_READ(dev->devno), &v) < 0)
     51 		return (-1);
     52 	dev->vol.left = MIX_VOLNORM(v & 0x00ff);
     53 	dev->vol.right = MIX_VOLNORM((v >> 8) & 0x00ff);
     54 
     55 	return (0);
     56 }
     57 
     58 /*
     59  * Open a mixer device in `/dev/mixerN`, where N is the number of the mixer.
     60  * Each device maps to an actual pcm audio card, so `/dev/mixer0` is the
     61  * mixer for pcm0, and so on.
     62  *
     63  * @param name		path to mixer device. NULL or "/dev/mixer" for the
     64  *			the default mixer (i.e `hw.snd.default_unit`).
     65  */
     66 struct mixer *
     67 mixer_open(const char *name)
     68 {
     69 	struct mixer *m = NULL;
     70 	struct mix_dev *dp;
     71 	const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
     72 	int i;
     73 
     74 	if ((m = calloc(1, sizeof(struct mixer))) == NULL)
     75 		goto fail;
     76 
     77 	if (name != NULL) {
     78 		/* `name` does not start with "/dev/mixer". */
     79 		if (strncmp(name, BASEPATH, strlen(BASEPATH)) != 0) {
     80 			m->unit = -1;
     81 		} else {
     82 			/* `name` is "/dev/mixer" so, we'll use the default unit. */
     83 			if (strncmp(name, BASEPATH, strlen(name)) == 0)
     84 				goto dunit;
     85 			m->unit = strtol(name + strlen(BASEPATH), NULL, 10);
     86 		}
     87 		(void)strlcpy(m->name, name, sizeof(m->name));
     88 	} else {
     89 dunit:
     90 		if ((m->unit = mixer_get_dunit()) < 0)
     91 			goto fail;
     92 		(void)snprintf(m->name, sizeof(m->name), "/dev/mixer%d", m->unit);
     93 	}
     94 
     95 	if ((m->fd = open(m->name, O_RDWR)) < 0)
     96 		goto fail;
     97 
     98 	m->devmask = m->recmask = m->recsrc = 0;
     99 	m->f_default = m->unit == mixer_get_dunit();
    100 	m->mode = mixer_get_mode(m->unit);
    101 	/* The unit number _must_ be set before the ioctl. */
    102 	m->mi.dev = m->unit;
    103 	m->ci.card = m->unit;
    104 	if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0) {
    105 		memset(&m->mi, 0, sizeof(m->mi));
    106 		strlcpy(m->mi.name, m->name, sizeof(m->mi.name));
    107 	}
    108 	if (ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0)
    109 		memset(&m->ci, 0, sizeof(m->ci));
    110 	if (ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 ||
    111 	    ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 ||
    112 	    ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 ||
    113 	    ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
    114 		goto fail;
    115 
    116 	TAILQ_INIT(&m->devs);
    117 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
    118 		if (!MIX_ISDEV(m, i))
    119 			continue;
    120 		if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL)
    121 			goto fail;
    122 		dp->parent_mixer = m;
    123 		dp->devno = i;
    124 		dp->nctl = 0;
    125 		if (_mixer_readvol(m, dp) < 0)
    126 			goto fail;
    127 		(void)strlcpy(dp->name, names[i], sizeof(dp->name));
    128 		TAILQ_INIT(&dp->ctls);
    129 		TAILQ_INSERT_TAIL(&m->devs, dp, devs);
    130 		m->ndev++;
    131 	}
    132 
    133 	/* The default device is always "vol". */
    134 	m->dev = TAILQ_FIRST(&m->devs);
    135 
    136 	return (m);
    137 fail:
    138 	if (m != NULL)
    139 		(void)mixer_close(m);
    140 
    141 	return (NULL);
    142 }
    143 
    144 /*
    145  * Free resources and close the mixer.
    146  */
    147 int
    148 mixer_close(struct mixer *m)
    149 {
    150 	struct mix_dev *dp;
    151 	int r;
    152 
    153 	r = close(m->fd);
    154 	while (!TAILQ_EMPTY(&m->devs)) {
    155 		dp = TAILQ_FIRST(&m->devs);
    156 		TAILQ_REMOVE(&m->devs, dp, devs);
    157 		while (!TAILQ_EMPTY(&dp->ctls))
    158 			(void)mixer_remove_ctl(TAILQ_FIRST(&dp->ctls));
    159 		free(dp);
    160 	}
    161 	free(m);
    162 
    163 	return (r);
    164 }
    165 
    166 /*
    167  * Select a mixer device. The mixer structure keeps a list of all the devices
    168  * the mixer has, but only one can be manipulated at a time -- this is what
    169  * the `dev` in the mixer structure field is for. Each time a device is to be
    170  * manipulated, `dev` has to point to it first.
    171  *
    172  * The caller must manually assign the return value to `m->dev`.
    173  */
    174 struct mix_dev *
    175 mixer_get_dev(struct mixer *m, int dev)
    176 {
    177 	struct mix_dev *dp;
    178 
    179 	if (dev < 0 || dev >= m->ndev) {
    180 		errno = ERANGE;
    181 		return (NULL);
    182 	}
    183 	TAILQ_FOREACH(dp, &m->devs, devs) {
    184 		if (dp->devno == dev)
    185 			return (dp);
    186 	}
    187 	errno = EINVAL;
    188 
    189 	return (NULL);
    190 }
    191 
    192 /*
    193  * Select a device by name.
    194  *
    195  * @param name		device name (e.g vol, pcm, ...)
    196  */
    197 struct mix_dev *
    198 mixer_get_dev_byname(struct mixer *m, const char *name)
    199 {
    200 	struct mix_dev *dp;
    201 
    202 	TAILQ_FOREACH(dp, &m->devs, devs) {
    203 		if (!strncmp(dp->name, name, sizeof(dp->name)))
    204 			return (dp);
    205 	}
    206 	errno = EINVAL;
    207 
    208 	return (NULL);
    209 }
    210 
    211 /*
    212  * Add a mixer control to a device.
    213  */
    214 int
    215 mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name,
    216     int (*mod)(struct mix_dev *, void *),
    217     int (*print)(struct mix_dev *, void *))
    218 {
    219 	struct mix_dev *dp;
    220 	mix_ctl_t *ctl, *cp;
    221 
    222 	/* XXX: should we accept NULL name? */
    223 	if (parent_dev == NULL) {
    224 		errno = EINVAL;
    225 		return (-1);
    226 	}
    227 	if ((ctl = calloc(1, sizeof(mix_ctl_t))) == NULL)
    228 		return (-1);
    229 	ctl->parent_dev = parent_dev;
    230 	ctl->id = id;
    231 	if (name != NULL)
    232 		(void)strlcpy(ctl->name, name, sizeof(ctl->name));
    233 	ctl->mod = mod;
    234 	ctl->print = print;
    235 	dp = ctl->parent_dev;
    236 	/* Make sure the same ID or name doesn't exist already. */
    237 	TAILQ_FOREACH(cp, &dp->ctls, ctls) {
    238 		if (!strncmp(cp->name, name, sizeof(cp->name)) || cp->id == id) {
    239 			errno = EINVAL;
    240 			return (-1);
    241 		}
    242 	}
    243 	TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls);
    244 	dp->nctl++;
    245 
    246 	return (0);
    247 }
    248 
    249 /*
    250  * Same as `mixer_add_ctl`.
    251  */
    252 int
    253 mixer_add_ctl_s(mix_ctl_t *ctl)
    254 {
    255 	if (ctl == NULL)
    256 		return (-1);
    257 
    258 	return (mixer_add_ctl(ctl->parent_dev, ctl->id, ctl->name,
    259 	    ctl->mod, ctl->print));
    260 }
    261 
    262 /*
    263  * Remove a mixer control from a device.
    264  */
    265 int
    266 mixer_remove_ctl(mix_ctl_t *ctl)
    267 {
    268 	struct mix_dev *p;
    269 
    270 	if (ctl == NULL) {
    271 		errno = EINVAL;
    272 		return (-1);
    273 	}
    274 	p = ctl->parent_dev;
    275 	if (!TAILQ_EMPTY(&p->ctls)) {
    276 		TAILQ_REMOVE(&p->ctls, ctl, ctls);
    277 		free(ctl);
    278 	}
    279 
    280 	return (0);
    281 }
    282 
    283 /*
    284  * Get a mixer control by id.
    285  */
    286 mix_ctl_t *
    287 mixer_get_ctl(struct mix_dev *d, int id)
    288 {
    289 	mix_ctl_t *cp;
    290 
    291 	TAILQ_FOREACH(cp, &d->ctls, ctls) {
    292 		if (cp->id == id)
    293 			return (cp);
    294 	}
    295 	errno = EINVAL;
    296 
    297 	return (NULL);
    298 }
    299 
    300 /*
    301  * Get a mixer control by name.
    302  */
    303 mix_ctl_t *
    304 mixer_get_ctl_byname(struct mix_dev *d, const char *name)
    305 {
    306 	mix_ctl_t *cp;
    307 
    308 	TAILQ_FOREACH(cp, &d->ctls, ctls) {
    309 		if (!strncmp(cp->name, name, sizeof(cp->name)))
    310 			return (cp);
    311 	}
    312 	errno = EINVAL;
    313 
    314 	return (NULL);
    315 }
    316 
    317 /*
    318  * Change the mixer's left and right volume. The allowed volume values are
    319  * between MIX_VOLMIN and MIX_VOLMAX. The `ioctl` for volume change requires
    320  * an integer value between 0 and 100 stored as `lvol | rvol << 8` --  for
    321  * that reason, we de-normalize the 32-bit float volume value, before
    322  * we pass it to the `ioctl`.
    323  *
    324  * Volume clumping should be done by the caller.
    325  */
    326 int
    327 mixer_set_vol(struct mixer *m, mix_volume_t vol)
    328 {
    329 	int v;
    330 
    331 	if (vol.left < MIX_VOLMIN || vol.left > MIX_VOLMAX ||
    332 	    vol.right < MIX_VOLMIN || vol.right > MIX_VOLMAX) {
    333 		errno = ERANGE;
    334 		return (-1);
    335 	}
    336 	v = MIX_VOLDENORM(vol.left) | MIX_VOLDENORM(vol.right) << 8;
    337 	if (ioctl(m->fd, MIXER_WRITE(m->dev->devno), &v) < 0)
    338 		return (-1);
    339 	if (_mixer_readvol(m, m->dev) < 0)
    340 		return (-1);
    341 
    342 	return (0);
    343 }
    344 
    345 /*
    346  * Manipulate a device's mute.
    347  *
    348  * @param opt		MIX_MUTE mute device
    349  *			MIX_UNMUTE unmute device
    350  *			MIX_TOGGLEMUTE toggle device's mute
    351  */
    352 int
    353 mixer_set_mute(struct mixer *m, int opt)
    354 {
    355 	switch (opt) {
    356 	case MIX_MUTE:
    357 		m->mutemask |= (1 << m->dev->devno);
    358 		break;
    359 	case MIX_UNMUTE:
    360 		m->mutemask &= ~(1 << m->dev->devno);
    361 		break;
    362 	case MIX_TOGGLEMUTE:
    363 		m->mutemask ^= (1 << m->dev->devno);
    364 		break;
    365 	default:
    366 		errno = EINVAL;
    367 		return (-1);
    368 	}
    369 	if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0)
    370 		return (-1);
    371 	if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0)
    372 		return (-1);
    373 
    374 	return 0;
    375 }
    376 
    377 /*
    378  * Modify a recording device. The selected device has to be a recording device,
    379  * otherwise the function will fail.
    380  *
    381  * @param opt		MIX_ADDRECSRC add device to recording sources
    382  *			MIX_REMOVERECSRC remove device from recording sources
    383  *			MIX_SETRECSRC set device as the only recording source
    384  *			MIX_TOGGLERECSRC toggle device from recording sources
    385  */
    386 int
    387 mixer_mod_recsrc(struct mixer *m, int opt)
    388 {
    389 	if (!m->recmask || !MIX_ISREC(m, m->dev->devno)) {
    390 		errno = ENODEV;
    391 		return (-1);
    392 	}
    393 	switch (opt) {
    394 	case MIX_ADDRECSRC:
    395 		m->recsrc |= (1 << m->dev->devno);
    396 		break;
    397 	case MIX_REMOVERECSRC:
    398 		m->recsrc &= ~(1 << m->dev->devno);
    399 		break;
    400 	case MIX_SETRECSRC:
    401 		m->recsrc = (1 << m->dev->devno);
    402 		break;
    403 	case MIX_TOGGLERECSRC:
    404 		m->recsrc ^= (1 << m->dev->devno);
    405 		break;
    406 	default:
    407 		errno = EINVAL;
    408 		return (-1);
    409 	}
    410 	if (ioctl(m->fd, SOUND_MIXER_WRITE_RECSRC, &m->recsrc) < 0)
    411 		return (-1);
    412 	if (ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
    413 		return (-1);
    414 
    415 	return (0);
    416 }
    417 
    418 /*
    419  * Get default audio card's number. This is used to open the default mixer
    420  * and set the mixer structure's `f_default` flag.
    421  */
    422 int
    423 mixer_get_dunit(void)
    424 {
    425 	size_t size;
    426 	int unit;
    427 
    428 	size = sizeof(int);
    429 	if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0)
    430 		return (-1);
    431 
    432 	return (unit);
    433 }
    434 
    435 /*
    436  * Change the default audio card. This is normally _not_ a mixer feature, but
    437  * it's useful to have, so the caller can avoid having to manually use
    438  * the sysctl API.
    439  *
    440  * @param unit		the audio card number (e.g pcm0, pcm1, ...).
    441  */
    442 int
    443 mixer_set_dunit(struct mixer *m, int unit)
    444 {
    445 	size_t size;
    446 
    447 	size = sizeof(int);
    448 	if (sysctlbyname("hw.snd.default_unit", NULL, 0, &unit, size) < 0)
    449 		return (-1);
    450 	/* XXX: how will other mixers get updated? */
    451 	m->f_default = m->unit == unit;
    452 
    453 	return (0);
    454 }
    455 
    456 /*
    457  * Get sound device mode (none, play, rec, play+rec). Userland programs can
    458  * use the MIX_MODE_* flags to determine the mode of the device.
    459  */
    460 int
    461 mixer_get_mode(int unit)
    462 {
    463 	char buf[64];
    464 	size_t size;
    465 	unsigned int mode;
    466 
    467 	(void)snprintf(buf, sizeof(buf), "dev.pcm.%d.mode", unit);
    468 	size = sizeof(unsigned int);
    469 	if (sysctlbyname(buf, &mode, &size, NULL, 0) < 0)
    470 		return (0);
    471 
    472 	return (mode);
    473 }
    474 
    475 /*
    476  * Get the total number of mixers in the system.
    477  */
    478 int
    479 mixer_get_nmixers(void)
    480 {
    481 	struct mixer *m;
    482 	oss_sysinfo si;
    483 
    484 	/*
    485 	 * Open a dummy mixer because we need the `fd` field for the
    486 	 * `ioctl` to work.
    487 	 */
    488 	if ((m = mixer_open(NULL)) == NULL)
    489 		return (-1);
    490 	if (ioctl(m->fd, OSS_SYSINFO, &si) < 0) {
    491 		(void)mixer_close(m);
    492 		return (-1);
    493 	}
    494 	(void)mixer_close(m);
    495 
    496 	return (si.nummixers);
    497 }