Boring Introduction

After having heard a presentation about all the nice features of a surround sound system at the Fraunhofer Institute, I decided to get one. Turns out it ain't easy at all with Linux - especially if you're completely new to all that nifty new surround stuff. Here's what I wanted:

Seems easy. It isn't. Just finding out what hardware is supported with Linux is a real pain. The good story is: if you know how to work it all, it works like a charm (it does here, YMMV). I will explain here what hardware I have got and how it all works.

Disambiguation

There seems to be a lot of confusion done in the "HiFi" sector - for those of you who got confused on the way, here's a little disambiguation:

Hardware Description

First off, the topology again:

Topology of the Dolby Surround System

Here's the hardware I've got:

Wiring the hardware all up is straightforward. I don't think there's anything you should pay special attention to - it's very easy. Compiling the CMI8738 driver (CONFIG_SND_CMIPCI, called "C-Media 8738, 8338") into your kernel is also easy. Getting sound is a bit harder, then...

First Step: Getting Sound over the Optical Fiber

Alright, I assume you've compiled the ALSA driver into your kernel (maybe your distribution provides modules, that's fine, too). So when running dmesg, you see the soundcard appear:

joe [~]: dmesg
[...]
ALSA device list:
  #0: C-Media PCI CMI8738-MC6 (model 55) at 0xac00, irq 18

Notice that I have disabled my boards on board soundcard, as it unnecessarily occupies an interrupt (which it should never trigger, however...) and makes everything more confusing. This is probably what you want to do, too. Another way to check if your card was properly recognized:

joe [~]: cat /proc/asound/cards
 0 [CMI8738MC6     ]: CMI8738-MC6 - C-Media PCI CMI8738-MC6
                      C-Media PCI CMI8738-MC6 (model 55) at 0xac00, irq 18

The Terratec sound card is represented by ALSA as three different Hardware devices: hw:0,0, hw:0,1 and hw:0,2. These have mnemonic names, too:

joe [~]: cat /proc/asound/pcm
00-02: CMI8738-MC6 : C-Media PCI IEC958 : playback 1 : capture 1
00-01: CMI8738-MC6 : C-Media PCI 2nd DAC : playback 1
00-00: CMI8738-MC6 : C-Media PCI DAC/ADC : playback 1 : capture 1

Hence the naming scheme you will encounter in the following parts.

So start up xmms or whatever sound player you use, crank up the volume and listen to... probably you'll listen to nothing. This is because the optical output isn't active, yet. So before you make changes now, remember: turn down the volume of your receiver. Optical fiber TOSLINK output (also known as IEC985) has to be activated first. So open up your Gnome Volume Control (or whichever ALSA mixer frontend you're using) and hit Edit, Preferences. Select all IEC985 tracks which are available to be visible. This is important: you're only controlling visibility here, do not activate each one. When they're visible, go to the "Switches" tab and only turn on "IEC958 Output". If you select "IEC958 Loop", optical output will be active, but it will loop the input through to the output, so you will not get what you expect (probably get nothing at all). It should look like this:

Picture of Gnome-Mixer with the IEC958 Output checkmark set

Now start xmms again, then slowly turn up the volume on the amplifier - and it should work, beautifully. However, you may notice that you cannot control the volume of the digital signal through the mixer setting. In fact, it's completely ignoring what volume you tell it to be. This is fine - S/PDIF does not support volume control over the wire. It always sends the 100% signal out so you get best quality (no clipping here, of course, as the signal is digital). Sometimes you still wish to control the volume, although you might in almost all cases keep it on 100% - if this is your wish, it will be granted within the...

Second Step: Software Controlling the Volume

Basically, you have to create a /etc/asound.conf file, which looks like the following:

# Actually existent hardware
pcm.HW_pcidacadc {
    type hw
    card 0
    device 0
}
pcm.HW_pci2nddac {
    type hw
    card 0
    device 1
}
pcm.HW_pciiec958 {
    type hw
    card 0
    device 2
}

# Softvolume Control
pcm.Filter_SoftwareVolume {
    type softvol
    slave.pcm "HW_pciiec958"
    control {
        name "Software Volume Mixer"
        card 0
    }
}

# Make last filter the default device
pcm.!default {
    type plug
    slave.pcm "Filter_SoftwareVolume"
}

What this pretty primitive file does is the following: the first three blocks define us aliases for the actual hardware devices the Terratec soundcard provides. The fourth block says we want to insert a software volume control filter there, the fifth block then says that the default ALSA device should be our Software-Volume-Mixer-Enabled soundcard. The mixer then gives you another control named "Software Volume Mixer" (who'd have guessed) through which you can control the output volume:

Picture of Gnome-Mixer with the Software Volume Control bar

Notice that the "Master" and "PCM" bars have no effect at all - they only control the analog output volume. The digital output volume is controlled solely through the software volume plugin bar. Also please take note that you'd usually want to leave this bar at 100% volume, as this gives the best quality.

Third Step: The Dolby Digital Experience

Through this setup, you will get Dolby Digital only and only if you choose to use the so-called "pass-through" mode. This means you have a movie which is already in the AC3 audio codec (well encoded AVI videos are, if the original - usually DVD source - supports it). You will find some legal sample videos here (you might just as well search for "Dolby Trailer" and find others, too). Once you have downloaded one, try to play it with mplayer:

joe [~]: mplayer dolbycanyon.vob
[...]
==========================================================================
Opening audio decoder: [liba52] AC3 decoding with liba52
Using SSE optimized IMDCT transform
Using MMX optimized resampler
AUDIO: 48000 Hz, 2 ch, s16le, 448.0 kbit/29.17% (ratio: 56000->192000)
Selected audio codec: [a52] afm: liba52 (AC3-liba52)
==========================================================================
[...]

Whoa - this is not what you want at all! What it tells you: the audio has native Dolby Digital (AC3) available, but this (nice, original) stream is decoded by liba52 and transformed into plain stereo for output. This is done natively because many people do not have an hardware Dolby Digital decoder and would therefore not be able to hear any sound - they must use the software decoding - you don't need to. You can tell mplayer to directly pass the Dolby Digital stream unmodified to your receiver...

joe [~]: mplayer -ac hwac3 dolbycanyon.vob
[...]
==========================================================================
Forced audio codec: hwac3
Opening audio decoder: [hwac3] AC3/DTS pass-through S/PDIF
No accelerated IMDCT transform found
hwac3: switched to AC3, 448000 bps, 48000 Hz
AUDIO: 48000 Hz, 2 ch, ac3, 448.0 kbit/29.17% (ratio: 56000->192000)
Selected audio codec: [hwac3] afm: hwac3 (AC3 through S/PDIF)
==========================================================================

Now this should sound way better than before - as now you get the whole power of all six channels. You can clearly see that the audio is routed through the receiver as it is (pass-though) and the decoder does all the work. On the Yamaha amplifier you can see that it's working as as soon as you start the movie, the display changes from "PRO LOGIC" to "DOLBY DGTL".

In practice, you will notice that not all movies have AC3 streams in them - and probably you didn't care anyways, because you could not play them back natively. Now you will need to make a difference in the way you play your movies (pass-through or just plain playback?). There's an easy solution: let mplayer decide. You give him your wanted codecs in order of precedence and it will choose whichever is available. This is done by issuing a

joe [~]: mplayer -ac hwac3,hwdts, somemovie.avi

Notice the trailing "," after "hwdts". It tells mplayer that it should first try hardware-supported AC3 (if it's natively encoded in the stream), then try hardware DTS (if available) and only if those two fail (this is the meaning of the last comma) it should use whatever works.

Fourth Step: Dolby Digital for Music

As I said, the above setup only works if you have already sources available - like movies - which have the AC3 stream already in there for you. What if you want to listen to music and use your subwoofer or the rear channels? There is a solution: let ALSA do all the work and let ALSA encode the stream (live) to AC3 and then send it over the wire. For this, you will need the ALSA-plugin package. It's available under Gentoo as the media-plugins/alsa-plugins package. Be sure to emerge it with the "ffmpeg" USE flag activated so the AC3-plugin is included. You will then need to include this plugin into your configuration file. In fact, it's pretty easy:

# Actually existent hardware
pcm.HW_pcidacadc {
    type hw
    card 0
    device 0
}
pcm.HW_pci2nddac {
    type hw
    card 0
    device 1
}
pcm.HW_pciiec958 {
    type hw
    card 0
    device 2
}

# Encode AC3 -> Directly on hardware
pcm.Filter_A52Encode {
    type a52
    bitrate 448
    card 0
}
pcm.Filter_SimpleUpmix {
    type upmix
    slave.pcm "Filter_A52Encode"
    channels 6 
}

# Make last filter the default device
pcm.!default {
    type plug
    slave.pcm "Filter_SimpleUpmix"
}

Notice that the "type a52" plugin encodes the received stream to AC3 and sends it directly to the S/PDIF output of the soundcard. The "upmix" plugin just means that simple upmixing is done, giving you standard stereo output plus the center and subwoofer output (which is the left and right input channel mixed in a 1:1 ratio).

Fifth Step: An Advanced Configuration - Fasten Your Seatbelt

You might already have guessed that I'm only telling you half the truth as the configuration files of ALSA are quite easy - so far. Get ready to get the real deal. This is the ALSA configuration which I am actually using. Oh, yes, it works. Without much ado:

# Actually existent hardware
pcm.HW_pcidacadc {
    type hw
    card 0
    device 0
}
pcm.HW_pci2nddac {
    type hw
    card 0
    device 1
}
pcm.HW_pciiec958 {
    type hw
    card 0
    device 2
}

# Mix channels for playback from multiple sources -> Directly on hardware
pcm.Filter_DMix {
    type dmix
    ipc_key 1024
    slave.pcm "HW_pciiec958"
}
pcm.Filter_SoftwareVolumeDMix {
    type softvol
    slave.pcm "Filter_DMix"
    control {
        name "Software"
        card 0
    }
}

# Encode AC3 -> Directly on hardware
pcm.Filter_A52Encode {
    type a52
    bitrate 448
    card 0
}
pcm.Filter_SimpleUpmix {
    type upmix
    slave.pcm "Filter_A52Encode"
    channels 6 
}
pcm.Filter_RoutingUpmix {
    type route
    slave.pcm "Filter_A52Encode"
    slave.channels 6
    ttable.0.0 1
    ttable.1.1 1
    ttable.0.2 1
    ttable.1.3 1
    ttable.0.4 0.5
    ttable.1.4 0.5
    ttable.0.5 0.5
    ttable.1.5 0.5
}

# Softvolume Filter, as digital volume control is not possible over S/PDIF
pcm.Filter_SoftwareVolume {
    type softvol
#   slave.pcm "Filter_SimpleUpmix"
    slave.pcm "Filter_RoutingUpmix"
    control {
        name "Software"
        card 0
    }
}

# Duplexer filter for simultanious playback/recording
pcm.Filter_Duplex {
    type asym
    playback.pcm "Filter_SoftwareVolume"
    capture.pcm "HW_pcidacadc"
}

# Rate Converter to 48kHz, needed for some applications
pcm.Filter_RateConvert {
    type rate
    slave {
        pcm "Filter_Duplex"
        rate 48000
    }
}

# Make last filter the default device
pcm.!default {
    type plug
    slave.pcm "Filter_RateConvert"
}

pcm.!noac3 {
    type plug
    slave.pcm "Filter_SoftwareVolumeDMix"
}

# Also do so for OSS
pcm.dsp "Filter_RateConvert"
pcm.dsp1 "Filter_RateConvert"
ctl.dsp {
    type plug
    slave.pcm "HW_pcidacadc"
}
ctl.mixer {
    type plug
    slave.pcm "HW_pcidacadc"
}

Please keep breathing. If this is all a little too much for you, here's a graph which explains it nicely:

asound.conf graphically

What it all means is this. I define two interfaces, "default" (the default interface, kind of obvious) and "noac3". These interfaces can be used by any application which supports ALSA. So when I playback through the "default" interface, this is what happens: the data goes into the Filter_Duplex filter first. The duplex filter takes care that recording and playback can be done simultaneously. The recording input is taken directly from the hardware while the playback output goes on to the Filter_SoftwareVolume. There the software is volume corrected, if necessary. From there are two ways - I currently use the right one (Filter_RoutingUpmix), but the left one (Filter_SimpleUpmix) is also possible; you'd only need to comment out one line in the config file above. This routing upmix-plugin does not only take care of the upmixing to the subwoofer and center box, but also makes the rear boxes output music, too. The six-channel upmixed data is then passed to the Filter_A52Encode plugin, which encodes the stream into Dolby Digital and then passes it directly on the to soundcard.

It is not possible to combine A52-upmixing (which needs to operate directly on the hardware) and the "dmix" software mixing (which also needs direct hardware underlying). That you cannot use the "dmix" plugin means that if you have playback in one application the other application cannot use the soundcard (only one app at a time). Usually this is not of great importance anyways (for me it isn't) - but if you need it, you can configure applications to not take advantage of AC3 encoding and instead use only the software volume correction (Filter_SoftwareVolumeDMix) and pass that on to the Filter_DMix digital mixer. This then operates directly on the IEC958 output of your soundcard.

Now, one could complicate the configuration file further by adding a "dsnoop" filter - this is kind of like the "dmix" filter, but in the inverse direction: it would therefore be possible to have multiple applications record from the soundcard the same stream. Needless to say that I never (ever!) used that and therefore didn't include it. No doubt some people will need it, however - adding it should be fairly easy.

Sixth Step: Troubleshooting?

During my way to get the above configuration working, there was lots of work to get done. I encountered some problems and found out how to fix them - here are the solutions.

"There is no audio at all"

Are you sure the optical output is enabled ("IEC 958 Output" Switch)? Try looking into the optical fiber cable to find out - is a red light coming out of it? If not, optical output is disabled. Enable it before you can listen to anything. If it is active and you still don't hear anything - are you sure your amplifier has the correct source selected and it is not muted?

"The a52 plugin doesn't work"

Did you install it at all? The a52 plugin is an additional piece of software which for Gentoo can be found in the "alsa-plugins" package. Note that you need to enable the "ffmpeg" USE flag to get the a52 plugin.

"MPlayer plays videos A/V-async, video is very slow"

I encountered this with some videos while others played beautifully. It turned out that videos with 48kHz sample rate played nicely, which 44kHz videos were giving me a lot of trouble: the video got "stuck", it played extremely slow. The audio played nicely for about 100 seconds, then the A/V got so out of sync that mplayer just terminated with weird errors. The solution I used is: insert the sample rate converter (called "Filter_RateConvert" above) to force conversion to 48kHz for all output sources. The problems then just disappear.

Seventh Step: Enjoy

If you get it all working, do what I did: get a cold beer from the fridge, open it up, tune in your favourite movie (the Space Station IMAX DVD has great AC3 sound and is pretty cool) or concert, crank up the volume until it blasts your socks off and enjoy! Have fun!