Pluto 2.1 with Software DSP

About a year ago I implemented a software-DSP version of the Linkwitz Labs Pluto 2.1.  My aim was to replace the original analog signal processor (ASP) with a small, standalone PC that can double as a media server: like a Squeezebox that also performs xover/eq duties.

Pluto is an outstanding loudspeaker, and really deserves to be built/heard by more people.  Part of my motivation is to provide a lower-cost, easier-to-build alternative to the ASP.

I own the original ASP version of Pluto 2.1 as well: in several hours of level-matched A/B listening tests I haven’t been able to hear any difference — except that my DSP version currently has a less powerful amplifier on the woofers, which limits the peak output somewhat.

If you want to go the software-DSP route yourself, follow the 2-way crossover instructions in my DSP how-to article.  I’ll post the proprietary Pluto-specific configuration files in the Owners’ area of the Orion/Pluto Forum.

Frequency Response

Here’s how the frequency response of my DSP implementation lines up with that of the ASP (the measured DSP response is the dashed magenta curve):

I used a nonlinear least-squares algorithm to tune the IIR filter parameters to get a good match, so the DSP tracks the ASP within about 0.2dB in each pass band.  The residuals plot better indicates the quality of the fit:

Phase Response

I haven’t done any phase response comparisons against the ASP, but since I use IIR filters that model the corresponding analog filters in the ASP the phase responses should be in very close agreement.  My experience with the LX521 DSP confirms this.

To achieve extremely accurate phase/time alignment of the drivers I employ phase equalization via a carefully designed 8th-order allpass; the ASP simply uses first-order all-passes to mimic a pure delay in the crossover band.  I had high hopes my approach would be a significant improvement, but actually I find the difference only marginally audible.


I haven’t had time to make careful distortion measurements, but they probably aren’t necessary: the DSP computation is done in double-precision floating point (i.e. about 53 bits of precision) with dithered re-quantization at the output.  Any quantization noise ought to be negligible.  Measurements confirmed this to be the case in my LX521 implementation, which is very similar.

Digital Clipping

My DSP is implemented in floating-point, so clipping between filter stages isn’t a concern.  Clipping can occur at the final output stage, particularly as the Pluto 2.1 ASP adds a lot of gain, but it all depends on the spectral content of the source material.

I ran my DSP on a corpus of about 10,000 commercial recordings spanning an eclectic range of genres.  For each track I measured the minimum digital headroom at the DSP output, with the overall gain set to match that of the ASP.  The resulting data are summarized in the following histograms, showing the number of tracks having a given amount of digital headroom at the DSP output:

Tracks to the left of 0dB (about 80% of all tracks) will have some digital clipping when the upstream digital volume control is at 100% (0dB); tracks to the right of 0dB have peak levels that leave some headroom at the output.  The red line indicates (somewhat arbitrarily) the upstream attenuation needed so that only 2% of tracks will get clipped.

The optimal compromise between reducing clipping and increasing dynamic range / SNR is up to you.  If you use an upstream digital volume control, you can probably leave things as they are and just turn down the volume if you hear clipping distortion.  This makes the best use of available dynamic range and SNR.  With the upstream gain at -12dB only 1% of tracks will have some digital clipping (and then only on the tweeter).  Below -14dB clipping will never occur.

However, if your volume control is downstream (analog) you probably want to include 12-14dB of attenuation in the DSP path so that clipping occurs rarely or not at all.  Since the DSP is already adding almost 8dB of gain, the net digital attenuation will actually be only 4-6dB (or 1 bit of precision); I feel this is an acceptable compromise.


In the original Pluto 2.1, amplifiers are built into the ASP.  With a DSP implementation you need to provide your own amplification.  Probably the best option (quality and bang for your buck) is to just build the LM3886-based amplifiers according to the Pluto 2.1 plans.

If you want to use an off-the-shelf amplifier you’ll need 4 channels, two of which (for the woofers) need to provide 6dB more gain than the other two.  If you go with 4 identical amplifier channels, make sure they’re rated for 100W/ch with all channels driven; you’ll need to compensate by attenuating the tweeter output by 6dB (either in the DSP or — better — with resistive attenuators at the amplifier’s analog inputs).  I’m happy with the results using an old 75W/ch surround receiver, but its output can’t match the original Pluto ASP+amps.

14 thoughts on “Pluto 2.1 with Software DSP

  1. Hey Richard,

    this is fantastic! Thanks so much for the report of your work. I am considering to build a Pluto 2.1, but the ASP seems a bit imposing/costly. You have documented your method really well. I am a guy who likes computers more than soldering, which makes DSP @ PC very nice. Also good to know you favor the amp of the plans.
    Keep up the good work!

  2. Hi Richard,

    Congratulations on your implementation!

    I currently own a pair of Orion speakers. I would like to implement the ASP in a Raspberry Pi. So far I have already built your ladspa plugins and can output the resulting processed multichannel WAV via HDMI with oxmplayer –layout 7.1 -o hdmi … (right now asynchronously b/c multi-channel HDMI still doesnt work trough alsa).

    I currently own the Orion+ version of the Orion ASP and the most current version is 3.3 – could you outline the process you follow to fine-tune the parameters to match the ASP curves?

    Best regards.

    • To match the ASP curves I started with filter chains that had roughly the right response, with parameters chosen by hand. Then I fine-tuned the filter parameters via a nonlinear least-squares optimization routine to get the closest possible fit. For my LX521 implementation the target response curves came from the measured impulse responses posted on the owner support page (which I converted to magnitude/phase responses via a Fourier transform). For Pluto I just interpolated a few dozen points read off the response graphs on the owner support page.

      I’m curious to see how this works out with the RPi. Please let me know!

      • Hi,

        I can’t believe I did not access this page sooner. I do not own any version of Pluto or Orion and fully understand proprietary information (I am a lawyer by day). And, I do not have the drivers recommended for the Pluto. I will use some stuff I have around here to start and try it by ear.

        I do not have the math chops to understand or implement things like what you said above:

        “I fine-tuned the filter parameters via a nonlinear least-squares optimization routine”

        I will see what I can do via ear tests.


  3. Update: I took the miniDSP parameters user martinnore contributed to the OPLUG forum (for the Orions) and converted the filters in the XLS files (woofer.xls, tweeter.xls and midrange.xls) to your syntax.

    Translation was straightforward, the shelving filters had a different syntax though: they use SL’s two frequencies nomenclature (F1, F2) and I translated them to yours, e.g. F0=sqrt(F1*F2) and A=20log(F1/F2).

    I am outputting 24-bit 7.1 via HDMI with the Raspberry Pi into a Denon receiver that has assignable power amps for 7.1 Multichannel In, so I can assign 4 power amps for the Tweeter, Mid, Sw1 and Sw2 for each speaker. Right now the system is just 2 boxes: tiny raspberry PI and Denon receiver… directly connected to the speakers.

    I processed an impulse WAV file to get the response of the filters using a program called REW to make sure everything is right. Do you recommend any other way?

    I have bought a SPDIF/Analog audio IN card for the Raspberry Pi so next week I will try with external sources and hopefully pipe everything for realtime use.

    • Excellent! I think you’re the first to get multi-channel DSP working on a RPi. I knew it should be possible, but never got around to it. I’m glad you took it on!

      Yes, you should be able to process an impulse (or use an MLS signal) to measure the response of your filter chain with REW. That’s more or less what I do, using my own code.

      How is the power rating on your Denon receiver? Ideally you’d have 100W per channel with all channels driven. Lack of amp power was the only drawback of my implementation relative to the original Pluto.

  4. The Denon has 10 × 170rms watt power amps. You can assign power amps to individual LR LF C discrete input channels coming in from HDMI, with the exception of LFE which is not assignable. In practice this means you can assign 7 channels (not 8) to the power amps. For the Orion this is not an issue because you only need 6 individual channels but it would have been nice to have all 8 to make an all-digital crossover for the LX521.

  5. Richard, one question regarding the translation of the analog filter parameters to digital filters, take for example this filter:

    # U1B: 84-168 SHP1 W-M EQ
    # In the notes on the diagram block: 0db to 6 dB

    I’ve used the following:
    Fc=Sqrt(F1*F2) = 118.79393923934

    which would be:


    I’ve translated all filters in the Orion’s diagram block similarly. However the frequency response I am getting is quite different from the one of the original ASP.

    Here’s another one:

    # U2B
    # 1429-2090 SLP2 XO
    # 3.3 to 0 dB

    Using the formulae above you get:

    # A =-3.302281146
    # Fc= 1728.18112476673
    # Q = 2.614494894

    The Q factor is very high and as a result the frequency response is very bumpy.

    Am I doing something wrong?

    • Andres,
      This all looks reasonable except that indeed both Q factors are high, resulting in overshoot/resonance. Surely that isn’t intended. So maybe a typo in the original filter parameters? Or a mis-interpretation? Maybe the parameters in the block diagram don’t correspond to the conversion formulae your using? To be honest I’ve never been sure how to interpret/convert the f1,f2 specification of shelving filters. This kind of ambiguity is exactly why I didn’t use parameters from SL’s block diagrams, and just matched his measured response instead. In your case I’d just set Q=0.7, look at the result, and adjust accordingly. Shelving filters aren’t very sensitive to the Q value; you just need to get it in the ballpark.

  6. Dear Richard,

    I’m finalizing my LXmini set-up, controlled by soft LADSPA DSP filter. Your How-To, plugins have been invaluable, with additional soft and plugins from Charlie Laub.

    I would like to define the overall attenuation for my setup, to avoid too much clipping, the way you did here. Is it a complex operation to assess the headroom for my music library when implementing LXmini filters ? Could you explain the basic process and tools?

    Best regards,


    • The basic idea is simple: for every audio file in your collection, calculate how much digital headroom remains (on each channel) after the dsp. Here is the bash script I use to do this:

      rm -f filenames.txt orig.txt woofer.txt mid.txt tweeter.txt
      # process all flacs under /tunes:
      find /tunes -iname "*flac" \
      while read SOURCE; do
      echo working on "$SOURCE"
      # find digital headroom in original file:
      OUTPUT=$(ecasound -i:"$SOURCE" -ev -o null | grep clipping)
      # check if clipping test worked (e.g. in case ecasound gave an error):
      if [ -n "$OUTPUT" ]; then
      echo "$OUTPUT" >> orig.txt
      echo "$SOURCE" >> filenames.txt
      # do lx521 dsp, saving each xover output to a file:
      # (We do 12dB attenuation first to reduce chances of clipping,
      # so you need to subtract 12dB when analyzing the results.
      # This way if the audio *would* have clipped at 0dB gain, we know
      # how much to reduce gain to prevent clipping)
      ecasound -x -q -z:mixmode,sum \
      -a:pre1 -i:"$SOURCE" -pf:lx521_pre1.ecp -eadb:-12 -o:loop,1 \
      -a:pre2,woofer -i:loop,1 \
      -a:mid,tweeter -i:loop,2 \
      -a:pre2 -pf:lx521_pre2.ecp -o loop,2 \
      -a:woofer -pf:lx521_woofer.ecp -o woofer.wav \
      -a:mid -pf:lx521_mid.ecp -o mid.wav \
      -a:tweeter -pf:lx521_tweeter.ecp -o tweeter.wav
      # find digital headroom on outputs:
      ecasound -i woofer.wav -ev -o null | grep clipping >> woofer.txt
      ecasound -i mid.wav -ev -o null | grep clipping >> mid.txt
      ecasound -i tweeter.wav -ev -o null | grep clipping >> tweeter.txt
      # strip everything but the numeric data:
      awk '{print $6}' orig.txt | cut -d. -f1-2 > origlevels.txt
      awk '{print $6}' woofer.txt | cut -d. -f1-2 > wooferlevels.txt
      awk '{print $6}' mid.txt | cut -d. -f1-2 > midlevels.txt
      awk '{print $6}' tweeter.txt | cut -d. -f1-2 > tweeterlevels.txt

      After running this (which can take several hours) the files wooferlevels.txt, midlevels.txt, tweeterlevels.txt will contain, for each audio file, the amplification factor that can be applied without digital clipping on the respective output. You need to divide the results by 4 to account for the 12dB extra attenuation. You’ll want to load these into a spreadsheet or some such to analyze the results.

  7. Dear Richard,

    Thank you very much for sharing. Il will report the results as soon as I will have run the tests. At the moment, I’m fighting with some issues about encapsulation of ecasound in MPD and busy device issues.

    Thank you for sharing all this knowledge and providing all that support.

    Best regards,


    • Hello,

      I implemented your procedure, and it worked like a charm to browse all my songs.This helped to identify the relevant attenuation, which will be around -10 db. As expected, the woofer is the one that drives the thing.

      I had to modify the script slightly to get it work (maybe a typo) to ad a pipe in “find /media/sf_BibliothequeJMF -iname “*flac” | \”.
      Thank you for sharing the method.

      Best regards,


      • Forgot to mention — if you use replaygain on playback then you probably want to account for this in the clipping analysis. On most tracks this allows several dB more gain before you hit clipping, since replaygain usually drops the gain. Just extract the replay gain tag from each file and subtract it from the calculated “max gain without clipping”.

Leave a Reply

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