Using sndio on Linux

Once upon a time, the good folks at OpenBSD decided they needed a new, better sound system. Alexandre Ratchov stepped up and wrote one, and thus was sndio born.

Sndio’s goals, as documented in its design paper and the slides from the talk Ratchov gave on it, both linked from its homepage, are to provide sound mixing (so that multiple programs can play sound at once), and provide a simple and fault-tolerant audio API and tools that use it, and be highly responsive (no sound stuttering). Its wikipedia page provides a fuller overview of its features.

Because simplicity is one of its key goals, the codebase is tiny, particularly compared to pulseaudio, but it is a very capable sound system. Complexity, after all, is usually a bad thing when it comes to software. Now, the purpose of this article isn’t to denigrate pulseaudio—I didn’t switch for any political reasons, but out of technical curiosity. If there is ever a comments section available on this blog, please refrain from talkin’ smack on Lennart Poettering and his work, because having been part of the Linux community for years, I am intensely bored by the bizarrely emotional debates pulseaudio inspires.

Note that on Linux, sndio occupies a similar space to pulseaudio: it’s a user-facing daemon sitting atop the kernel’s ALSA system; it is not a replacement for ALSA.

That said, let’s move on to the actual work of setting it up. Note that I assume you’re already familiar with how to build software.

Setting Up

I recommend checking configure’s available options rather than copying my lines exactly.

# git it
git clone http://caoua.org/git/sndio
cd sndio

These next steps are optional; if you want a file called .aucat_cookie to live in your home directory forever, despite the fact that hidden files are a misfeature that grew out of a bug, ignore these instructions. Otherwise, download my li'l patch and apply it, ideally in a new branch so you can keep rebasing it atop upstream’s changes. Note that the patch is extremely stupid and assumes that $XDG_CACHE_HOME is set.

# better cookie location
git checkout -b patched --track master
git apply 0001-put-cookie-somewhere-better.patch
git commit -m 'Put the aucat cookie somewhere better'

Finally, you can actually build.

# build it
./configure --enable-alsa
make
# This symlink doesn't happen automagically in latest git
make -C libsndio libsndio.so

Testing

Testing sndio may be troublesome if pulseaudio is running. If you run into problems, try stopping pulse and start again.

# Run the daemon, logging errors to stderr
./sndiod/sndiod -dd
# In a new shell...
./aucat/aucat -i /usr/share/sounds/alsa/Front_Center.wav \
	& ./aucat/aucat -i /usr/share/sounds/alsa/Front_Left.wav

You should here these sounds at the same time.

Installing

Rather than using bare make to install the program, I’d recommend using your distribution’s packaging tools. If you use Arch, for instance, you can write a PKGBUILD for those instructions, or git my PKGBUILD (along with the patch) from AUR and build it with makepkg. As always, make sure you actually read the PKGBUILD first; it is just a bash script, and you should make sure you know what it’s doing.

Making other programs play nicely

You can change the volume using alsamixer, since sndio is running on top of alsa. For per-program control, someone’s going to need to set up a midi control program for sndio.

Some programs will need to be rebuilt if you want them to work with sndio. Fortunately, two of the big ones for me, mpd and mpv, are easy. Both mpd and mpv include an --enable-sndio flag when building.

Games, the only other programs I allow to make noise on my computer, were a bit harder, until I realized that many of them use libao or openal under the hood. Therefore, it was just a matter of rebuilding those two libraries, both of which autodetected the presence of sndio, and then configuring them. Note that you may need to build “multilib” 32-bit versions of libao and alsoft as well, because many game devs to this day inexplicably refuse to support native 64-bit processors.

/etc/libao.conf
default_driver=sndio
/etc/openal/alsoft.conf
# Change the "drivers = " line to...
drivers = sndio,alsa,pulse

On Arch, I recommend downloading the PKGBUILDs for these programs from Arch’s official package page, adding --enable-sndio if need be, and finally adding groups=modified to the variables at the top. Then, make sure to set IgnoreGroup = modified in /etc/pacman.conf. This way, you’ll get notified when the official version is updated (so you don’t have to trawl for new releases yourself) but your version won’t be automatically overwritten.

Finally, you can sort of loop sound from non-sndio programs back to alsa through sndio by adding the following snippet to /etc/asound.conf

/etc/asound.conf
pcm.sndio {
	type asym
	playback.pcm "sndio-play"

	hint {
		show on description "sndio"
	}
}

pcm.sndio-play {
	type plug
	slave {
		pcm "sndio-raw"
		rate 48000
		format s16_le
		channels 2
	}
}

pcm.sndio-raw {
	type file
	slave.pcm null

	format raw
	file "| aucat -f snd/0 -i -"
} pcm.default sndio

Finishing Touches

Once you’re satisfied that everything is set up to your liking, you will probably want to disable pulse, which may be set up to start automatically. That's bad, because when X starts, pulse may still run, changing the audio volume to random levels.

Speaking of volume levels, make sure alsa is set to save and restore the volume at shutdown and boot, respectively, since pulse won’t be handling that.

# set up volume restoration
systemctl enable alsa-restore

Final notes

I’ve been pretty happy with sndio so far; I really like the fact that it’s a well-tempered audio server with a tiny, legible code base. If you’re curious…and don’t mind doing a little extra set-up…give it a shot!