Digital Crossover/EQ with Open-Source Software: HOWTO

The following setup can be used to implement a 2- or 3-way digital crossover (with equalization) for active loudspeakers.  It runs on a linux PC and uses only free, open-source software.  My design goal was a small, self-contained unit that can live on a shelf beside an amplifier: it runs without need of a display, keyboard, or mouse, and it looks like this:

The PC is the small box on the left.  It’s silent, always on, and requires no user interaction.

One could use this system to implement the xover/eq for the Linkwitz Labs Pluto 2.1 or LX521.  For those interested, I’ll post specific configuration files in the Owners’ area of the Orion/Pluto/LX521 Forum: together with this how-to, those files can be used to build a DSP version of the Analog Signal Processor (ASP) for those loudspeakers.  I’ve written a long article detailing my development of the LX521 implementation; my Pluto implementation is similar.


Schematically the setup looks like this:

The input can be an analog (line-level) or digital (S/PDIF) feed to a sound card, or else a digital audio file stored on disk and decoded by the PC.  The crossover/eq are performed (in real time) in software on the PC, with output (4- or 6-channel, digital or analog) to a multi-channel sound card.  In practice a single sound card can handle both inputs and outputs.

Volume control can be upstream (in digital) or downstream (analog), depending on how you want to configure things.  The PC can also act as a media server, programmed so that all output goes through the crossover/eq.



You’ll need a PC equipped with multi-channel audio output.  For a stand-alone unit as in the photo above, you’ll want a small, fanless PC (no fan noise) with a solid state drive (SSD; even less noise and faster startup time).  The minimum hardware specs are modest: I’ve built LX521 implementations on a Shuttle XS35v2, a Zotac zbox, and even a Raspberry Pi.

For the audio interface you’ll need a sound card with at least 6 output channels.  Any card capable of 5.1 or 7.1 surround sound will suffice.  It’s best if the card supports a sample rate of 44.1kHz (the standard for CD audio) since re-sampling to anything else takes a lot of extra cpu cycles to do properly.  Make sure the card has a S/PDIF input if you want to use that as your audio source.  I’ve had success with a Diamond Xtreme 7.1 usb, a Creative XFi Surround 5.1 usb, and the built-in Intel AC97 audio codec in my desktop PC.

Alternatively one can send the crossover outputs over hdmi, which supports up to 8 digital channels.  In this case you’ll need an additional device for multi-channel digital-to-analog conversion: in my own LX521 setup I run the hdmi from my Zotac zbox to an hdmi audio de-embedder, whose analog outputs go to an AT6012 amplifier.


You need to have linux installed.  Any distro should work, but for a standalone “embedded” unit as pictured above you’ll want a minimalist install that doesn’t run an X server.  I use Ubuntu server edition because it’s what I know and it’s easy to install.

Make sure the ALSA sound drivers and utilities are installed:  sudo apt-get install alsa alsa-base alsa-utils alsa-tools libasound2-plugins should work on any Debian-based distro (e.g. Ubuntu).

The main tool we’ll use for running the DSP is ecasound, so make sure it’s installed (sudo apt-get install ecasound).

Download and install rt-plugins, a collection of IIR digital filters (implemented as LADSPA plugins) that I wrote for doing loudspeaker xover/eq.  You probably want additional plugins from the Computer Music Toolkit (sudo apt-get install cmt).  Steve Harris’s plugins collection also has some useful filters (sudo apt-get install swh-plugins).

Identifying Audio Inputs/Outputs

Do a cat /proc/asound/cards to see the sound cards identified by ALSA.  On one of my systems the output looks like this:

0 [Intel  ]: HDA-Intel - HDA Intel
             HDA Intel at 0xff9f4000 irq 46
1 [UA25EX ]: USB-Audio - UA-25EX EDIROL
             UA-25EX at usb-0000:00:1d.0-2, full speed
2 [Device ]: USB-Audio - USB Sound Device
             USB Sound Device at usb-0000:00:1d.2-1, full speed

Here card 2, named Device, is the 7.1 channel usb interface that I’ll use in the examples.  Take note of the device name of the card you want to use.  Run alsamixer and unmute the front, surround, and center/LFE channels on your sound card, and set their levels to 100% (0dB).

Run aplay -L to list the available output pcms.  Here’s the relevant part of the output on my system:

    USB Sound Device, USB Audio
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
    USB Sound Device, USB Audio
    7.1 Surround output to Front, Center, Side, Rear and Woofer speakers

You should have something similar.  Here surround51 is the pcm to use for 6-channel output.  The labels “Front, Center, etc” are intended for surround sound use; for our purposes it’s better to think of these as Channel 1, Channel 2, etc.  To test the output, plug some cheap powered multimedia speakers into one of the analog output jacks on the sound card and run

speaker-test -D surround51:Device -c 6 -t wav

(replacing Device with the device name for your sound card).  For 4-channel output use -c 4.  Depending on your hardware you may need to experiment with different pcm names and/or analog outputs to get this working.  The mapping of software to hardware channels can vary between devices; try all the output jacks so you know which ones have a signal on them.

Finally, to test output from ecasound, download chan_labels_6.wav and run

ecasound -tl -i chan_labels_6.wav -f:16,6,44100 -eadb:-24 -o alsa,surround51:Device

(replacing surround51:Device with the pcm and device names you determined above).  This will play a continuous loop of a 6-channel voice recording of “…left woofer … right woofer … left mid … right mid … left tweeter … right tweeter…” identifying each output channel by name.  Take note of which label plays on which analog output, since the same channel assignment is used in the 3-way crossover implementation below.

For a 2-way crossover you’ll need only 4 output channels, in which case download chan_labels_4.wav and run the following command instead.

ecasound -tl -i chan_labels_4.wav -f:16,4,44100 -eadb:-24 -o alsa,surround51:Device

This plays a loop of “…left woofer … right woofer … left tweeter … right tweeter…”  Again, take note of which hardware outputs the various labels appear on.

Crossover/EQ Specification

2-Way Crossover:

The block diagram for a general 2-way crossover (including equalization and/or other filters) looks like this:

The filters are grouped into three chains named “pre”, “tweeter” and “woofer”.  The following ecasound command line will implement this crossover:

ecasound -z:mixmode,sum -x \
         -a:pre -i:mysong.mp3 -pf:pre.ecp -o:loop,1 \
         -a:woofer,tweeter -i:loop,1 \
         -a:woofer -pf:woofer.ecp -chorder:1,2,0,0 \
         -a:tweeter -pf:tweeter.ecp -chorder:0,0,1,2 \
         -a:woofer,tweeter -f:16,4,44100 -o:alsa,surround51:Device

This takes input from the (stereo) audio file mysong.mp3, applies the crossover and other filters, and outputs the resulting 4-channel, 16-bit raw PCM directly to the sound card.  The woofer L/R outputs appear on channels 1 & 2; the tweeter L/R outputs are on channels 3 & 4 (the test you ran above will help determine which physical outputs these correspond to).

Various aspects of this command can be changed to handle specific use cases: e.g. you can change the input and output bit-depth or sample rate (provided your hardware is compatible) or you might want to process audio from a digital or analog hardware input instead of an audio file.  Changing the -o:alsa,... to -o:myoutput.wav will send the 4-channel output to a file instead, which can be useful for testing.

Update (14/09/2015): Q: What does the -x in the ecasound command do?  A: Nothing.  But I leave it there for when I want to redirect the audio output to a file for testing.  The -x tells ecasound to truncate the output file (if it already exists) before writing.  If you never use file output then you can safely leave this out.

The files pre.ecp, tweeter.ecp and woofer.ecp define the actual filter chains using ecasound syntax (see the man page).  Each chain can have any number of filters, of any type, limited only by the filters available as LADSPA plugins or built into ecasound.

For example, here are the filter chain definitions for a typical 2-way crossover, using IIR digital filters from rt-plugins and a pure delay from cmt:

# pre.ecp
pre = -el:RTparaeq,-8.5,82,2.6

# woofer.ecp
woofer = -el:RTlr4lowpass,1500 -el:RTlowshelf,4.0,150,0.71

# tweeter.ecp
tweeter = -el:RTlr4hipass,1500 -el:delay_0.01s,0.0019,1 -eadb:-4.5

Here the “pre” chain contains only one filter: a -8.5dB notch at 82Hz with Q=2.6 (e.g. to suppress a room mode).  The “woofer” chain consists of a Linkwitz-Riley 4th-order low-pass at 1500Hz, followed by a shelving filter with a gain of 4dB at low frequencies, centered at 150Hz, Q=0.71.  The “tweeter” chain consists of a Linkwitz-Riley high-pass at 1500Hz, a 1.9ms delay (for phase/time alignment of drivers) and 4.5dB of attenuation (to match driver sensitivities).

3-Way Crossover:

The topology of a general 3-way crossover looks like this:

Now we have five filter chains and a correspondingly more complex ecasound implementation:

 ecasound -z:mixmode,sum -x \
          -a:pre1 -i:mysong.mp3 -pf:pre1.ecp -o:loop,1 \
          -a:pre2,woofer -i:loop,1 \
          -a:mid,tweeter -i:loop,2 \
          -a:pre2 -pf:pre2.ecp -o loop,2 \
          -a:woofer -pf:woofer.ecp -chorder:1,2,0,0,0,0 \
          -a:mid -pf:mid.ecp -chorder:0,0,1,2,0,0 \
          -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,1,2 \
          -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Device

This takes input from mysong.mp3 and outputs 6-channel, 16-bit raw PCM to the sound card, with the woofer L/R output on channels 1 & 2, mid on 3 & 4, and tweeter on 5 & 6. The files pre1.ecp, pre2.ecp, woofer.ecp, mid.ecp and tweeter.ecp define the corresponding filter chains, just as in the 2-way crossover example above.

Digital Clipping

The signal path within ecasound is 32-bit floating point.  This provides practically unlimited headroom, so clipping between filter stages is not a concern.  Only at the final output stage do samples get clipped to the interval [-1.0,1.0].  The easiest way to adjust overall gain to prevent clipping at the output is to put a level adjustment in the “pre1” filter chain; e.g. putting -eadb:-6.0 in pre1.ecp will reduce the overall gain by 6dB, on all channels.


Depending on how the LADSPA plugins are configured on your system, the DSP commands above will run in either 32- or 64-bit floating point (my rt-plugins collection defaults to 64-bit, i.e. double precision).  The audio gets re-quantized at the final output stage; if your sound card is only 16-bit, the resulting quantization noise might be audible in some circumstances.  The standard remedy is to dither before re-quantizing.  Unfortunately ecasound doesn’t do this, but sox does, so one approach is to pipe the audio through sox.  In the examples above, simply replace the -f:16,6,44100 -o:alsa,surround51:Device with the following:

-f:f32_le,6,44100 -o:stdout | \
  sox -q -c 6 -r 44100 -b 32 -e float -L -t raw - \
  -e signed -c 6 -b 16 -t alsa surround51:Device dither -s

which causes ecasound to output 6-channel, raw PCM in 32-bit floating point.  This gets piped to sox, which does dithered re-quantization to 16-bit (with noise shaping) on all 6 channels and sends the output to the sound card.  (Most of this command is about specifying the format of the input and output streams; these need to be given explicitly when running raw PCM through a pipe.  Changing every 6 to a 4 will make this work for a 2-way crossover.)

Analog Input

To process an analog line-level input, simply change the -i:mysong.mp3 in the examples above to -i:alsa,hw:Device (or whatever ALSA device name corresponds to your hardware).  Use alsamixer to ensure the “line” input is unmuted and set as the default capture device.

You’ll need to take care in setting the input level gain in alsamixer.  Finding the optimal setting is a delicate matter; it depends on both the output level of your analog source and the sensitivity of your sound card’s line-level input.  Too much gain will result in digital clipping; too little will sacrifice signal-to-noise ratio and dynamic range.  You could just listen for evidence of clipping and adjust levels accordingly, but the best approach is probably to make a digital recording while you feed the sound card’s input with a full-scale test tone, like this:

ecasound -i:alsa,hw:Device -o:output.wav

Inspect output.wav for evidence of clipping, adjust the input gain and/or your analog output, and repeat until the digital recording is just shy of clipping.

S/PDIF Input

To process a digital stereo S/PDIF input, simply change the -i:mysong.mp3 in the examples above to -i:alsa,iec958:Device (or whatever ALSA pcm corresponds to the S/PDIF input on your hardware).  Use alsamixer to ensure the S/PDIF input is enabled.

warning: your S/PDIF hardware probably expects a 16-bit/44.1kHz input, the ecasound default (if not, you need to precede the first -i: with a suitable -f: switch to specify the format of the digital input stream).  In any case, you must ensure the output sample rate of your digital source will always match the specified input sample rate.  Mismatching sample rates at the S/PDIF interface could result in outputting digital noise at high output volume — which could be disastrous to your loudspeaker drivers and/or ears.  The Squeezebox Touch, for example, will output 96kHz if the source material has this sample rate: don’t do this unless your S/PDIF input supports it and you’ve configured ecasound appropriately; instead, you should configure squeezeboxserver to force re-sampling everything to 44.1kHz, or just use the analog outs.  I’m working on a more elegant solution to this problem.

HDMI Output

If your PC has hdmi out, this offers an attractive way to get the crossover outputs out of the PC in digital form: hdmi supports up to 8 channels of raw uncompressed PCM at 24-bit resolution.  A separate device is then needed to convert all channels to analog: an hdmi audio de-embedder is one option; or use an hdmi receiver, which adds the convenience of down-stream volume control.

If your PC’s hdmi supports PCM audio, the output of aplay -L will look something like this:

   Direct hardware device without any conversions
   Direct hardware device without any conversions
   Direct hardware device without any conversions
   Direct hardware device without any conversions

Use alsamixer to unmute all output channels on the card.  You’ll need to experiment to determine which of the devices above will accept a multichannel PCM stream.  See this page for detailed instructions, especially Section 8 (that document pertains to NVidia devices, but much of it applies generally).  In my own case, on a Zotac zbox, the NVidia card has index 1 (look at /proc/asound/cards to check) so I ran each of the following:

speaker-test -c 6 -r 48000 -D hw:1,3 -t wav
speaker-test -c 6 -r 48000 -D hw:1,7 -t wav
speaker-test -c 6 -r 48000 -D hw:1,8 -t wav
speaker-test -c 6 -r 48000 -D hw:1,9 -t wav

Only hw:1,3 worked, so this is the device to use.  In ecasound, using -f:s24_le,6,44100 -o:alsahw,1,3 gives 24-bit, 6-channel output over hdmi.  (This is using the open-source nouveau driver; with the proprietary NVidia driver, audio over hdmi only works if an X server is running.)

update (20/6/2016): I’ve found a major problem with hdmi output to an Atlona HD570 “audio de-embedder”.  When driven with signals above -15dBFS this combo can generate subsonic distortion that pushes the LX521 drivers past their excursion limits, with ensuing unpleasant noise and/or damage to the drivers.  I don’t know the cause yet, or even whether the problem is in the pc or the Atlona device.  Please use caution, and see this post if you’re considering this approach.

MPD Integration

MPD is a viable and highly configurable replacement for the squeezebox and other network media players.  The xover/eq described here can be built into mpd quite easily.  For example, to have mpd send its output through the 2-way crossover example above, you could put the following definition in your mpd.conf:

audio_output {
        type    "pipe"
        name    "DSP crossover/eq"
        format  "44100:32:2"
        mixer_type  "software"
        command "ecasound -q -z:nodb -z:mixmode,sum
         -a:pre -f:s32_le,2,44100 -i:stdin -pf:/etc/pre.ecp -o:loop,1
         -a:woofer,tweeter -i:loop,1
         -a:woofer -pf:/etc/woofer.ecp -chorder:1,2,0,0
         -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2
         -a:woofer,tweeter -f:16,4,44100 -o:alsa,surround51:Device

This creates an mpd output that pipes 32-bit stereo PCM to ecasound, which then applies the crossover and sends all 4 output channels to the soundcard.  For this to work you’ll need to build mpd from source with --enable-pipe-output.  The reason we pass data in 32-bit is to avoid adding the quantization noise that would result if mpd truncated its output to 16 bits after applying its volume control.

“Bit Perfect” Playback (Update 9/9/2015):

Some have expressed concern that the mpd config above re-samples everything to 44.1kHz, preventing “bit perfect” playback of hi-res audio files.  Unfortunately mpd’s pipe plugin outputs raw (headerless) pcm data: currently there is no provision for passing the stream format variables (sampling frequency, bit depth) to ecasound so that it knows how to interpret the raw data.  The easy solution is to hammer the data into one fixed format before passing it to ecasound.  My current soundcard only does 44.1kHz so that’s a good choice for me; if you prefer to re-sample to 96kHz (and have a compatible soundcard) you just need to change every occurence of 44100 to 96000 in the mpd config above.

Alternatively, it should be possible to avoid re-sampling by having mpd output directly to a virtual alsa device (instead of piping to ecasound) defined via a custom asound.conf that sets up the xover/eq ladspa filters.  Others have worked on this approach (see e.g. this thread) but I haven’t tried it myself.  Personally I view hi-res formats and the pursuit of “bit perfect” playback as distractions from the real issues in audio quality (everyone should read Monty’s excellent 24/192 article).  Provided you have enough cpu power to use the best quality sample rate conversion in real time, the resampling will be acoustically transparent.


  • In the case of hardware (analog or digital) input you probably want the DSP to start running immediately on boot.  You just need to put the appropriate ecasound command line in your /etc/rc.local or similar.
  • To prevent unpleasant pops and thumps when powering the PC on or off, first mute all input and output channels and store the ALSA mixer settings where they will be loaded on boot (doing alsactl store should work).  Then put appropriate amixer commands in /etc/rc.local so the outputs you want get unmuted only after the system has booted.
  • Running the DSP in software introduces a small latency (on the order of 100ms) in the audio stream, which might be noticeable when starting, stopping or switching audio tracks.  This can be minimized by reducing the buffer size that ecasound uses.  I find that adding -z:nodb -b:256 to the ecasound command makes the latency imperceptible on my system.  Clicks and pops in the output are probably a sign of buffer under/overruns, which can usually be fixed by increasing the buffer size, e.g. with -b:1024.
  • To adjust the left/right balance for a particular crossover output (e.g. to correct for variability in driver sensitivity, like the Pluto ASP does) you can use the ecasound syntax -eadb:gain,channel.  E.g. to boost the signal to the right-hand tweeter by 0.8dB you would put -eadb:0.8,2 in tweeter.ecp .


All of my audio sources (a Logitech Squeezebox and an mpd server) are actually small linux PCs.  So I figured why not use that cpu for digital signal processing as well?  The squeezebox lacks both the cpu power and multi-channel output, but integrating a crossover into mpd turns out to be easy.

In terms of audio quality, software DSP has no benefits over more specialized hardware (e.g. the miniDSP or Behringer DCX2496).  But computers do enjoy enormous economies of scale (witness the $25 Raspberry Pi) so a software approach can be less costly.  Computers will keep getting smaller and cheaper, whereas hardware DSP units will likely never benefit from such economies of scale.

But I specifically wanted a software solution because of its benefits for the DIY audio enthusiast (i.e. me):

  • One can integrate it with a media server (e.g. mpd) within a single device, which could be used for other things as well.
  • It makes available a larger palette of filters and algorithms.  I can write my own to provide anything that’s missing, and I can be certain of what the filters are actually doing.
  • It’s much easier to program: with a laptop I can do this from the listening position.
  • Through an ssh connection to the PC running the DSP one can, from the listening position, switch between alternative xover/eq configurations on the fly.  This makes level-matched listening tests/comparisons much easier and more reliable to set up and carry out.
  • Because the software and configuration are open-source, others can take what I’ve done and make it better.


135 thoughts on “Digital Crossover/EQ with Open-Source Software: HOWTO

  1. Thanks for sharing, Richard!

    I’ve been trying to replicate your configuration with Ubuntu Server 13.04 (AMD64) and an ALC662-based sound card, but I only get sound out of four of the six sound card outputs. I’ve searched several forums to no avail.

    Would you mind letting us know which version of Ubuntu you are running, and if you are using anything other than the standard kernel and version of Alsa?


    • I’m using Ubuntu Server 12.04 (i386), with the stock kernel and ALSA. When testing 6 channels out, make sure you put “-f:16,6,44100” on ecasound’s output. Also, on some sound cards I’ve only been able to get 6 channels out if I send the output to the Surround71 PCM — presumably some quirk in how ALSA maps the output channels. Hope this helps!

      • Hi,

        I have put together a Linux box running AVLinux with a Delta 1010LT. I have managed to get through the speaker tests and have identified the outputs. What I want to do is a three-way active crossover with the bass going to a mono subwoofer, the mids going to a stereo amp and the tweeters going to another stereo amp. How do you suggest I resolve the stereo signal for the bass signal to mono?

        • To derive a mono channel for the sub one would usually mix down by summing the L/R channels. Ecasound will sum all the output chains, so you could start with the 3-way topology (see block diagram above) but split the “woofer” chain into “subL” and “subR” chains that both output on ch 5:

          ecasound -z:mixmode,sum
          -a:pre1 [...] -pf:/etc/pre.ecp -o:loop,1
          -a:pre2,subL,subR -i:loop,1
          -a:mid,tweeter -i:loop,2
          -a:pre2 -pf:pre2.ecp -o loop,2
          -a:subL -pf:sub.ecp -ea:50 -chorder:0,0,0,0,1
          -a:subR -pf:sub.ecp -ea:50 -chorder:0,0,0,0,2
          -a:mid -pf:mid.ecp -chorder:1,2,0,0,0 \
          -a:tweeter -pf:tweeter.ecp -chorder:0,0,1,2,0 \
          -a:subL,subR,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Device

          This is off the top of my head; I haven’t tested it. It puts the mid outputs on ch 1/2, tweeters on ch 3/4 and sub on ch 5. The sub channels are attenuated by 50% (-ea:50) before summing, since summing doubles the amplitude if L/R inputs are in phase.

          • Wow. Thanks! I will give that a try. Still trying to understand how all this works.

            I will study more before I burden you with questions. Thanks for the reply.

          • Hi, I have worked hard at understanding but am stumped. I have successfully isolated the soundcard outputs and can run speaker-test -Dsurround51:M1010LT -c6 -twav & hear the front left, front right etc. in each of my six speakers,

            I have installed ecasound, rt-plugins and the other ones mentioned above. When I run the code included above (after editing for my soundcard name) and or any other code on this page in terminal I get all kinds of errors that either state unrecognized format or unchained setup and similar.

            Maybe I am not supposed to run the ecasound commands solely in terminal. The article above is admittedly a bit above my head.

            I am going through the ecasound manual but am flummoxed.

            So I do not wear out my welcome here do you have a recommended tutorial so I can better educate myself?

            It is exciting that I can get six channels of audio out of my card and if I can get this next part figured out I should have pretty cool setup.

          • Success! Sort of. I used the three-way crossover in the main page and changed the parameters to reflect my system. Namely, I needed to change -o:alsa to -o:jack_alsa and then it worked. Now, I just need to adjust the crossover points in the woofer.ecp, mid.ecp and tweeter.ecp files.
            I want the woofer at 150, the mids as a bandpass 150-3500 and tweeter at 3500 and above.

            I realized that since I have a dual-coil subwoofer I do not need to sum it down to mono.

            For the life of me, I have looked for the .ecp files using find / -name *.ecp and cannot find them. I will keep looking.

          • Glad to hear you’re getting somewhere with this! Alsa output fails on your system because your distro runs JACK, which blocks access to the sound system via alsa.

            You need to write the *.ecp files yourself following the templates above. I tend to keep them in /etc but they can live anywhere.

            As for learning ecasound I just read every tutorial available, especially anything on multi-channel processing and loop devices. These were helpful:

            If you get stuck, feel free to ask here and I’ll do my best though I can’t guarantee a quick response.

      • Thanks again. The little graph you made clicked for me. I was able to connect what you were saying by seeing what you put in the topology layout. I think I have it right now.

        I finally understood what you did to get a mono sub out. My 15″ open-baffle sub is much more musical than the sealed 12″ sub. Not as much punch but better articulation. I listen to a lot of acoustic music where it really shines. So, here is what I did after adding the pre.ecp you posted for me. My subR/subL .ecp files below are basically the woofer.ecp copied for each and renamed:

        ecasound -z:mixmode,sum -x \
        -a:pre,subL,subR -i:jack_alsa,hw:M1010LT \
        -a:pre -o:loop,1 \
        -a:mid,tweeter -i:loop,1 \
        -a:pre -pf:/etc/pre.ecp \
        -a:subL -pf:/etc/subL.ecp -ea:50 -chorder:1,0,0,0,0,0 \
        -a:subR -pf:/etc/subR.ecp -ea:50 -chorder:2,0,0,0,0,0 \
        -a:mid -pf:/etc/mid.ecp -chorder:0,0,1,2,0,0 \
        -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,0,0,1,2 \
        -a:subL,subR,mid,tweeter -f:16,6,44100

        I hope I typed it in right because it sounds really good to me now. The phase fix helped a great deal once I heard the difference. I love the open-baffle sound much more than boxed speakers. I am very pleased. This whole effort has been good for my kids too – the old man can still learn things see?

        Thanks for your generosity and the gift of your time.


    • Thanks for being so helpful: It is no wonder that you were endorsed by Siegried Linkwitz.

      I have created my .ecp files as .txt files and wonder if I am getting the syntax right? As to the pre.ecp I do not need a notch at this time so it is essentially blank. I am not able to figure out the syntax for the mid bandpass so it is a best guess. If you have time can you comment? I will put all these in /etc and point to them.

      # pre.ecp
      pre =

      *(should this/could this be empty?) (if empty is it needed?)

      # tweeter.ecp
      tweeter = -el:RTlr4hipass,3500 -el:delay_0.01s,0.0019,1
      *(no attentuation since I am tri-amping the setup)

      # mid.ecp
      mid = -el:ladspa-bandpass-a-iir,150,3500


      # woofer.ecp
      woofer = -el:RTlr4lowpass,150 -el:RTlowshelf,4.0,70,0.71

      *(I may end up using an open-baffle sub and will need to add some boost).

      Thanks again, if this is too much please tell me to pound sand and leave you alone.


      • Syntax looks ok at a glance. Since pre.ecp isn’t needed you can delete it and the reference to it in your ecasound command.

        You don’t want to use that bandpass on the mids: it’s probably only 2nd-order so its slopes won’t match those on the woofers & tweeters. Instead, build a 4th-order bandpass like this: -el:RTlr4hipass,150 -el:RTlr4lowpass,3500

        Even better, move the 150Hz highpass into the pre2 section… otherwise the mid/tweeter won’t be in phase: the mid will get a phase shift from the 150Hz highpass but the tweeter won’t. SL writes about the issue here:

        • Well, it works at testing. I used the setup after making the edits for the bandpass per your suggestion above. I am not sure how to move the 150Hz highpass into the pre2 section but will figure that out. I am sure there is a phase problem at the moment per your thoughts above.

          It is tri-amped now with a stereo amp on each of the subwoofer (a dual coil), the mid speakers and the tweeters. Much adjustment is still needed but it works!

          The biggest change is I know have 300 watts running to the sub whereas before I only had 60.

          Got it running off of my IPad with apple lossless files to a MCM Line level converter that converts the -10dB to +4dB out to the inputs on the M-Audio 1010LT which has professional +4 inputs.

          Got some work to do. If you were near me I would buy you a beer! Probably two!



        • Well, it sounds pretty good to me. I had to make some adjustments to the level of the line it – was a tad bit hot. Here is the ecasound command I am using. I am quite sure it is inelegant but it sounds good so far. Much better than the passive crossovers I was using before.

          ecasound -z:mixmode,sum -x \
          -a:pre1 -i:jack_alsa,hw:M1010LT -o:loop,1 \
          -a:pre2,woofer -i:loop,1 \
          -a:mid,tweeter -i:loop,2 \
          -a:pre2 -o loop,2 \
          -a:woofer -pf:woofer.ecp -chorder:1,2,0,0,0,0 \
          -a:mid -pf:mid.ecp -chorder:0,0,1,2,0,0 \
          -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,1,2 \
          -a:woofer,mid,tweeter -f:16,6,44100 -o:jack_alsa,surround51:M1010LT

          The formatting of comment here does not look like it looks in the terminal.

          Thanks again. James

          • Correction: I just put the mid in pre2 and removed it from below.

            ecasound -z:mixmode,sum -x \
            -a:pre1 -i:jack_alsa,hw:M1010LT -o:loop,1 \
            -a:pre2,woofer, mid -i:loop,1 \
            -a:tweeter -i:loop,2 \
            -a:pre2 -o loop,2 \
            -a:woofer -pf:/etc/woofer.ecp -chorder:1,2,0,0,0,0 \
            -a:mid -pf:/etc/mid.ecp -chorder:0,0,1,2,0,0 \
            -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,0,0,1,2 \
            -a:woofer,mid,tweeter -f:16,6,44100 -o:jack_alsa,surround51:M1010LT

          • That sort of works, but gives a non-cascaded topology with sub-optimal phase alignment. Here’s a better version that also removes the (unused) pre1 chain:

            ecasound -z:mixmode,sum -x \
            -a:pre,woofer -i:jack_alsa,hw:M1010LT \
            -a:pre -o:loop,1 \
            -a:mid,tweeter -i:loop,1 \
            -a:pre -pf:/etc/pre.ecp \
            -a:woofer -pf:/etc/woofer.ecp -chorder:1,2,0,0,0,0 \
            -a:mid -pf:/etc/mid.ecp -chorder:0,0,1,2,0,0 \
            -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,0,0,1,2 \
            -a:woofer,mid,tweeter -f:16,6,44100 -o:jack_alsa,surround51:M1010LT

            For a minimal working crossover you’ll need the following files:

            # /etc/woofer.ecp:
            # /etc/pre.ecp:
            # /etc/mid.ecp:
            # /etc/tweeter.ecp:

            This builds a cascaded topology like so:
            where the pre chain contains a high-pass to match the woofer’s low-pass; this affects both mid & tweeter, ensuring they remain in-phase.

            Maybe one day we’ll share a beer. Meanwhile, enjoy!

  2. Hello,
    i have tried this on a Raspberry-Pi, but I got stuck by installing the rt-plugins. (using the the latest Raspbian Wheezy Image 2013-07-26)

    Trying 0.0.2 Version gives me after “make”:

    gcc -I. -Ofast -Wall -fomit-frame-pointer -march=native -ffast-math -fstrength-reduce -funroll-loops -c -fPIC -DPIC -o RTallpass1.o RTallpass1.c
    cc1: error: bad value (native) for -march switch
    make: *** [RTallpass1.o] Error 1

    The 0.0.1 Version:
    gcc -I. -Ofast -Wall -fomit-frame-pointer -msse2 -mfpmath=sse -ffast-math -fstrength-reduce -funroll-loops -c -fPIC -DPIC -o RTallpass1.o RTallpass1.c
    cc1: error: unrecognized command line option ‘-msse2’
    cc1: error: unrecognized command line option ‘-mfpmath=sse’
    make: *** [RTallpass1.o] Error 1

    I think it has something to do with missing sse on Arm CPUs.


    • That’s right, arm doesn’t have SSE instructions. I fixed this in version 0.0.2, which will build on the Pi if you also edit the Makefile and remove the “-march=native” from CFLAGS — that option doesn’t work on the Pi, I have no idea why. I’ll fix this in the next release.

  3. Thank you for sharing your project with us.
    Do you know how much latency you have in your setup? For normal audio playback this does not matter, but if i connect e.g. my guitar to the analog input this matters very much.
    Did you test a lot on RaspberryPi? How much stereo ways can you achieve on this tiny cpu?

    • I’m not sure about latency. Ecasound runs in real-time mode, so perhaps tens of milliseconds, but that’s just a guess.
      I don’t have my system fully functional on the Pi: the Pi has a usb bottleneck that prevents using a digital/analog input at the same time as multi-channel output, at least on the usb soundcards I’ve tried. The latest firmware has improved things, to the point where I can play audio from files on disk, but not from a digital or analog input.

        • AFAIK the Pi2 suffers the same problems with usb bottlenecks. But omxplayer should now be able to do multi-channel out via hdmi; I think you could just pipe the audio from ecasound via stdout. This would leave the usb bandwidth free to handle analog input. I’ve been meaning to try this but haven’t found time.

  4. I’m clearly a noob when it comes to setting up MPD. I’ve eagerly followed your instructions, and have output all the way up to running MPD. I think I’m not quite understanding some of the MPD configuration, and am getting errors on startup:

    port “6600”
    music_directory “/music”
    playlist_directory “/root/.mpd”
    log_level “verbose”
    log_file “~/.mpd/mpd.log”
    db_file “/root/.mpd/mpd.db”
    bind_to_address “”
    audio_output {
    type “pipe”
    name “DSP crossover/eq”
    format “44100:32:2”
    mixer_type “software”
    command “ecasound -q -z:nodb -zmixmode,sum
    -a:pre -f:s32_le,2,44100 -i:stdin -pf/etc/pre.exp -o:loop,1
    -a:woofer,tweeter -i:loop,1
    -a:woofer -p /e:/etc/woofer.ecp -chorder:1,2,0,0
    -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2
    -a:woofer,tweeter -f:16,4,44100 -o:alsa,surround52:CMI8768 ”

    error on startup:

    line 13: Missing closing ”

    I’ve tried “\” on the end of the lines, stringing them into one big line, and while there is no error when I string ’em, the MPD log shows:
    Sep 07 10:14 : client: [0] process command “outputs”
    Sep 07 10:14 : client: [0] command returned 0
    every few seconds….
    The ecasound command does work separately.

    any advice you can add, I’d truly appreciate.



      • Thank you! The other problem I had was not having all the pieces installed when compiling MPD. I can now see all my music, and as “me”, it attempts to play. Ecasound works as root, but not as me. Permission problem, I think. I’ll get it.

        Thanks, again for pioneering!

  5. Hello, again….

    Made major progress. I’m now able to get sound from an MPD client, but . . .

    The crossover doesn’t seem to be working. I get full range from the “tweeter” output, and nothing from any of the other ports. When tested with the command-line ecasound, all works fine, and I’ve identified which should be active.

    Question: The ecp config files. Should they be full filename/path:

    # pre.ecp
    pre = -el:/usr/local/lib/ladspa/,3,26,.7

    or more like what you show:

    # pre.ecp
    pre = -el:RTpara,-8.5,82,2.6



  6. Still working on it…

    Running the ecasound command without the server:
    # test for ecasound and stuff
    ecasound -z:mixmode,sum -x \
    -a pre -i:/music/Veni_veni.wav -pf:/etc/pre.ecp -o:loop,1 \
    -a:woofer,tweeter -i:loop,1 \
    -a:woofer -pf:/etc/woofer.ecp -chorder:1,2,0,0 \
    -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2 \
    -a:woofer,tweeter -f:16,4,44100 -o:alsa,default:CMI9768 2>&1 test.txt

    The text file created includes:
    (eca-chainsetup-parser) Enabling ‘sum’ mixmode.
    (eca-chainsetup-parser) Truncating outputs (overwrite-mode).
    (eca-object-factory) ERROR: Unable to find LADSPA plugin “RTparaeqi”
    (eca-object-factory) ERROR: Unable to find LADSPA plugin “RTlr4lowpass”
    (eca-object-factory) ERROR: Unable to find LADSPA plugin “RThighpass”

    Checking with LADSPA, it does indeed have these plugins available.

    the .ecp files refer to the plugins by copy/paste from running

    echo “ladspa-register”| ecasound -c
    160. RT 2nd-order highpass
    -el:RThighpass,’Frequency (Hz)’,’Q’

    # tweeter.ecp
    tweeter = -el:RThighpass,100,.7



  7. Still at it….
    Ecasound isn’t crashing with core anymore, but. ..

    The command line with -x:mixmode,sum isn’t producing any output at all.

    It also crashes with core when the input file ends. It’s definitely not your plugins, but I’m just not able to get the command line right.


  8. Finally figured it out. when you do the -o with the -f:16:4:44100 and send it to “surround51”, my sound card says, “I’m a 6 channel sound card, 4 channels is ‘out of range'”. I find that if I send 6 channels, then it’s happy, or if I send to “surround4.0” it is happy.

    Hopefully, this will help somebody else. The results sound better than my home-built analog crossover, done from the 1970s Linkwitz article.


      • Replaced the AMD pc with my old Toshiba Pentium III laptop. Yes, I really mean a Pentium 3. It’s running fine, and it’s a lot quieter than the fan-filled PC. 30% cpu use on average, and it’s not logging any underruns. I’m very very happy with the results!

  9. I’ve managed to (politely) purloin a number of small PCs (eepc, revo and a Tranquil media pc) from our junk box at work. I’m busily installing Debian and hugely excited to get this all underway. Your solution is something that I desperately hunted for four or five years ago before I started attempting to make my own ASP based on Linkwitz designs. The problem is that they’re still all in bits in boxes and will likely never need to get finished now! Thanks in advance, Dan.

  10. You mentioned you’ve implemented the xo using a Raspberry Pi.

    I’d like to use my existing avr via hdmi before investing in more sophisticated xo and amp. Did you get multichannel hdmi output working from the pi? What image did you start from?


    • I did get a xo running on a Pi. The cpu can handle the processing in real-time; unfortunately the usb bottleneck on the Pi prevents getting 4- or 6-channel out to the soundcard without stuttering.

      I see that recent versions of omxplayer have enabled multichannel audio over hdmi, so you should be able to pipe 4- or 6-channel pcm through that, but I haven’t pursued it myself.

  11. Hi, excellent tutorial!!
    I have a question: If I feed ecasound trough mpd with 16bits audio instead 32 bits, will quality suffer?

    • No, 16 bits should be plenty. If (as I do) you use mpd’s software volume control, it will introduce some noise/distortion due to re-quantization. It’s measurable, but with 16 bit data the distortion products are about -96dBFS hence inaudible under most listening conditions. As insurance against this I try to keep the data path at 24 bits or more, but in practice I doubt that it matters.

        • Yes, my plugins could be used directly within alsa without having to go through ecasound. In your asoundrc you would create a virtual soundcard device, whose job it is to route the audio through the various ladspa plugins and then to the appropriate hardware channels. This would be pretty neat: any software that wants to output audio could just interface with this virtual device.

          It’s been on my to-do list for a while. I just haven’t felt like learning the asoundrc syntax, which looks to be fragile and hard to debug. Let me know if you get something working!

  12. Very interesting! I’ve been working on digital crossovers for DIY speakers for several years. My current system, built about 2 years ago, is a Linux box with four 3 GHz cpu cores, an RME digital sound card, and RME A/D-D/A box (RME ADI-8 DS) with 8 channels in/out, a couple of ATI 4 channel amps (AT1800) and a couple of Phoenix-like panels and W-frame woofers.

    I wrote all the software myself in C++ except for some utilities (jackd, fftw3). My digital filters are 8192 tap FIR filters; there are 8 filters (3 for each speaker system and 2 for ambient sound channels, at present unused). The system sampling rate is 88.2 kHz. I’m using the convolution theorem (hence the need for fftw3) to reduce the time complexity of running the filters to O n*log(n), so the computer is actually just loafing along (using about 25% of one core) at this sample rate.

    To generate the filters, I measured the speakers out-of-doors in a large parking lot, and fitted curves to the data, then inverted the driver impulse responses within their operating ranges to get the filter coefficients. It is actually more complicated than that, but that’s a subject for another comment.

  13. I think the Beaglebone Black is a better choice than the Raspberry Pi for an embedded audio DSP system. Its main advantages over the Raspberry Pi are:

    – is faster, 1 GHz clock
    – has hardware floating point (NEON co-processor, 2 gigaflops with MACs)
    – runs Ubuntu Linux (my preference over plain Debian)
    – has a sophisticated digital audio processor (McASP) and DMA engine
    – has more digital I/O pins (96 pins on header, most available to apps)

    The main disadvantages:

    – cost $45 which is a little more than the Pi
    – software and support is not as mature as the Pi

    I’m currently building a couple of digital crossovers for my Pluto speakers based on the Beaglebone. I have a Beaglebone controlling a cs4271 codec on a breadboard via alsa and jackd, and I have my software mostly written. My intent is to run the system at 176.4 kHz, but that may turn out to be more than the Beaglebone can handle. Currently my 44.1-to-176.4 kHz upsampling code and two channels of digital filter code are using up about 80% of the available cycles, but I’m not done optimizing. I really need to leave a fair amount of headroom on the CPU, maybe 40%, to be sure of not ever missing a sample. Unfortunately the available Linux kernels for Beaglebone do not yet include a real-time version. I’m also in the process of learning how to make PCBs using the gEDA free software package.

    • I agree. I picked up a couple of bbone black units a few months ago, but got distracted by other projects so they’ve just been sitting around.

      The cs4271 is a stereo codec… did you use 2 of them for your stereo 2-way xover?

      And how did you connect the cs4271 to the bbone? Not knowing how, I’ve been forced to use usb-audio which has its limitations.

      • My idea is to have a crossover per physical speaker system, e.g., for the Pluto, there’d be two Beaglebone/codec/power amp boxes as the two bases. This keeps the wiring short between the codec and the power amp and cuts down on the Beaglebone compute workload.

        For a detailed writeup of how I connected the cs4271 to the Beaglebone, check my posting of 2/7/14 on the Element 14 community website (–building-a-dac?et=blogs.comment.created#comment-30888). There’s also a photo of my test system on the same site dated 2/3/14

        In summary, I use I2C for configuring the cs4271 registers and the McASP unit on the Beaglebone for transferring the audio samples. The signals are:
        codec BBB mcasp0
        lrclk (4) -> fsx (P9.29) and fsr (P9.27)
        sclk (5) -> aclkx (P9.31) and aclkr (P9.42)
        sdout (6) -> axr2 (P9.28)
        sdin (7) <- axr3 (P9.25)

        codec BBB I2C2
        scl (11) scl (P9.19) (10k ohm pullup may be needed)
        sda (12) sca (P9.20) (10k ohm pullup may be needed)

        codec BBB GPIO
        rst (14) <- gpio0-5 (P9.17)

        ad0 (13) <- ground (to set the I2C address)

  14. Great job.

    I made it work straight away (took a couple of hours) on my Cubitruck & Debian Wheezy and an RME UCX. No major issues.
    The two channel crossover is just generating 6% load. Not too bad.

    How about 1st order and higher order (32db/48db) filters??
    I used to use a Behringer DCX2496. It allowed to go for 32/48db filters.
    Is it possible to stack 4h order filters ??
    A first order filter would also be nice to have. My tweeters are crocced over with
    a single cap today. Would be nice to have that filter option in the rt-plugins.

    2. How do you handle different samplerates with mpd??


    • 1. First-order filters are on my todo list. I haven’t needed them and probably won’t, but I’ll put them in next time I look at the code. As for higher order filters, I probably won’t bother: you can build them up by chaining together first- and second-order filters. E.g. an LR4 filter is two identical 2nd-order filters (Q=1/sqrt(2)) chained toghether. Putting two LR4 filters back-to-back will give you a simple 48dB/oct filter.

      2. I’ve configured my mpd.conf to output everything as 24-bit/44.1kHz (resampled as necessary). You could do 24/96 if you care and your cpu can keep up. Use the “samplerate_converter” tag to choose among several resampling algorithms to balance quality against cpu power. The best is “Best Sinc Interpolator” but it’s cpu-intensive so might cause stuttering on weak hardware that can’t keep up. For me “Medium Sinc Interpolator” sounds indistinguishable, and it runs fine in real time on all the hardware I’ve tried it on.

  15. I am running ubuntu 14.04 on asus motherboard with amd opteron 4-core processor nvida graphics card. No physical space limitations. Since your original post would you have more up to date suggestions for sound cards. I am running a 5.1 system but eventually the main speakers will be bi-amped (Audio Artistry CBT36K Line Array Speaker). Current running Carver original amazing speakers as mains. I am looking to eventually replace my surround sound preamp (emotiva umc-200) with computer solution mostly due to bi-amp requirement for CBT36K speakers (I don’t wish to add an extra stage of ADC-DAC to system to implement required digital crossover).

    PS: What math are you interested in. I develop symbolic computer algebra software (sympy/galgebra) for geometric (Clifford) algebra.

    • I haven’t tried any new sound cards lately and probably won’t. In my own setup I don’t use a sound card per se: I output 6ch digital audio via hdmi (this needs the closed-source nvidia driver, at least the last time I checked; the open-source “nouveau” driver couldn’t take multi-channel pcm). The hdmi goes to an outboard 6ch dac, then the ATI amp.

      My math has been mainly dynamical systems, but applied math generally. Lately I’ve gotten into design algorithms for DSP, using best approximation in the L1 norm. I’ve been using maxima for symbolic work.

      • Are you running ubuntu 32 or 64 bits (some sound cards seem to care). What are you using for your dac. Since I have a surround sound preamp (umc-200) with digital input (hdmi from computer) should I use that for my dac (forget about using it for dts/dolby decoding and equalization) instead of a sound card. If so how would I do dolby decoding (tivo optical output) of streaming signal from tivo on the computer (what software should I use).

        With regard to math. Are the ecasound filters implemented as recursive time domain filters?

        • I’m running ubuntu 32-bit. I think.

          My dac is this one:

          It takes hdmi in and outputs up to 8-ch analog.

          Indeed, you could just as well use a surround sound preamp, provided it knows how to receive 6-ch pcm audio over hdmi. If you want dolby decoding that will have to be done in software prior to the xover/eq, though I don’t know how. In this application you definitely don’t want your preamp to apply any processing!

          • Thank for the information on the atlona dac. I will initially try to use a asus xonar d2 sound card (also has spdif input and ouput) which appears to have good alsa support. With regard to dolby/dts encoding it appears that both VLC and XBMC will decode the audio streams. What I need to figure out is how to decode the AC3 stream from my tivo’s spdif output on my computer.

            Separately have you used or have any opinion on the Faust DSP programming language.

          • I’m aware of Faust but haven’t used it myself. For me it would be overkill: the DSP required for loudspeakers is pretty simple and all doable with bi-quads or bi-linear recursive filters.

        • Sorry, I missed your question about the filter implementation.

          Yes, the filters I wrote are implemented in the time domain. They’re all 2nd-order recursive filters, i.e. “biquads”, derived from the transfer functions of their analog counterparts via a bilinear transform. I couldn’t find an existing implementation of everything I needed so I ended up rolling my own. But there’s nothing special about them.

      • I have one 🙂
        I intend to use it just for implementing this DSP crossover, using a BeagleBone Black.

        The U7 seems to be kinda supported…
        Follow this:
        to get alsamixer working and set output volume at 0dB (it comes up initially at -20dB)

        doing a loop back from out to in seems to work just fine:
        ecasound -i null -el:sine_fcac,100,0.5 -f:24,2,192000 -o alsa,front:U7

        $ arecord -V stereo -D hw:1,1 -f S24_3LE -r 192000 -c 2 /dev/zero
        Recording WAVE '/dev/zero' : Signed 24 bit Little Endian in 3bytes, Rate 192000 Hz, Stereo
        +################## 55%|55%###################+

        This is on Lubuntu 14.04, but Debian on BeagleBone works aswell.

  16. Hi,

    first I would like to thank you for your work you put into this tutorial.

    I have a question about routing the audio to the ecasound.

    Would it be possible to use a video player on a linux client (e.g. XMBC oder VLC) and direct the sound output of those to the ecasound for the dsp? Ecasound would be running on that client as well. Then use a soundcard or two soundcards to output the sound to the amplifiers?

    Hope it is understandable what I would like to achiev 😉


    • Probably the easiest way is via a named pipe (fifo). Create a named pipe:
      mkfifo mypipe.raw
      and tell ecasound to read its input from this pipe:
      ecasound -f:s16le,2,44100 -i:mypipe.raw ...
      Then you just need to configure vlc/xbmc to send raw audio to mypipe.raw. You may need to tweak the ecasound command and/or your video player so they agree on the same sample format and rate.

  17. Thank you so much for this tutorial. I am working on building a linux-based crossover for my 3-way JBL horns. I’ve worked pretty far though the tutorial, but I am getting the following errors/notes when running the ecasound command:

    (eca-chainsetup) WARNING: Couldn’t lock all memory!
    (eca-chainsetup) Opened input “alsa”, mode “read”. Format: s16_le, channels 2, srate 44100, interleaved.
    (eca-chainsetup) NOTE: using existing audio parameters -f:f32_le,2,44100 for object ‘loop’ (tried to open with -f:s16_le,2,44100).
    (eca-chainsetup) Opened input “loop”, mode “read”. Format: f32_le, channels 2, srate 44100, interleaved (locked params).
    (eca-chainsetup) NOTE: using existing audio parameters -f:f32_le,2,44100 for object ‘loop’ (tried to open with -f:s16_le,2,44100).
    (eca-chainsetup) Opened output “loop”, mode “write”. Format: f32_le, channels 2, srate 44100, interleaved (locked params).
    (eca-chainsetup) Opened input “loop”, mode “read”. Format: f32_le, channels 2, srate 44100, interleaved (locked params).
    (eca-chainsetup) Opened output “loop”, mode “write”. Format: f32_le, channels 2, srate 44100, interleaved (locked params).
    (eca-chainsetup) Opened output “alsa”, mode “write”. Format: s16_le, channels 6, srate 44100, interleaved.

    This is my ecasound string:
    ecasound -z:mixmode,sum -x -z:nodb -b:64 -a:pre1 -i:alsa,hw:Device -pf:/etc/pre1.ecp -o:loop,1 -a:pre2,woofer -i:loop,1 -a:mid,tweeter -i:loop,2 -a:pre2 -pf:/etc/pre2.ecp -o loop,2 -a:woofer -pf:/etc/woofer.ecp -chorder:1,2,0,0,0,0 -a:mid -pf:/etc/mid.ecp -chorder:0,0,1,2,0,0 -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,0,0,1,2 -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Device

    Do you see anything obvious that I am doing wrong? Running the same Linux distro as you with a Zotac Nano PC and a Vantec USB 7.1 audio interface.

    I should note that this is passing audio through — either with the S/PDIF input or line input of the interface. But I would like to know why there are these locked params and what I can do about it. I’ve also tried SOX for dithering, but no matter what buffer I type in I always get a under-run. Thanks in advance.

    • There are no errors here: ecasound is just being verbose about reporting what sample format it’s using to pass data between audio chains. If it really matters, you can probably prevent this by explicitly setting the audio format (i.e. -f:f32_le,2,44100 or -f:s16_le,6,44100 as appropriate) at the input and output of each chain. But I find it easier to just let ecasound use sensible defaults, since they end up being correct.

  18. Piping to sox for ditther gave me delays between the channels, and also some alsa buffe underrun problems, not anything you have encountered? Tried to change buffer sizes up and down for both ecasound and sox but did not manage to make it work.

      • Late answer … But the system is a AMD athlon2 X4 2.9GHz with 6GB of RAM etc. Doing nothing else at the moment, and i dont see any extreme CPU usage.

        Running with the following command:
        sudo LADSPA_PATH=/usr/local/lib/ladspa:/usr/lib/ladspa ecasound -z:mixmode,sum -x -b:8196 -a:pre1 -i:alsa,hw:SB -pf:pre1.ecp -o:loop,1 -a:pre2,woofer -i:loop,1 -a:mid,tweeter -i:loop,2 -a:pre2 -pf:pre2.ecp -o loop,2 -a:woofer -pf:woofer.ecp -chorder:1,2,0,0,0,0 -a:mid -pf:mid.ecp -chorder:0,0,1,2,0,0 -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,1,2 -a:woofer,mid,tweeter -f:f32_le,6,44100 -o:stdout | sox -q -c 6 -r 44100 -b 32 -e float -L -t raw – -e signed -c 6 -b 16 -t alsa surround51:SB dither -s

        I have output, and the dithering seems to work, but the woofer output is not in sync with the mid so something is not right. Also sporadical sox WARN alsa: under-run

          • I’m also having strange issues where the woofer is out of sync with the mid and tweeter. At least that’s what it sounds like. The amount is directly related to the buffer setting for ecasound. The larger I make the buffer, the larger the delay. At buffer of 2048 the delay sounds like several hundred ms. The command I’m running is below. Any help would be greatly appreciated.

            export LADSPA_PATH=/usr/local/lib/ladspa:/usr/lib/ladspa; stdbuf -i0 -o0 -e0 ecasound -z:mixmode,sum -x -z:nodb -b:2048 -a:pre1 -f:16,2,44100 -i stdin -pf:pre1.ecp -o:loop,1 -a:pre2,woofer -i:loop,1 -a:mid,tweeter -i:loop,2 -a:pre2 -pf:pre2.ecp -o loop,2 -a:woofer -pf:woofer.ecp -chorder:1,2,0,0,0,0,0,0 -a:mid -pf:mid.ecp -chorder:0,0,1,2,0,0,0,0 -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,0,0,1,2 -a:woofer,mid,tweeter -f:s16,8,44100 -o:stdout

          • I never got around to looking into this, but it’s strange behavior. A buffer of 2048 samples is less than 50 ms at 44kHz. I’d be surprised if that were perceivable as a delay, especially at woofer frequencies. It’s only a two periods for a 40Hz tone. Sounds like a bottleneck somewhere in the system, likely USB if that’s where your sound card is. Are you seeing buffer over/under-runs when reported on the command line? I’m curious whether you sorted this out…

  19. Hi,
    Thank you very much for developing these and posting them online. I have been starting to follow your very good examples.

    I installed a fresh Lubuntu 14.10, and couldn’t get the make command to work (error 127). It turns out the gcc compiler is not installed in the Lubuntu distro. So I just wanted to add a comment here for others with limited linux experience like myself that you first need to install the gcc complier: sudo apt-get install gcc

  20. I wonder: have you or anyone else tried this on a raspberry pi 2? That may have enough horsepower to do the inputs, outputs, and dsp at the same time.

    • Actually, even the RPi rev.B has the horsepower. I’ve checked myself: I was able to do realtime 6-ch dsp at 44kHz, at around 50% cpu load. The problem is with getting the output through the usb bottleneck: there is a longstanding bug in the RPi that causes usb packet loss under heavy load. I tried several workarounds but the result was always alsa underruns and choppy audio. I believe this limitation persists in the Pi 2 but haven’t tried.

      A good alternative is to send the audio over hdmi. AFAIK there is no alsa driver that can output 6-ch audio directly to hdmi on the Pi, but you could pipe the audio from ecasound to omxplayer, which does do multi-channel audio (a feature I requested for this purpose). I never followed up on this, but it should work.

      • Hi,
        I was able to get surround51 working on my RP 2 going to a USB soundcard to a tri-amped setup but only as output. That is, only if the music was already on the PI 2 system. I have had a devil of a time getting the USB I/O figured out. The PI 2 routes both Ethernet and USB through one channel.

        I am likely to give up on the I/O part but it may be interesting to see if Clementine or similar loads well on the RP2 and use it for output only to a USB surround card. Anyone else have any luck?


      • How do you pipe audio from ecasound to omxplayer? I’m failing at this (seemingly) trivial task. Usually piping in linux is just a matter of “do something | send it here”

        James R

          • I got it to work with a named pipe, e.g. “`ecasound -tl -i ~/Music/chan_labels_6.wav -f:16,6,44100 -eadb:-24 -b:2048 -o:audiopipe1.wav & omxplayer -o hdmi –layout 7.1 audiopipe1.wav“` But now it’s stopped working – ecasound is giving me the error:
            “`(eca-fileio-stream) (eca-fileio-stream) fseek() error! (lfs).“`
            Any clue what that means? The only difference is a day of being unplugged.

  21. Great work. Is there any tutorial for using ecasound as a 5.1 channel equalizer? I am replacing my emotiva preamp and doing everything on the the computer (htpc ubuntu 14.04, kodi isengard 15.0 media center, xonar d2 soundcard) and I need to apply different bandpass filters to the front, center, back, and subwoofer speakers after the digital sound stream is output from kodi. Eventually I wish to replace my Carver Amazing speaker with the one shown below which would require bi-amplification and a dsp crossover.

    With regard to speakers you should look at ––301-980

    with theory at –

    • 5.1-channel eq is simpler than building a crossover. You could do something like this:

      ecasound -x -z:mixmode,sum \
      -a:all -f:16,6,44100 -i:stdin -o:alsa,surround51:Device \
      -a:mains -pf:mains.ecp -chorder:1,2,0,0,0,0 \
      -a:center -pf:center.ecp -chorder:0,0,3,0,0,0 \
      -a:sub -pf:sub.ecp -chorder:0,0,0,4,0,0 \
      -a:surround -pf:surround.ecp -chorder:0,0,0,0,5,6

      This assumes standard channel assignments with mains on ch.1/2, center on ch.3, sub on ch.4 and surrounds on ch.5/6. What’s going on here is all filter chains have 6 channels, but channels that don’t belong to a given collection of speakers are muted so they don’t contribute to the summed output.

      The text files mains.ecp, center.ecp, sub.ecp, and surround.ecp would be used to define the filter chains to be applied to the respective channels. So e.g. sub.ecp could contain something like this:

      sub = -el:RTparaeq,-6.0,80,2.6

      to put a parametric eq on the sub channel only (-6dB with Q=2.6 at 80Hz).

    • Thanks for the CBT tip! I finally got around to reading all those papers. The theory on those is beautiful.

      I’ve been wanting to design a portable but high-quality mono unit for sound reinforcement (amplifying the digital piano in my band). This seems ideal.

  22. Will it be possible to omit the fixed sampling frequency in the filters?
    It could be nice, if the crossover was able to switch sf synchronously with the music files.
    As I have music stored in 44.1, 48, 88.2, 96, 176.4 and 192, in 16 and 24 bit, it would be nice to not having it resampled in the crossover.

    An other interesting thing could be to pipe output through I2S to 2-4 DAC-boards (one board for tweeters, mids, woofers etc.). I know the ESS9023-based boards often go for around 20-25 USD. That way one could make a seriously well performing xo with high-end level of sound quality.

    • One approach is to run the xover/eq filters directly within alsa. i.e. write a custom .asoundrc to create a virtual multi-channel audio device that applies the ladpspa filters and sends the output to the right hardware channels. I haven’t tried this myself, both others have worked on it, see e.g. this post on diyaudio.

  23. If you needed more that 8 synchronized channels what would you use for a DAC on linux. The only ones I have found are the RME Hammerfall cards that allow you to increment 4 analog channels at a time.

  24. Hi Richard

    I just wanted to thank you for this tutorial!
    I have been using your crossover solution in my car since september 2013.
    Running on a small Atom/Ion laptop in the glove compartment, it does its job very well!

    Keep up the good work!

  25. Hi Richard,

    Many thanks for this awesome tutorial!
    I have been successfully building a small system with a Wandboard, MPD and an Hypex amplifier following your good advice. I have to say that the ecasound part thanks to you as been the easiest part !

    It works great and I control it with MPDroid associated with a small piece of software I have developed to manage a switch and a volume control bridging this with mpd. The source is available on github if any one is interested (

    Thanks again

  26. I sent Don Keele you comment on the CBT. Below is his reply with more CBT references/links if you are interested –

    ——– Forwarded Message ——–
    Subject: RE: CBT
    Date: Sun, 27 Dec 2015 16:49:43 -0500
    From: Don Keele
    To: ‘Alan Bromborsky’

    Hi Alan,

    Thanks for the reply and the Richard Taylor link. Very interesting! Nice complement he gave me.

    Feel free to pass on the following to Richard.

    FYI: Excerpt from another email:

    I just returned from the AES convention in NYC Oct. 27 to Nov. 1, 2015. I gave three CBT papers! I was a busy guy! FYI: Here’s a Dropbox link to the three papers along with the three PowerPoint presentations if you’re interested in the gruesome details:

    I have written a total of nine AES CBT papers since 2000). Here are the titles of the three papers I gave in NY:

    1. “Directivity-Customizable Loudspeaker Arrays Using Constant Beamwidth Transducer (CBT) Overlapped Shading”

    2. “Time/Phase Behavior of Constant Beamwidth Transducer (CBT) Circular-Arc Loudspeaker Line Arrays”

    3. “Implementation of Segmented Circular-Arc Constant Beamwidth Transducer (CBT) Loudspeaker Arrays”

    With these three papers I’ve written nine CBT papers in all since 2000! I’m a dedicated evangelist for the CBT array technology!

    You should be interested in the Time/Phase paper. I properly implemented CBT array will reproduce square waves at all locations (whatever that’s worth)! It’s essentially linear phase and time aligned at all locations. Look at the Power Point first; it will make the most sense.

    Thanks again!

    Best Regards,

    Don Keele

  27. I am working on this setup with an Odroid C1+ and two different surround sound USB cards. The speaker test above for 6 channels works perfectly.

    However, when I try the test for ecasound using the 6 channel wave I only get the woofer and mid pairs and garbled sounds for the tweeters.

    It is the same for both cards. I checked the ecasoundrc file and do not see a conflict. Thoughts?

    • Replying to myself. The downloaded 6 channel sample may be messed up. I ignored it and used this command to get great sound out of my USB card using an Odroid C1+ SOC. Wow, this is cool:

      ecasound -z:mixmode,sum -x \
      -a:pre1 -i:/home/odroid/peggy.wav -o:loop,1 \
      -a:pre2,woofer -i:loop,1 \
      -a:mid,tweeter -i:loop,2 \
      -a:pre2 -o loop,2 \
      -a:woofer -pf:/etc/woofer.ecp -chorder:0,0,0,0,1,2 \
      -a:mid -pf:/etc/mid.ecp -chorder:1,2,0,0,0,0 \
      -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2,0,0 \
      -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Audio

      Now, i need to figure out how to use mpd so I can load all the music on to my Odroid SD card. Is this a sane mpd command?

      audio_output {
      type “pipe”
      name “DSP crossover/eq”
      format “44100:32:2”
      mixer_type “software”
      command “ecasound -q -z:nodb -z:mixmode,sum
      -a:pre1 -f:s32_le,2,44100 -i:stdin -o:loop,1
      -a:pre2,woofer -i:loop,1 \
      -a:mid,tweeter -i:loop,2 \
      -a:pre2 -o loop,2 \
      -a:woofer -pf:/etc/woofer.ecp -chorder:0,0,0,0,1,2 \
      -a:mid -pf:/etc/mid.ecp -chorder:1,2,0,0,0,0 \
      -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2,0,0 \
      -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Audio

      • Default Odroid update

        Well, where to start? I am using a Jessie OS for the Odroid downloaded from the site. I autoremoved pulseaudio. I installed ecasound and all relevant alsa parts. I have edited the mpd.conf per the command above including putting the ecasound command in one long line. The only audio_output I am using in the mpd.conf file is “pipe”.

        GMPC sees the music I downloaded into a file. It plays fine but it does not employ the crossover instructions. That is: I get six channels of audio through my USB SoundPro card but they are all full-spectrum. I checked my LADSPA_PATH and made sure it pointed to the ladspa files too.

        If someone can point me where I am wrong please help. Here is the command which is in one very long line between the quotation marks:

        audio_output {
        type “pipe”
        name “DSP crossover/eq”
        format “44100:32:2″
        mixer_type “software”
        command “ecasound -q -z:nodb -z:mixmode,sum -a pre1 -f:s32_le,2,44100 -i:stdin -o:loop,1 -a pe2,woofer -i:loop,1 -a:mid,tweeter -i:loop,2 -a pre2 -o loop,2 -a:woofer -pf:/etc/woofer.ecp -chorder:0,0,0,0,1,2
        -a:mid -pf:/etc/mid.ecp -chorder:1,2,0,0,0,0 -a:tweeter -pf:/etc/tweeter.ecp -chorder:0,0,1,2,0,0 -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Audio”

        I get no errors at all and it actually works really smoothly. It just does not get crossed over.

  28. I thought I might add that I was unable to install mpd from source. I kept getting a dependency failure for libicu. I located and installed ICU and libicu-dev successfully but it kept hanging. I was at my limit for Linux skills already.

    The mpd.conf file had “pipe” in it already and it is the only output I am using. Perhaps I am wrong but my thought was if “pipe” was not enabled I would get no output. Is that wrong?



  29. Got it done. Running smooth on an Odroid C1+ through a Sewell SoundPro using MPD & GMPC. It has a three-way crossover and is tri-amped. Thanks for this website! Sounds good. I needed to recompile MPD from source.

      • Hi,
        It was unclear whether it was enabled or not to me. The “pipe” command existed in the list of commented options in the mpd.conf file but it seemed like it was a generic mpd.conf.

        When I enabled pipe out in the existing file it did not work. So, I tried recompiling and then it did work. I especially like being able to tweak the crossover to get things just right for my ears. I have no testing equipment so I am just listening and making adjustments.

        Thanks again for sharing this with all of us.

  30. Just completed successfully building your filters on an “orange pi plus” to replace my old PC. Only problem I had was figuring out what I needed to install….

    The build went flawlessly after installing gcc, build-essentials, cmake, cppcheck….

  31. Richard,

    I am thinking that an Odroid and USB card like I have worked with would work to implement Linkwitz’s Pluto system.

    What do you think? Like a Poor Man’s Pluto?


  32. I am laughing because I just clicked the next link above and the page is about Pluto implementation. I had not seen that page before. I am going to have a go at it.

  33. Made a kernel change and got multichannel out over hdmi on the raspberry pi 2!
    Now I’ve run into a couple issues:
    1) How can I edit the channels so I have 8 channels to work with? looking at
    “` ecasound -z:mixmode,sum -x \
    -a:pre1 -i:mysong.mp3 -pf:pre1.ecp -o:loop,1 \
    -a:pre2,woofer -i:loop,1 \
    -a:mid,tweeter -i:loop,2 \
    -a:pre2 -pf:pre2.ecp -o loop,2 \
    -a:woofer -pf:woofer.ecp -chorder:1,2,0,0,0,0 \
    -a:mid -pf:mid.ecp -chorder:0,0,1,2,0,0 \
    -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,1,2 \
    -a:woofer,mid,tweeter -f:16,6,44100 -o:alsa,surround51:Device“`
    I assumed I could just change to something like this:
    “`sudo ecasound -z:mixmode,sum -x \
    -a:pre1 -i Daydream\ Nation\ -\ Sonic\ Youth\ -\ \’cross\ The\ Breeze.mp3 -pf:pre1.ecp -o:loop,1 \
    -a:pre2,woofer -i:loop,1 \
    -a:mid,tweeter -i:loop,2 \
    -a:pre2 -pf:pre2.ecp -o loop,2 \
    -a:woofer -pf:woofer.ecp -chorder:0,0,1,0,0,0,0,0 \
    -a:woofer,mid,tweeter -f:16,8,96000 -z:nodb -b:2048\
    I’m testing, so I only want one output at a time so I can map the channels, I ran “`speaker-test -c 8 -r 48000 -D hw:0,0 -t wav“` and that sends audio to every channel, as expected. What happens when I run the above code is that all the audio is running to the left front channel (channel 1). Any idea how I can get 8 channels working. I only need 6, but my right front channel isn’t working, so 8 channels would let me work around that.

    The next issue is that the audio is fairly distorted if I keep -b to high or too low. -b:2048 seems to be the best setting, but I can still hear the occasional dropout, what’s the best way to deal with that?


    • To add a little color: this works:
      pi@raspberrypi:/media/usbstick $ ecasound -tl -i chan_labels_6.wav -f:16,8,44100 -eadb:-2 -o:alsahw,0,0 -chorder:1,2,3,4,5,6,7,8

      in that by switching the channels individually, I can come up with a channel map, so using chan_labels_6.wav, if I play

      ecasound -tl -i chan_labels_6.wav -f:16,8,44100 -eadb:-2 -o:alsahw,0,0 -chorder:0,0,0,0,0,0,0,3

      I hear “left mid” come out of my Surround Back Right channel on the amp.

      but when I try to play

      sudo ecasound -z:mixmode,sum -x \
      -a:pre1 -i /media/usbstick/04\ -\ South\ Side\ of\ the\ Sky.flac \
      -pf:pre1.ecp -o:loop,1 \
      -a:pre2,woofer -i:loop,1 \
      -a:mid,tweeter -i:loop,2 \
      -a:pre2 -pf:pre2.ecp -o loop,2 \
      -a:woofer -pf:woofer.ecp -chorder:0,0,0,0,0,0,0,1 \
      -a:mid -pf:mid.ecp -chorder:0,0,0,0,0,0,0,0 \
      -a:tweeter -pf:tweeter.ecp -chorder:0,0,0,0,0,0,0,0 \
      -a:woofer,mid,tweeter -f:16,8,44100 -o:alsahw,0,0 -z:nodb -b:2048

      I don’t get any audio out of the SBR channel . I can get sound out of SBR if I use -chorder:1,0,0,0,0,0,0,1, but that outputs sound to the front left and SBR channels. I also don’t hear any filtering on the audio even though I’m running through the woofer chain. Where do the woofer.ecp files live?

  34. Thanks for the fantastic work that you’ve been putting in, and making it available to us all. This is a solution I’ve been looking for for quite a while. I’m not very experienced with command line linux and certainly not ecasound but I’ve managed to get your approach working on my setup – which is a simple PC running Ubuntu 16.04 with an on board 7.1 soundcard on an ASUS motherboard. This feeds out to a Pluto inspired setup with subwoofers cuttin in at 100 Hz. I’m really pleased with the result – Tunable in a way that my previous ASP just wasn’t. The problem I’ve got, though is that I don’t know how to pipe audio from running applications (eg players like VLC or Amarok, or spotify) to ecasound. I’ve tried for eg

    spotify stdout | ecasound -i:stdin (plus the rest of the ecasound script)

    but haven’t had any progress. I presume it’s just simple ignorance of how to set up a pipe in linux but could you please tell what I’m missing?

    • Your ecasound command is correct, but not all audio players allow streaming pcm audio to stdout. Each has its own syntax, which should be documented in its man page. For vlc the appropriate command-line option is ‘–sout’. I don’t know if spotify or amarok can do this. Even if it can, know that the raw pcm data will be headerless, i.e. with no self-identifying info about sample rate or format, so you will need to make the format explicit in your ecacound command line.

      Probably the best approach is to not use ecasound for this. Instead, set up the ladpsa filters in a custom alsa device (‘virtual soundcard’). Making this device the system-wide default will route all audio to your setup, regardless of what player you’re using. I haven’t tried this myself, but I’ve seen working systems documented elsewhere, e.g. in this thread:

      • Thanks for that. What you’re suggesting makes sense and I’ll explore it. In the mean time I’ve found a solution that seems to work: Use Mopidy – a MPD variant that integrates easily with spotify and other streaming inputs as well as local sources with a good range of frontends. However this only uses Gstreamer output format so I couldn’t see how to make it use your MPD solution. Instead I installed Jack and used this to connect Mopidy to Ecasound with your active crossover. It seems a long way around but at the moment it’s working pretty well. The good thing is that I can use your LADSPA filters with Ecasound to tune everything just as I want and the frontend options to Mopidy give lots of flexibility to managing both streaming and local file sources. I’ve still to check it out fully but so far I’m happy. Thanks for your fantastic work and inspiration. I’m enjoying reading your other audio stuff.

  35. Dear RIchard,
    thank you for this tutorial. It is exactly what I was looking for my Raspberry Pi for a very long time. I tested it on a Raspberry Pi 2, 3 and zero with soundcards/dacs from HifiBerry and Pimoroni PhatDAC. Works fine.
    BUT: Until now I just was able to playback a mp3 file to test my settings.
    Now I really like to output music played by Squeezelite on my Pi.
    Sadly I did not understand what I have to start to output what Squeezelite is playing:
    ecasound -z:mixmode,sum -x \
    -a:pre -i:?????????????? -pf:pre.ecp -o:loop,1 \
    -a:woofer,tweeter -i:loop,1 \
    -a:woofer -pf:woofer.ecp -chorder:1,2,0,0 \
    -a:tweeter -pf:tweeter.ecp -chorder:0,0,1,2 \
    -a:woofer,tweeter -f:16,2,44100 -o:????????????

    Squeezelite is usually playing on sysdefault:CARD=sndrpihifiberry
    At the moment, Squeezelite starts with ALSA parameters 80:4::
    I would be really happy if you could help me out on that.
    Many thanks and best regards

    • Run squeezelite with “-o -” on the command line to pipe output to stdout. Then run ecasound with “-i:stdin”. You probably also need to explicitly tell squeezelite what sample rate / bit depth to output (the man page describes how) and configure ecasound’s input format (with “-f:,,”) to match.

      • I get the following:
        “WARNING: Underrun in reading from “stdin”. Trying to recover.”
        For about 1000 Times until I stop via ctrl+c.
        I did not change any rat/bit depth in squeezelite.
        I get the same message when squeezelite ist NOT running and I start ecasound.
        What’s wrong?

          • I did a total new install of raspbian on the raspberry pi. I also newly installed squeezelite and everything around ecasound as described above. I start squeezelite manually via squeezelite-armv6hf -o – -n raspberrypi. After that I start
            ecasound -z:mixmode,sum -x \
            -a:pre -i:stdin -pf:pre.ecp -o:loop,1 \
            -a:woofer,tweeter -i:loop,1 \
            -a:woofer -pf:woofer.ecp -chorder:1,2,0,0 \
            -a:tweeter -pf:tweeter.ecp -chorder:0,0,1,2 \
            -a:woofer,tweeter -f:16,4,44100 -o:sysdefault:CARD=sndrpihifiberry

            as there is a hifiberry DAC attached.
            No luck.
            I bet it’s something very small. Parameter. Setting. I don’t know. But I hope you’ve got some ideas I can test. I really want to make it work.

  36. Well perhaps this one gives a hint. As soon as I start squeezelite (/opt/squeezelite/squeezelite-armv6hf -a 16 -o – -n raspbox) and start playing audio, my terminal / console editor I use to ssh my Raspberry Pi is getting tons of these:
    ??’??.é?0_?????????????j???S???M???i????ä???g???s???????????Ü???7???G??????????????e???S???J???X???????????????????$??8ä?<c? Ä? v? D???????????? ?????e??E??????T??S.??=??é??V??&????????????K??H??)!$????1??????????????????????#&2 ????????????,??#?????????????????????? ?? ??S?I?"·???????????????├????
    ??????????????????6??????????Y???f??????@??K??;??1?? ?␊???H???:???D???[???┬???????⎺?'┐?┼???±???⎼???????????⎺? q???Z???j???????????
    ??!????@??????P??u??Aä???é???T?8a?U??#??????????!??Uk?tR?lM?>O? L?3ö?T?Ü??P??;??:??Y??gt?Kt?C??n?????o??A??????+??S??n??e??;??%??R?????V??z?????’??M??S??Vu?????????????????????????r?)??????ö???S???F???Å?????????????.??>t?-z???????????????????????????????#??@??(?????????? ??

    • Looks like squeezelite is sending the audio data to the terminal, not ecasound. Probably I wasn’t clear. You need to run both squeezelite and ecasound in one command, with the output of one sent to the other via stdout, like this:
      squeezelite -o - [...] | ecasound -i:stdin [...]

  37. Hi,

    What a great tutorial! I have successfully implemented the x-over.
    I have 2-way x-over implemented in ecasound for LX mini.
    Although everything is working properly, but i do have an annoying issue.

    Here is my setup:
    Kodi > Alsa Loopback > ecasound > Alsa hw (HDMI) > AVR Receiver

    The problem i have is drifting (not sure if this is the right term).
    What i mean is, if i restart ecasound, and start watching a movie. The audio and video are in sync. But 2h later, the audio is a little bit delayed (just few ms).
    But in few days, the delay is getting bigger, like few seconds.

    Until now i have no idea how to fix this.
    Do you maybe know what is causing this?

  38. Disclaimer: I know nothing about audio :P.

    Why not simply use ecasound’s -efh and -efl parameters as resp. high- and low-pass filters? What’s the benefit of using those rt-plugin filters?

    • The benefits are:

      1) avoiding the guess-work / reverse engineering involved in using the built-in filters
      2) adding missing functionality

      One could certainly use the built-in -efh/-efl. But the documentation doesn’t specify the order (1st? 2nd?) or critical frequency (is it the -3dB point? -6dB?) so it’s hard to know exactly what these filters are doing.

      A 2nd-order low- or high-pass with specified Q value is a common (and standard) building block in signal processing. Since ecasound doesn’t have such a thing built in, I wrote my own.

  39. Dear Richard,
    First let me say you did stunning job. I already managed to make working 3way crossover chain but I am facing two problems:
    1/. When I place in the line command like “-el:RTlr4lowpass,3500” it works. BUT when I try to create *.ecp file consist inside this command (let’s say low.ecp) and put this command in line instead of -el:RTlr4lowpass,3500 filter doesn’t work (actually it passes all frequences). I cant figure out what am I doing wrong. Where should I put *.ecp file? I already try directory where filters are as well as home directory. Doesnt work. The second reason could be I did something wrong in creation the ecp file. Could you please give me example of properly written ecp file and clear where it should be placed in therms of catalogues?
    2/. I already got rc.local file as follows:
    #!/bin/sh -e
    # rc.local
    modprobe snd-aloop

    exit 0

    This is intended to run loop fake sound card during boot.
    How should I add to this file which describe filter chain (let’s say to run it automatically on boot?
    For now I handled it at this way that i make file run in startup programs as program (-e gnome-terminal /path/ but it open terminal each time I boot and to keep filter working I need to keep terminal open which is extremely annoying.

    Please help!

    • Sorry, probably getting back to this too late to be helpful, but in any case:

      1. You can but the *.ecp files anyhere; just use use the full path to them in your ecasound command. I put them in /etc. My ecasound command looks like ecasound […] -pf:/etc/low.ecp […]

      2. On some systems I put my ecasound filter chain directly in rc.local, just before the “exit 0”. Run ecasound in the background by terminating the command with “&”. I’ve sometimes found it helpful to add commands to mute the audio before running ecasound and to unmute it after. Let me know and I’ll post my rc.local here if you like.

  40. Two years ago this page revolutionised my sound set up! As noted above, I wanted to route streaming music (as well as local files) to Richard’s ecasound digital Xover. The best I managed was to use mopidy which has spotify and youtube as well as local file backends feeding via gstreamer to jack which has an output that ecasound can read. This has worked well even though it’s a bit roundabout, but recently mopidy has been having trouble with changes in the spotify protocol which means that I haven’t been able to load playlists which is pretty frustrating.
    However, there is a simple fix which enables all systemwide audio to be fed through jack and then into ecasound. Just get jack to run the following script after startup (in Setup…options)
    pacmd load-module module-jack-source channels=2; pacmd load-module module-jack-sink channels=2;
    Best to also get it to run killall jackd after Shutdown to keep things clean, if you wish. So now all audio gets automatically fed to ecasound as soon as jack is loaded and then to my Linkwitz Pluto+subwoofer (variants). Best computer monitors I’ve heard!

  41. Hi Richard,

    Thanks for very clear and informative explanations. I came to your HOWTO after getting a Gradient (sub)woofer for my beloved QUAD Els 63. The seller had not keep the crossovers but his price was right for the subs alone.

    I adapted your configuration to send two channels (woofer and tweeter) to two distinct DACs:
    ecasound -z:nodb \
    -z:mixmode,sum -x \
    -a:pre \
    -i:test.flac -pf:pre.ecp -o:loop,1 \
    -a:woofer,tweeter -i:loop,1 \
    -a:woofer -pf:woofer.ecp -o:alsahw,1,0 \
    -a:tweeter -pf:tweeter.ecp -o:alsahw,2,0 \
    It works as it should.
    Next, I struggle to have it work with MPD. I adapted the pipe as
    audio_output {
    type “pipe”
    name “DSP xover”
    format “44100:16:2”
    mixer_type “software”
    command “ecasound -q -z:nodb -z:mixmode,sum -a:pre -i:stdin -pf:/home/dussault/Musique/pre.ecp -o:loop,1 -a:woofer,tweeter -i:loop,1 -a:woofer -pf:/home/dussault/Musique/woofer.ecp -o:alsahw,1,0 -a:tweeter -pf:/home/dussault/Musique/tweeter.ecp -o:alsahw,2,0 ”
    Difficult to read because I had to put it on a single line to start the MPD server. My server seems not to accept opening ‘ ” ‘ without closing it on the same line.

    My setup works (plays tracks from my library through MPD), but does not xover, I get the same sound on both hw,1,0 and hw,2,0 (which is not the case when using ecasound directly).

    I run MPD 0.20.18 under Ubuntu 18.04 (bionic).

    Thanks for any help!

    • Hi JPD,

      Not sure what’s happening here, but try logging any error messages from ecasound to a file, e.g. use “ecasound ... -o:alsahw,2,0 2&> /home/dussault/ecasound.log” in your mpd setup. Then play some music in mpd and watch the contents of ecasound.log. Let me know what you find…

      Most likely causes are:
      1) mpd not having read permission for the filter config files in /home/dussault/Musique
      2) mpd running in an environment without the LADSPA_PATH environment variable set, and so unable to find the ladspa filters, in which case you might need to preface your ecasound command with e.g. “export LADSPA_PATH=/usr/local/lib/ladspa; …” or wherever your ladspa filters are installed.

      In either of these scenarios ecasound is likely to just blunder on and output the unfiltered audio.


      • Thank you Richard!
        It was the LADSPA_PATH missing that caused my problem.
        I am a newbie with signal processing. Would you happen to know of a filter that could provide a 6db/octave boost to compensate for the cancellation of the dipole woofer?
        Thank’s again!

        • Glad to know you were able to solve your problem!

          As for filters to compensate for dipole rolloff, there are at least a couple options discussed by Siegfried Linkwitz. He shows electronic circuits, but all can be implemented in digital by chaining one or more of the standard biquad filters. I typically use a shelving filter to boost low frequencies while leaving high frequencies at unity gain (you will need to experiment with values for the center frequency, gain and Q-factor to achieve the target 6dB/oct slope in the frequency band where you want it). You might also want a shallow notch to level the peak that occurs where the response stops rising (where path lengths for radiation from front and rear differ by a half-wavelength).

          • Hi Richard!

            Thank you so much for your tutorial and further help! I succeeded in implementing a quite good performing cross over. I wrote a report about my setup (in french), available at .

            I use this setup in a second home, and did not touch it nor use it for two weeks. Two weeks later, unfortunately, the setup does not work anymore. I absolutely do not understand. Here is a simplified description of what happens: I execute

            ecasound -z:nodb \
            -z:mixmode,sum -x \
            -a:pre \
            -i:test.flac -pf:pre.ecp -o:loop,1 \
            -a:tweeter,woofer -i:loop,1 \
            -a:woofer -pf:woofer.ecp -o:alsahw,1,0 \
            -a:tweeter -pf:tweeter.ecp -o:alsahw,2,0 \

            and it ends with an error message

            (eca-chainsetup) Opened input “loop”, mode “read”. Format: f32_le,
            … channels 2, srate 44100, interleaved (locked params).
            (eca-chainsetup) Opened output “alsahw”, mode “write”. Format: s16_le,
            … channels 2, srate 44100, interleaved.
            ERROR: Connecting chainsetup failed: “Enabling chainsetup: AUDIOIO-ALSA:
            … Error when setting up hwparams: Erreur d’entrée/sortie”

            when chaining the tweeter. Using -dd or -ddd does not give any additional clue, just the same error message.

            If I replace any of alsahw,X,0 by a dummy alsahw,0,0 , the other plays its part OK. So, individually, both the woofer and the tweeter play OK. Together, error, no output.

            I repeat that two weeks ago, everything played as intended. All that *could* have change since two weeks is some updates in Ubuntu 18.04 (bionic) packages (I did not track the updates).

            Would you have (once again!) any clue to help me?

            Thanks again,


          • Hi Jean-Pierre,
            Glad you found this useful!
            Alas, I don’t know what’s causing this error. My first thought is that the system update has resulted in your physical sound cards being assigned to different numeric alsa id’s than before. Because of this possibility, I always refer to cards by name. So for example my output looks like “-o:alsa,hw:UA25EX“. The device name (in this case “UA25EX”, found by doing a cat /proc/asound/cards) is determined by the hardware device itself, so will not change with system updates.
            Admittedly, this doesn’t seem to have anything to do with your problem. It’s just the only thing that worries me about your setup at first glance.

          • I just read your report — very nice! Good idea to use a lowpass filter to limit driver excursion at low frequency, but I highly recommend changing it to a 2nd-order filter (12dB/oct). The RTlr4hipass that you’re using is 4th-order (24dB/oct). That steepness comes at the cost of high group delay, especially at very low frequency, which can result in audible smearing/doubling of transients (e.g. I can often notice this on a piano’s hammer strikes). Good 2nd-order alternatives are my own -el:RThighpass,20,0.7 or else -el:hpf,20 (from the ladspa-sdk package). I might also set it to 30Hz rather than 20Hz; dipole radiation at 20Hz demands a punishing amount of excursion.

  42. Hi Richard,

    this is an amazing guide, thanks for the work.

    Have you figured out a solution for digital input (in my case over spdif)? As long as the digital input has the right sample rate everthing works flawlessly. However when the signal is lost (source device powered down or optical cable unplugged) I of course get the loud digital noise.
    Is it possible to detect when the input stream is incorrect or missing? Then one could trigger a script to use a different ecasound command with for example analog input.
    Currently I use a small external dac to convert the digital input to an analog input signal for the pc where the filters are implemented. I was just wondering whether it is possible to eliminate this in theory unnecessary DA and AD conversion.

    • Hi Max,
      Thanks; glad to know people still find this useful!
      And no – I haven’t figured out how to gracefully handle errors/dropouts in a digital input stream. That issue (in addition to the problem of unknown sample rate and resolution) makes it difficult to handle digital input from unknown or varying sources.
      Your idea of wrapping the ecasound command in a trigger script is certainly one solution, but this issue is probably best dealt with in hardware. Your extra DA/AD stages are actually a decent solution: likely less error-prone than any script I could write, and if they’re implemented well then any degradation of the signal should be negligible.

  43. Hi Richard
    How do you deal with channel shift and volumecontrol if you combine a mediaprogram on the pc for streaming and for example and turntable on an analog input?
    I need and physical volume knob + selector,
    Also when playing from vinyl, if i used volume control in mediaprogram this will not attenuate the signal.
    Br Morten

    • Hi Morten,
      Not sure what you mean by channel shift… But as for a physical volume knob, this presents a challenge. Some people have used a stepped attenuator (with as many channels as required) just before the input to the power amp. I don’t have any experience with this myself, since I don’t have sources that are external to the PC like vinyl.

  44. Great tutorial,
    Got the filters working in stereo working…but:
    I am trying to downmix the stereo signal for a 2 way mono wallspeaker.
    In the manpage front ecasound under routing and mixing chmix is mentioned.
    -chmix:1 -i:mysong.mp3 -chorder:1,0 -f:16,1,44100 -o:loop,1 \ does not work..
    The mixed signal should result into a loop 1, which will be used as in the examples above as i am correct.

    Any ideas?


  45. Hi Richard!

    I’ve been aware of your work for 5-6 years, but I have finally in the last year made myself some LX Minis. I’m currently using the MiniDSP 2×4 HD, but as I’m using an rPi for the source, I am excited about trying out your work.

    I’m wondering if you know of anyone integrating the ecasound command line script(s) directly into the ALSA configuration, so the transforms are transparent to *any* software running on the host?

    I’m certain it’s “possible” but ALSA config files are pretty tricky. I’m thinking that the config could have some kind of loopback where it sends to a pipe, and gets back on another pipe, then sending out to the right hardware. I’m thinking that this “device” could then be declared as default, and everything would just magically work. Does this makes sense?

    Alternatively, I am a fan of PulseAudio (though I know that most are not). I’m currently using it to synchronize music streams in several rooms of my house, and I can send audio from software on a server to the rPi connected to the speakers flawlessly. SO, even if configuring ALSA is out of bounds, I could have PulseAudio dump to a pipe, to through ecasound, and then to ALSA cards. The drawback here is that PulseAudio makes pretty extensive use of knowing the real latency of hardware, so it can try and keep things in sync and manage buffers, etc. Obviously, that won’t be possible with a pipe “sink”.

    Any thoughts about any of this?

    And I’m excited to learn from your site that the config parameters for your software uses the same conventions as MiniDSP, so I don’t have to stress about translating values. Thank you for that!

    Thanks for your work and your excellent write up!

    • Hi Matt.

      I’m glad my stuff was useful to you! I haven’t updated in a while because for my purposes it’s a solved problem — there’s just nothing more than needs to be done.

      It would certainly be more convenient to have everything done by ALSA, though. Not using ecasound, mind you, but invoked directly within virtual ALSA devices. I know that others have figured out how to do this. There was a bunch of discussion about this on the diyaudio forum some years ago so you might want to look there. ALSA config files are notoriously tricky, but there’s better documentation or more examples around than when I first tried.

      If you like pulseaudio then you might be able to work something out using pulseeffects, which can route inputs and outputs through filters on the fly.

      I suggest you talk to Charlie Laub, who started out using my ecasound setup but then migrated to gstreamer, which is probably closer to what you’re trying to do. Have a look at the usage notes in his ACDf package.

Leave a Reply

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