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; char *name = ...; if ((m = mixer_open(name)) == NULL) err(1, "mixer_open: %s", name); ... ... 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