This project was part of Google Summer of Code 2021, but development is still active. The development report can be found on the FreeBSD Wiki. The reason behind this project is that the FreeBSD’s OSS mixer capabilities were really basic and outdated — even un/muting didn’t exist and one had to write custom scripts for such a basic task. Setting default audio devices had to be done by tweaking sysctls and programs needing to use the mixer required DIY implementations as there was no mixer library available. The project was merged to upstream on FreeBSD 14.0.
Table of contents
Kernel patches
Un/muting (commit)
I decided that un/muting is better to be implemented in sound(4) in order to
avoid having to write daemons or use files. The way this works is by
implementing the SOUND_MIXER_READ_MUTE
and SOUND_MIXER_WRITE_MUTE
ioctls,
which did exist in older OSS implementations, but were considered obselete.
One thing to note is that the functionality isn’t the same as their old one.
Older OSS versions had those 2 ioctls take/return an integer with a value of 0
or 1, which indicated whether the whole mixer is muted or not. My
implementation takes/returns a bitmask that tells which devices are muted. This
allows us to mute and unmute only the devices we want, instead of the whole
mixer. If you’re familiar with the OSS
API, this bitmask works the same way
as DEVMASK
, RECMASK
and RECSRC
.
Playback/recording mode information (commit)
Here I implemented a sysctl (dev.pcm.<N>.mode
) which gives information about
a device’s playback/recording mode. The rationale for this control is to
include /dev/sndstat
’s mixer information in the output of the new mixer(8).
The sysctl can return the following values (NOTE: these values are OR’ed
together if more than one mode is supported):
Value | Meaning |
---|---|
0x01 | Mixer |
0x02 | Playback device |
0x04 | Recording device |
Userland
mixer(3) implementation (commit)
mixer(3) provides a simple interface for working with the OSS mixer. The man page explains how the library works, including some examples, so there’s no need to repeat myself. You can see the library in action in the source code for mixer(8).
The basic structure of a program looks like this (link with -lmixer
):
#include <err.h>
#include <mixer.h>
int
main(int argc, char *argv[])
{
struct mixer *m;
const char *name = "/dev/mixer0";
if ((m = mixer_open(name)) == NULL)
err(1, "mixer_open(%s)", name);
/* do stuff */
mixer_close(m);
return (0);
}
mixer(8) rewrite (commit)
This implementation is a complete rewrite of the old mixer(8) utility. It now uses mixer(3) as a backend and implements all the new features the library provides. It’s got more command line options and works with a control-oriented interface inspired by OpenBSD’s mixerctl(8). Again, everything is detailed in the man page.
Old mixer(8) output:
$ mixer.old
Mixer vol is currently set to 85:85
Mixer pcm is currently set to 100:100
Mixer speaker is currently set to 74:74
Mixer line is currently set to 1:1
Mixer mic is currently set to 67:67
Mixer mix is currently set to 74:74
Mixer rec is currently set to 37:37
Mixer igain is currently set to 0:0
Mixer ogain is currently set to 100:100
Mixer monitor is currently set to 67:67
Recording source: mic
New mixer(8) output:
$ mixer
pcm0:mixer: <Realtek ALC662 rev3 (Analog 2.0+HP/2.0)> on hdaa0 kld snd_hda (play/rec) (default)
vol = 0.85:0.85 pbk
pcm = 1.00:1.00 pbk
speaker = 0.74:0.74 rec
line = 0.01:0.01 rec
mic = 0.67:0.67 rec src
mix = 0.74:0.74 rec
rec = 0.37:0.37 pbk
igain = 0.00:0.00 pbk
ogain = 1.00:1.00 pbk
monitor = 0.67:0.67 rec