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.

11 thoughts on “Dynamic Range Compression for PulseAudio”

  1. Hi 🙂

    This is just what I was looking for, thanks for putting the source on the net. I need a good limiter while processing files with sox. Sox also won’t work with ladspa plugins unless they are mono. Fast lookup limiter is a very good quality plugin, but it is stereo only.

    I have been banging on it for 6 hours now but I can not get the source to compile on a Ubuntu 13.04 system. The original swh-0.4.15 compiles without problems. Could you please give us detailed Ubuntu build instructions, it would be nice if you could provide us with a tar – package with the patches already applied.

    Thanks for you good work 🙂

    1. Hi, Mikael – I don’t have an Ubuntu packaging experience but I’d be happy to post/link to a package if somebody can do one. The patch itself is meant to apply to the vanilla tree, which should be distro agnostic (unless Ubuntu introduces its own changes?). What’s happening for you? Is the patch not applying successfully? Is the build failing after a successful patch? Maybe you could pastebin some errors. -Bill

  2. Hi Bill 🙂

    I now managed to get the source to compile 🙂

    Thanks again for your work. I looked through the patches and saw that you put some serious effort in getting this to work. I really appreciate that you put the source on the net. This version of the plugin is very valuable for me 🙂

    However I think you maybe should try to build the patched source yourself on other platforms that SWH supports. The unpatched swh-0.4.15 can be compiled with a simple: ./configure && make but after applying your patches this does no longer work. It complains not being able to find “aclocal-1.8”. I suspect Steve Harris won’t accept the patch if it breaks building on some supported platforms and I really hope Steve accepts your patch to the official distribution 🙂

    However the workaround I found to get the plugin to compile is below and also other information for using the plugin with sox.

    Here are the steps to compile the source. You can just copy + paste the commands below to your terminal to get the job done 🙂 This has been tested on Linux Mint 13 (a Ubuntu based distro) and it installs the official SWH – plugin package first, then compiles the patched SWH source and copies only the mono version of the Fast Lookahead Limiter to the distros ladspa-plugin directory.

    – sudo apt-get install build-essential automake libfftw3-dev libgsm1-dev swh-plugins
    – wget http://www.bfccomputing.com/downloads/linux/pulse/ladspa-swh-plugins-0.4.16-0.fc19.src.rpm
    – rpm2cpio ladspa-swh-plugins-0.4.16-0.fc19.src.rpm | cpio -idmv
    – tar xzvf swh-plugins-0.4.16.tar.gz
    – tar xzvf fast_lookahead_limiter_mono_4621.tar.gz -C swh-plugins-0.4.16/
    – cp *.patch swh-plugins-0.4.16/
    – cd swh-plugins-0.4.16/
    – patch -p0 < ladspa-swh-plugins-libgsm.patch
    – patch -p0 < swh-plugins-plugin-Makefile.am.patch
    – patch -p1 < swh-plugins-0.4.15-pic.patch
    – patch -p1 < swh-plugins-0.4.15-riceitdown.patch
    – patch -p1 < swh-plugins-fast_lookahead_limiter_mono.patch
    – autoreconf -f -i
    – ./configure
    – make
    – sudo cp .libs/fast_lookahead_limiter_mono_4621.so /usr/lib/ladspa/
    – sudo chmod 644 /usr/lib/ladspa/fast_lookahead_limiter_mono_4621.so

    How to use the limiter with sox
    ——————————–

    This example limits peaks to -10 dBFS:

    sox INPUTFILE.wav OUTPUTFILE.wav ladspa fast_lookahead_limiter_mono_4621 0 -10 0.01 0 0

    Parameters to the limiter are:
    – Input gain (range -20 dB to + 20 dB). Gain that is applied to the input stage. Can be used to trim gain to bring it roughly under the limit or to push the signal against the limit.
    – Limiter ceiling (range -20 dB to 0 dB). The maximum output amplitude. Peaks over this level will be attenuated as smoothly as possible to bring them as close as possible to this level.
    – Release time (range 0.01 to 2.0 sec).
    – unknown (Use value of 0)
    – unknown (Use value of 0)

    Sox seems to allow you to use values that are out of the range the developer defined. Use out of range values with caution.

    The limiter has a fixed 5 ms delay meaning that all audio moves 5 ms later from its original position. This means that the last 5 ms of audio is always cut off when using this limiter.

    You can move the audio back to its original position with sox "trim" effect, but you can't get back the lost 5 ms from the end of the file.

    The 5 ms delay is only a concern if you use the limiter on audio that is later synced with video. However it may not be worth it to compensate for the delay. To put the 5 ms delay in perspective an equal delay also happens when the listener is 1.7 meters away from the speakers.

    To compensate for the 5 ms delay while limiting audio use:

    sox INPUTFILE.wav OUTPUTFILE.wav ladspa fast_lookahead_limiter_mono_4621 0 -10 0.01 0 0 trim 0.005

  3. Hi again 🙂

    Today I tested the compilation method on Ubuntu 12.04 LTS 32 bit and Ubuntu 12.04 LTS 64 bit.

    On these platforms two more tools must be installed before compilation: rpm2cpio and libtool. The corrected recipe is below.

    Yesterday I tried this compilation method on UbuntuStudio 13.04 64 bit and it produced a non working plugin. Sox refused to load it. This may be due to some pecurialities of the UbuntuStudio platform, I will examine the cause of the problem later.

    The corrected build recipe for Ubuntu 12.04:

    – sudo apt-get install build-essential automake libfftw3-dev libgsm1-dev swh-plugins rpm2cpio libtool
    – wget http://www.bfccomputing.com/downloads/linux/pulse/ladspa-swh-plugins-0.4.16-0.fc19.src.rpm
    – rpm2cpio ladspa-swh-plugins-0.4.16-0.fc19.src.rpm | cpio -idmv
    – tar xzvf swh-plugins-0.4.16.tar.gz
    – tar xzvf fast_lookahead_limiter_mono_4621.tar.gz -C swh-plugins-0.4.16/
    – cp *.patch swh-plugins-0.4.16/
    – cd swh-plugins-0.4.16/
    – patch -p0 < ladspa-swh-plugins-libgsm.patch
    – patch -p0 < swh-plugins-plugin-Makefile.am.patch
    – patch -p1 < swh-plugins-0.4.15-pic.patch
    – patch -p1 < swh-plugins-0.4.15-riceitdown.patch
    – patch -p1 < swh-plugins-fast_lookahead_limiter_mono.patch
    – autoreconf -f -i
    – ./configure
    – make
    – sudo cp .libs/fast_lookahead_limiter_mono_4621.so /usr/lib/ladspa/
    – sudo chmod 644 /usr/lib/ladspa/fast_lookahead_limiter_mono_4621.so

  4. This all looks great! I’d really like a system-wide compressor, like the one found in VLC. But beeing a non-programming newbie to linux, I’d really preffer a .deb or a ppa-repository for simple install. This looks like a great app, and I’m sure a LOT of people would love this. So, why not set up a ready-to-install package for this?

    1. We need PulseAudio enhancements to make that possible, Niklas. Look for the “UGH” in the text above to find that part.

  5. First off:

    Thanks for a really nice site and good work on the codeing 🙂

    problem is tho .. i cant get it to patch in lmde (basicly debian jessie)

    i got fast-lookahead to work with stereo .. but there is no way to get it to work with surround channels or 3 channels .. (im on a 2.1 setup)

    when im trying to patch it it complains about not being able to change usergroup on makefile.am and just fails straight after that… even when chmod -R 777 and chown -R nobody:nogroup .. still doesnt work .. banging my head in a wall for a couple of hours now.. any ideas?

  6. This looks great, but unfortunately I’ve also had difficulty installing. @Marcus_Brandberg – I’m also using debian jessie; if you read this perhaps you could post your install steps? I tried to follow @Mikael’s instructions, but received the following error:
    $ rpm2cpio ladspa-swh-plugins-0.4.16-0.fc19.src.rpm | cpio -idmv
    error: bad option ‘archcolor’ at (null):94
    cpio: premature end of archive

    Any suggestions?

  7. Hi, Bill!

    I’m a young gentleman in Stockholm, Sweden that switched from Microsoft OS about a year ago, and I really needed to have real-time compression today, since I was increasing/decreasing volume on every clip I watched, so thank you so much! I will recommend this at the drop of a hat.
    Really helpful!

Leave a Reply

Your email address will not be published. Required fields are marked *