Dynamic Range Compression for PulseAudio

OK, so I did a good thing (got a working dynamic range compression going for PulseAudio) then I did a bad thing (forgot where I put the source).  As a side-effect of rebuilding a machine here, I found it!  So, to make amends, I packaged it up nicely and I’ll try to make a good post here to help you use it too.  Oh, and I’ve been beta testing it on a few machines for a couple years…

So, as they say: What is all this, then?

Dynamic Range Compression (sometimes erroneously called ‘volume normalization’) is a technique by which you take a sound stream and make the quiets louder and the louds quieter.  If you’ve ever watched a movie that alternated between whispers and loud explosions and you had to keep getting up to adjust the amplifier, you’ll understand what this is about.  Similarly, if you’ve ever tried listening to classical music in the car and can’t hear parts of it over the road noise, but the trumpets blast out the tweeters, you know which this might be useful.

Just to be clear: it does destroy the ‘intent’ of the volume (dynamic) range.  That’s fine, we don’t want to keep fiddling with the volume control, and it can always be turned off.

What is PulseAudio?  It’s the dominant sound server (controller) used by most desktop linux distributions today. It can do a bunch of fancy stuff, but its flat_volumes support is … it doesn’t seem to work the way people expect it to.

Fortunately, PulseAudio has the ability to establish arbitrary “sinks”, or sound sources, and those can be configured using units of the LADSPA sound filter library.  Unfortunately, PulseAudio needs a mono filter (if you had e.g.. 7.1 sound, it would run 8 of them), and the LADSPA SWH Plugins comes only with a stereo limiter filter.

So, this project consisted of these steps:
1. figuring out the right route and LADSPA filters to use
2. hacking up a mono filter from the LADSPA source
3. figuring out how to get pulseaudio to use them
4. figuring out how to get applications to use them.

So, each, in order:

LADSPA WHAT?

LADSPA is the Linux Audio Developers Simple Plugin API.  The ladspa-swh-plugins package is a collection of plugins that conforms to that API, hosted by Steve over at http://plugin.org.uk/.   The swh plugins features a dynamic range compressor, so I thought it would be that easy, but two things came up.  First, I realized that just doing dynamic range compression wasn’t good enough – to make this sound good also required the use of a limiter.  Fortunately there was a limiter of the right type there too! But, that limiter was in stereo, and I needed mono, so I had to modify the stereo plugin to work with just a mono channel. Anyway, that was then.

To start, you’ll need my copy of the plugin package – I’ve bumped the version from 0.4.15 to 0.4.16 to make the package manager from overwriting it on updates, but I don’t have Steve’s blessing yet to do this.  I’ll be following up with him shortly, to see if he’ll carry the version of the plugin I made.  I mostly use Fedora and Fedora has a strict “fix it upstream” mantra, so if I want this to work nicely in the future, I want to get the package upstreamed.  I don’t want to maintain a fork!

Get what you need from here.

If you’re on any of the Fedora-derived operating systems (Fedora, RHEL, CentOS, Scientific Linux, maybe SUSE) you can just get the SRPM package.   If you don’t have a build environment already, do:

yum install rpmdevtools

then run

rpmdev-setuptree

which will make a ~/rpmbuild directory.  Download the SRPM into the SRPMS directory and run:

yum-builddep

on it to install all the build dependencies.

Then install the source with:

rpm -ihv ladspa-swh-plugins-0.4.16-0.fc19.src.rpm

and change to the ../SPECS directory and run:

rpmbuild -ba ladspa-swh-plugins.spec

If all goes well, you should have some new RPM files, which you can install with

rpm -Uhv ../RPMS/x86_64/ladspa-swh-plugins-0.4.16-0.fc19.x86_64.rpm

If you’re on a 32-bit machine, those x86_64 tags will look like i686.  Adjust accordingly.  I can’t cover everything that might come up in a build system here, but the above should work if everything is running normally.

If you’re on a non-RPM system, grab instead the patch file, and the extra source files and get the tar file from the main site, and then merge them and do a standard package build. If anybody wants to contribute a .deb file, or any other package, just ping me and I’ll add it here.

PulseAudio Configuration

At this point you’ll have the plugins installed and are ready to setup PulseAudio.  This may well be the ugliest and is certainly the least-automated part of the process.  It’s not just us, everybody has to deal with this when using these kinds of plugins with PulseAudio.  OK, let’s just dive in to the configuration file and then we’ll break it down piece-by-piece.  This will go at the end of your /etc/pulse/default.pa file:

load-module module-ladspa-sink sink_name=ladspa_output.fast_lookahead_limiter_mono_4621.fastLookaheadLimiterMono master=alsa_output.pci-0000_00_14.2.analog-stereo plugin=fast_lookahead_limiter_mono_4621 label=fastLookaheadLimiterMono control=14,-15,0.8
load-module module-ladspa-sink sink_name=ladspa_output.dyson_compress_1403.dysonCompress master=ladspa_output.fast_lookahead_limiter_mono_4621.fastLookaheadLimiterMono plugin=dyson_compress_1403 label=dysonCompress control=0,1,0.5,0.99
set-default-sink ladspa_output.dyson_compress_1403.dysonCompress

set-sink-volume alsa_output.pci-0000_00_14.2.analog-stereo 65536
set-sink-mute alsa_output.pci-0000_00_14.2.analog-stereo 0

OK, so we have to understand the stack here. First you have some audio hardware connected to your PCI bus. That audio hardware probably has a few inputs and outputs. You’re going to put sound on one of those outputs.  We’re going to put the limiter on that hardware output.  Then we’re going to put the compressor on that limiter.  Then we’re going to feed the application’s audio output to the compressor.  Through this chain, the application’s output will come out your speakers, after first going through the compressor, then the limiter, then the hardware, down and your speaker cable.

Got that?

Even though pulse already knows about your hardware device, there is no way to get that as a variable in the config file, so we have to hard code it (collective *UGH*!).  We’re going to have to know the PCI address of the card and also its output device number.  Let’s look at the hardware I have on this machine:

$ lspci  | grep Audio
00:01.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series]
00:14.2 Audio device: Advanced Micro Devices, Inc. [AMD] FCH Azalia Controller (rev 01)

The first device listed here is on the HDMI output.  That’s fine if you’re hooking up to a stereo with HDMI outputs or have a monitor that supports it, but I’ve got a pair of desktop speakers (I’m still rockin’ the beige Apple Design Speakers I bought in ’93) hooked up to the audio out of the motherboard’s analog out cables, so I want the second line there, not the HDMI line.  Let’s look at the master parameter on the first line again:

master=alsa_output.pci-0000_00_14.2.analog-stereo

And then the second line again:

00:14.2 Audio device: Advanced Micro Devices, Inc. [AMD] FCH Azalia Controller (rev 01)

OK, you see it, that’s how the PCI address gets encoded into the first line. So, what about ‘analog stereo’? For that, we need to figure out what pa_card_profile’s you have. Hopefully this will work for you:

$ echo "list-sinks" | pacmd  | grep name:
        name: <alsa_output.pci-0000_00_14.2.analog-stereo>

Which is the entire name that you’ll need to use. If you only have one audio device, this can give you all the information you need.  If you think you’re lucky, try this script and see if it generates all that for you automatically.

Go ahead and tune the config file lines to match your hardware.

The last two lines are optional, but I often find that PulseAudio comes up with with volumes down and the devices muted, so this helps avoid the ‘no sound on startup’ problem. Speaking of which … before you tear your hair out if this doesn’t work, you may need to go in to alsamixer and unmute channels (‘m’ key), turn up volumes (up arrow) and do so for each sound card device (change with F6). This can even be a problem out of the blue, so remember this one. You should run alsactl store when you have the settings right, but that doesn’t always seem to stick either. *sigh* – linux audio is very flexible and at least 98% reliable.

Anyhow, if your config file is correct, you can now run:

pulseaudio -k
pulseaudio --start

to bounce PulseAudio and get it running on the LADSPA filters.

Usage

Now you’re going to need to get apps to use it. Even though it’s listed as the default sink in the config file, that only works occasionally. More reliable (and manual) is the pavucontrol (PulseAudio Volume Control) program.

yum -y install pavucontrol

or get it from your favorite package manager. Run it, and you should see, on the Output Devices tab, two more output devices, one for the limiter and one for the compressor. Go ahead and plonk the “Set as fallback” checkmark icon on the compressor device. Set them both to 100% if they’re not already. Remember this – once in a while Pulse will wig out and you’ll have to bounce one or both of those volume sliders from 100% to something else and back to 100% to get the sound to come back to life.

If everything worked, you should see something like this:

It’s just some silly meme video that was going around back then, but the important parts are to look at how to assign the playing application to the combined playback device (PA will remember this choice for next time). And it’s a decent example of a piece of sound with a wide dynamic range. If you watch the three meters, you’ll see the bottom one fluctuating wildly, the middle one, less so, and the top one (the actual audio device) barely moving much at all.

That’s our goal here, and I hope it’s working well for you. This can be used to save your ears on headphones, to not wake up the family while you’re watching a movie on your HTPC late at night, or just to keep your desktop volumes sane. Leave some feedback and let us know how it’s working for you.

Future work should include: getting the patches upstreamed, improving the module parameter values, making pulse easier to configure for these plugins, and making this easy for linux desktop users to install and utilize in their distros.