How to get virtual surround sound on your headphones plugged to your Linux rig

by Rudd-O published 2009/07/13 10:16:13 GMT+0, last modified 2013-06-26T03:24:21+00:00
Want to experience movies better but don't have the home theater? Now you can.

The latest release of ALSA (Advanced Linux Sound Architecture) ships a sound virtualization plugin that uses (to the extent that my hearing can tell) HRTF to virtualize multichannel sound.  This plugin is called vdownmix and it has the ability to mix down both quadraphonic and 5.1 surround sound so they sound more "spatial" with headphones.

Here, we'll learn how to set that up with Fedora 11 -- with the advantage that this solution works with PulseAudio just fine.  Users of other distributions may adapt these instructions to their packaging and configuration.  We assume, of course, that you don't have a home theater installed in your system -- rather, you only have a set of headphones plugged into your stereo or sound card.

Step 1: install the requisite software

Launch your software installer tool and look for a package named alsa-plugins-vdownmix.  Install it.  For those of you who like to use the command line:

yum install -y alsa-plugins-vdownmix

Step 2: set the software up

By default, this plugin comes with a neutered configuration that you have to explicitly request in your application.  This is silly.  The way we're going to set it up is rather simple: we're going to tell ALSA to disregard the standard multichannel outputs in the cards with this short ALSA configuration script:

pcm.!surround51 {
        @args [ CARD DEV ]
        @args.CARD {
                type string
                default {
                        @func getenv
                        vars [
                                ALSA_SURROUND51_CARD
                                ALSA_PCM_CARD
                                ALSA_CARD
                        ]
                        default {
                                @func refer
                                name defaults.pcm.surround51.card
                        }
                }
        }
        @args.DEV {
                type integer
                default {
                        @func igetenv
                        vars [
                                ALSA_SURROUND51_DEVICE
                        ]
                        default {
                                @func refer
                                name defaults.pcm.surround51.device
                        }
                }
        }
        type vdownmix
        slave.pcm {
                @func refer
                name {
                        @func concat
                        strings [
                                "cards."
                                {
                                        @func card_driver
                                        card $CARD
                                }
                                ".pcm.surround51." $DEV ":CARD=" $CARD
                        ]
                }
        }
        hint {
                description "Downmix to stereo 5.1 Surround output to Front, Center, Rear and Subwoofer speakers"
                device $DEV
        }
}

pcm.!surround40 {
        @args [ CARD DEV ]
        @args.CARD {
                type string
                default {
                        @func getenv
                        vars [
                                ALSA_SURROUND51_CARD
                                ALSA_PCM_CARD
                                ALSA_CARD
                        ]
                        default {
                                @func refer
                                name defaults.pcm.surround40.card
                        }
                }
        }
        @args.DEV {
                type integer
                default {
                        @func igetenv
                        vars [
                                ALSA_SURROUND51_DEVICE
                        ]
                        default {
                                @func refer
                                name defaults.pcm.surround40.device
                        }
                }
        }
        type vdownmix
        slave.pcm {
                @func refer
                name {
                        @func concat
                        strings [
                                "cards."
                                {
                                        @func card_driver
                                        card $CARD
                                }
                                ".pcm.surround51." $DEV ":CARD=" $CARD
                        ]
                }
        }
        hint {
                description "Downmix to stereo 4.0 Surround output to Front, Center, Rear and Subwoofer speakers"
                device $DEV
        }
}

 Save the contents of this script in the file /etc/alsa/pcm/vdownmix.conf (do this as root, obviously). Now we're going to tell ALSA to pick this configuration file up, by editing the file /etc/asound.conf. The original file should look like this:

#
# Place your global alsa-lib configuration here...
#

@hooks [
        {
                func load
                files [
                        "/etc/alsa/pulse-default.conf"
                ]
                errors false
        }
]

Add a new line as per this example:

#
# Place your global alsa-lib configuration here...
#

@hooks [
        {
                func load
                files [
                        "/etc/alsa/pcm/vdownmix.conf"
                        "/etc/alsa/pulse-default.conf"
                ]
                errors false
        }
]

You're finished with the setup. You can verify that the configuration has been picked up properly by running the command aplay -L on a console -- you should see the description of the surround51 definition begin with Downmix....

And that's it.  Your system is now properly set up to virtualize surround sound to your headphones. 

Now, you should know this configuration is not applied by default: PulseAudio still needs to be informed when you intend to play multichannel audio, otherwise PulseAudio performs non-spatialized mixdown of your multichannel audio.  Here's how you prevent that from happening.

How to activate and deactivate virtualization on the fly

Whenever you want to listen to multichannel audio on your headphones, you will want to tell PulseAudio to output multichannel audio on your gear.  You do this by:

  1. Opening the PulseAudio Volume Control
  2. Going to the Configuration tab (the last tab in the application)
  3. Selecting Output Analog 5.1 on the drop-down of your sound card

This lets PulseAudio know that you have a "surround-sound capable" sound card, which makes PulseAudio relay each sound channel straight into the ALSA downmixer, which in turn mixes each channel down to the correct virtual position.

Finally: when you go back to your speakers or to listening stereo music, select Output Analog Stereo again, and the spatialization will be disabled.

And that's it!