libcardiacarrest
is a trivial implementation of libpulse*
PulseAudio library API that
unconditionally (but gracefully) fails to connect to the PulseAudio daemon and does **nothing** else.
apulse and
pressureaudio (which uses apulse internally)
are an inspiration for this but unlike those two projects libcardiacarrest
is not an emulation
layer, all it does is it gracefully fails to provide the requested PulseAudio service hoping the
application would try something else (e.g. ALSA or JACK).
Many apps can be built with PulseAudio support, many people want that support, many other people (e.g. me) freak out at the single mention of linking against PulseAudio libraries (see below).
This project provides a way to produce software artifacts that work for both camps without building two different versions of everything.
The general framework is as follows:
- You use original PulseAudio headers (bundled) but link against
libcardiacarrest
libraries at link time. Unlike the originallibpulse
,libcardiacarrest
doesn’t permit static linking, so it’s impossible to f*ck it up. (You can skip this step if you don’t build your software yourself and you are not sufficiently paranoid. Note, however, that I found several instances where stuff was statically linked withlibpulse
while recompiling my system to uselibcardiacarrest
. You have been warned.) - If you want to use PulseAudio daemon you then simply run your apps in an environment where your
dynamic loader can find the original
libpulse
and everything works as expected. - But if instead you point your dynamic loader to
apulse/pressureaudio
then playback will ignore the daemon and will go over pure ALSA (but many other features will not work). - Finally, if you point your dynamic loader to
libcardiacarrest
then an app that useslibpulse
API will get “can’t connect to the daemon” error and (hopefully) will simply switch to another output plugin like ALSA or JACK (mpv
does exactly this, YMMV) and not just crash.
The intended use of this is
- you point your dynamic loader to
libcardiacarrest
by default, - you override
LD_LIBRARY_PATH
withapulse/pressureaudio
and the original libs on app-by-app basis.
But you can also use it backwards by running with the original libs by default and overriding with
libcardiacarrest
for selected apps (e.g., the browser) or whatever.
So you linked your mpv
with libcardiacarrest
.
- With original
libpulse
inLD_LIBRARY_PATH
:- You start
mpv
- it tries its
pulse
output plugin, that loads originallibpulse
libpulse
tries the daemon, starts it if it isn’t up alreadylibpulse
connects to it, sets everything upmpv
pushes decoded waves tolibpulse
libpulse
pushes them to the daemon- the daemon pushes them to ALSA
- ALSA pushes them to the kernel
- sound starts playing
- You start
- With
apulse libpulse
inLD_LIBRARY_PATH
:- You start
mpv
- it tries its
pulse
output plugin, that loadsapulse libpulse
apulse
starts emulating thelibpulse
API over ALSAmpv
pushes decoded waves toapulse
apulse
pushes them to ALSA- ALSA pushes them to the kernel
- sound starts playing
- You start
- With
libcardiacarrest libpulse
inLD_LIBRARY_PATH
:- You start
mpv
- it tries its
pulse
output plugin, that loadslibcardiacarrest libpulse
libcardiacarrest
pretends it tries the daemon, failsmpv
is like “hm, PulseAudio daemon is not available, let’s try something else”mpv
tries another output plugin, sayalsa
- it sets itself up
mpv
pushes decoded waves to ALSA- ALSA pushes them to the kernel
- sound starts playing
- You start
The point is that libcardiacarrest
is not an emulation layer, it just gracefully fails to provide
the requested service.
I don’t want a single executable bit of PulseAudio on my machine.
- PulseAudio is very anti-UNIX and anti-KISS (the opposite of “small, sharp tools” and “tools, not restrictions”).
- The code is enormous (ENORMOUS), over-engineered and of overall pretty shitty quality. It’s impossible to tell how many zero-days it contains.
- I have yet to meet a single person who still wanted to run PulseAudio on their machine after reading PA sources. All people I’ve shown the sources to were either laughing, crying or both at the end of the session. Yes, that includes the library (see src/pulse.c for some notes on the subject).
- I don’t like the ethics of pushing over-engineered software onto under-informed users who are unable to resist the tyranny of free cycles (binary packages).
- Hence, I consider any software that is deliberately PulseAudio-only to be unethical (if not
malicious). There are equivalent libraries with similarly convenient APIs (e.g.
openal
,libao
) that are similarly (actually, more) cross-platform that (transparently) provide a choice over audio backends. Shoving PulseAudio down everyone’s throats is not a service to users.
- See comments in src/pulse.c.
- Xorg is a daemon, Wayland is a library. Yay, Wayland! PulseAudio is a daemon, ALSA is a library.
Yay, PulseAudio? PulseAudio is exactly X11 for audio. The library-to-daemon protocol is similarly
complicated, protocol parsing code is written by hand (not autogenerated, like for
xcb
), when exposing PulseAudio to the network all of that code gets exposed to the network, to hide that shitty code from the scary net they introduce yet another complicated authentication protocol on top of it, exactly like X11.Imagine exposing init system’s administrative interface (e.g. systemd’s
systemctl
call) to an untrusted network. Sounds crazy, right? PulseAudio does pretty much this. PulseAudio is an audio daemon, but it’s also a plugin system, small init system (for its plugins) and authentication system at the same time. Thelibpulse
library also includes its own alternatives to iconv, glib and a bunch of slightly-different functions from libc. This is pure crazyness (very typical of Lennart Poettering creations, you might have heard ofsystemd
,dbus
andavahi
, all of that is him). Pushing uninformed people into running PulseAudio connected to untrusted data sources is unethical.Yep, I’m looking at you, Firefox developers, all your sandboxing means exactly nothing if the attacker gets to the PA socket.
- PulseAudio clients can gather all kinds of information about your hardware setup and other
connected clients (see
pa_context
API andpulse/introspect.h
in PA headers). - PulseAudio clients can helpfully start/restart the daemon for you.
- PulseAudio clients can reconfigure the daemon on the fly.
- PulseAudio clients can dynamically load and unload modules into the daemon. And PulseAudio ships with a bunch of crazy modules by default.
Even if all the code paths in the original libpulse
with no daemon available would have lead
straight to errors (which is not the case, it does a lot of shit even without a daemon and tries to
spawn a daemon at every corner) I would still prefer to link against libcardiacarrest
in case I
f*ck something up in my config.
Just read the sources (and then you, too, would try to stay away from it forever).
Use apulse and/or pressureaudio.
Like with X11, most likely, you do not. ALSA does software mixing by default for a decade now (but a
lot of documentation on the net is outdated). Primary sound cards, bluetooth headsets,
per-application soft volume, loopback (aka “desktop sound”) capture (comes out-of-the-box with
ALSA 1.1.6) and quasi-dynamic output switching can be configured with a couple of lines of
asound.conf
.
However, like with X11, sometimes you do need a daemon, and if you need an audio daemon you should
use sndio or JACK (I prefer v1
as it
doesn’t require dbus
). Their library-to-daemon protocols are much simpler (sndio’s is simply
trivial), library API is much cleaner (sndio’s one fits on a single page), they don’t lag, they
introduce no latency, and they also do MIDI, not just PCM. Unfortunately, neither of them is a
default choice in Ubuntu, so relatively few apps target them (but almost all pro-audio apps have
JACK
support).
For uncooperative apps you can use apulse
to go PA->ALSA
and then alsa-plugins
to go
ALSA->JACK
. Works for me, YMMV.
If you have to run PulseAudio at least don’t simply attach it to untrusted data sources. Including
the browser. I recommend you to run Firefox with apulse/pressureaudio
to make it play over pure
ALSA and then use pulse
module of alsa-plugins
to go from ALSA back to PA. Yes, I really do
recommend doing apulse->ALSA->PA->ALSA
instead of the normal libpulse->PA->ALSA
. This way
Firefox gets no direct access to PulseAudio daemon.
libcardiacarrest
only implements PA mainloop
and context
creation, everything else either
pretends to do something but does absolutely nothing, returns errors, or simply aborts the whole
process (when it’s unclear how to do something saner without writing a lot of code).
libcardiacarrest
wants to be the minimal amount of code required to make all the (sane) apps think
that libpulse
works, but the daemon is not available. Insane uses of the API like modifying lots
of PA-structures before ever trying to connect to the daemon will cause the process to abort, it’s
by design, fix your app (or report my misunderstanding of your sanity).
In total, libcardiacarrest
logic is implemented in ~100 LOC of fairly trivial code, everything
else is just PA cruft (yep, it’s a lot).
The only dependencies are libc
and glib
.
Everything. APIs are identical.
I’m running with libcardiacarrest
on all of my machines without any issues. Everything I ever
tried running under this works. Xfce desktop, KDE5 desktop, firefox, chromium, mpv, mplayer, cmus,
SDL1, SDL2, various official and unofficial pulseaudio tools, etc.
Everything that failed to work with this (pamixer, cubeb) was fixed and patches were accepted upstream.
LGPLv2.1+ (same as bundled PulseAudio headers). See COPYING.
Accepted both via GitHub issues and PRs, and via email (including patches formatted with
git-format-patch
).
- Read the src_shell{git log}. Emulate it.
- To update PulseAudio:
- Run src_shell{./3rdparty/pulseaudio-headers/update.sh /path/to/pulseaudio-source} from
the root of the repository.
A better way is to actually build actual
libpulse
and steal its headers. Alternatively, you can also run src_shell{diff -Nuar pulseaudio-r1/src/pulse pulseaudio-r2/src/pulse} between two PA releases and check if any interesting.h
were added or removed.The
update.sh
script should be good enough most of the time, though. PulseAudio doesn’t add/remove new headers that often. - Run src_shell{git diff ./3rdparty/pulseaudio-headers/pulse/version.h}.
- Update
version.mk
file accordingly. - Run src_shell{git diff ./3rdparty/pulseaudio-headers/pulse}.
- If something except
version.h
changed, updatesrc
accordingly. - If something non-trivial in
src
was changed, bumpCA_VERION
inversion.mk
.
- Run src_shell{./3rdparty/pulseaudio-headers/update.sh /path/to/pulseaudio-source} from
the root of the repository.