Introduction: Raspberry Pi Music Server With Built-in Crossover and DSP

About: I'm Just this guy, you know.

Hi 'iblers I want to share this project with you: turning your raspberry pi into a powerful music server that can play high resolution audio files, separate audio signals by frequency (crossover), and provide speaker and room correcting DSP.

This project isn't for the faint of heart, because it does involve some linux kernel hacking and other exciting things. You could also potentially ruin speakers, amplifiers, or your Raspberry Pi. If you are undaunted in your question for perfect sound, come along and let's make some beautiful Pi music together :)

Step 1: Step 1: Gather Your Materials

Step one in this adventure is to get your mise en place.

You'll need:

  1. A Raspberry Pi 2 (you may be able to get by with a Model B, but probably not. Go for a 2)
  2. SD card
  3. A 7.1 Channel Receiver with HDMI input*
    1. If you are making a 2 way speaker, then you only need a 5.1 receiver
    2. You could also an HDMI audio separator, if you have a 6 channel amp laying around
  4. Speakers
    1. I'm using an open baffle design made from drivers I had laying around
      1. 5.25" mids from Parts-Express (won in a raffle)
      2. 1" NHT silk dome tweeter (won in a raffle)
      3. 8" Aura 8000 rpm subwoofers that I bought in 1999
    2. The baffles are some off-cut pieces of wood I had laying around
  5. Cables
    1. 6 speaker cables. It helps if you have 3 different kinds/sizes of wire so you can keep them all separate
    2. HDMI - we'll be piping audio around through HDMI
  6. USB drive - you need to put your righteous tunes somewhere!
  7. Computer - for all the SSHing you're about to do. Macs and Linux machines have SSH clients built in. You probably need Putty for Windows, but I don't know because I haven't used Windows in, like, forever.

*make sure you have set up your 7.1 channel receiver to use ALL channels for ALL frequencies. On mine I had to set all speakers to "large" and sub mode = "off" so that every speaker is getting whatever frequencies we tell it. I also made sure that there was no delay by setting the distance to 0ft for all of the channels.

Step 2: Step 2: Configure the OS

  1. Download a Raspberry Pi image. I used Raspian Lite Jessie 2016-02-09-raspbian-jessie-lite
  2. Install Raspbian
  3. Check the Kernel version
    1. uname -r #make a note of this - you’ll need 4.1.17 or 4.1.18 - these are the only ones that work with my patch AFAIK. You can try it with a different version, YMMV
  4. Housekeeping
    1. Setup Wifi #if you need to
    2. Expand the file system #using raspi-config
    3. Name the server #using raspi-config
      1. I called mine rubymusic.
      2. It makes it easier to find your pi on the network if you have a handy server name
    4. sudo mkdir ~/downloads #optional - makes a directory to keep your downloads handy
    5. sudo mkdir ~/music #optional - makes a directory to put your tunes, brah
    6. sudo reboot
    7. sudo apt-get update

  5. Start installing the dependencies for the DSP/Crossover
    1. sudo apt-get install alsa-base alsa-utils alsa-tools libasound2-plugins
    2. sudo apt-get install ecasound
    3. sudo apt-get install ladspa-sdk
    4. Mount your USB drive somewhere handy #only needed if your music is on a USB stick
      1. sudo mkdir /media/usbstick
      2. sudo mount -t vfat /dev/sda1 /media/usbstick/ #you’ll need to do this after each reboot, we’ll make it auto load later

Step 3: Step 3: Pop Some Kernel

The next step is the part where we hack the kernel! This is what's going to enable us to get 8 channels of sweet, sweet, high resolution audio out of the Pi via HDMI.

If you are starting fresh, this part is easy, if you have hacked your kernel already, you may want to look at 3b and compile your own kernel. If you have a kernel number different than 4.1.*** you'll need to compile your own or update the kernel.

    Download my hacked kernel files (for full details on what’s different, see below)

    wget <a href="https://github.com/jrubinstein/raspiDSP/blob/master/jruby-8-channel-kernel.tar.gz" rel="nofollow"> <a href="https://github.com/jrubinstein/raspiDSP/blob/mast...</a">  https://github.com/jrubinstein/raspiDSP/blob/mast...>> ~/downloads <br>cd ~/downloads

    Unzip them

    tar -xvzf jruby-8-channel-kernel.tar.gz
    
    #make a note of the kernels here:
    ls /lib/modules/

    One of them should match the kernel version from uname -r

    now we remove the ‘sound’ section of the kernel drivers

    sudo rm -r /lib/modules/4.1.17-v7+/kernel/sound/   #kernel version “4.1.17-v7+” must match your kernel version!! 
    
     sudo rm -r /lib/modules/4.1.17+/kernel/sound/   #kernel version “4.1.17+” must match your kernel version!! replace this if that doesn't

    Now we copy in my hacked driver files

    sudo cp -r ~/downloads/jruby-8-channel-kernel/sound/ /lib/modules/4.1.17+/kernel/
    
    sudo cp -r ~/downloads/jruby-8-channel-kernel/sound/sound/ /lib/modules/4.1.17+/kernel/
    
    sudo reboot

    3B If you want to hack your own kernel

    1. Follow the instructions on the Raspberry Pi Documentation for Kernel Building
    2. Once you have the kernel files downloaded (that's the git clone bit) , you need to change the file /sound/arm/bcm2835-pcm.c
      1. where it says MAX_CHANNELS = 2, change that to 8
      2. where it says MAX_RATE = 44100, change that to 96000 or 192000 if you are feeling brave
    3. That’s it, now you can go compile the kernel and install it following the instructions in the kernel building guide

    Step 4: Step 4: Channel Identification

    Now the kernel is hacked with our custom sound configuration, we can start figuring out what channel on our receiver goes where. This step requires a lot of patience. You should also have something to take notes with because you don't want to forget what the channels are later.

      Download the 4 or 6 channel wave file from Richard taylor

      wget <a href="http://faculty.tru.ca/rtaylor/rt-plugins/chan_labels_6.wav" rel="nofollow"> <a href="http://faculty.tru.ca/rtaylor/rt-plugins/chan_lab...</a"> <a href="http://faculty.tru.ca/rtaylor/rt-plugins/chan_lab...</a"> http://faculty.tru.ca/rtaylor/rt-plugins/chan_lab...</a>>> ~/downloads<br>cd ~/downloads

      Make sure your speaker(s) are connected to your receiver .
      Only connect speakers that can deal with a full range audio signal.
      Don’t use your fancy tweeters.
      Make sure your pi is connected to the receiver via HDMI

      Check what your hardware is called

        aplay -L
        

        If you are using HDMI, you should see …

        null
            Discard all samples (playback) or generate zero samples (capture)<br>pulse<br>PulseAudio Sound Server
        default
        filtereq<br>filtercross<br>speaker<br>t-table<br>sysdefault:CARD=ALSA
            bcm2835 ALSA, bcm2835 ALSA
        
           Default Audio Device
        
        dmix:CARD=ALSA,DEV=0
        
         bcm2835 ALSA, bcm2835 ALSA
        
           Direct sample mixing device
        
        dmix:CARD=ALSA,DEV=1
        
           bcm2835 ALSA, bcm2835 IEC958/HDMI
        
            Direct sample mixing device
        
        dsnoop:CARD=ALSA,DEV=0
        
            bcm2835 ALSA, bcm2835 ALSA
        
            Direct sample snooping device
        
        dsnoop:CARD=ALSA,DEV=1
        
            bcm2835 ALSA, bcm2835 IEC958/HDMI
        
           Direct sample snooping device</p><p>hw:CARD=ALSA,DEV=0
        
           bcm2835 ALSA, bcm2835 ALSA
        
           Direct hardware device without any conversions
        
        hw:CARD=ALSA,DEV=1
        
           bcm2835 ALSA, bcm2835 IEC958/HDMI
        
           Direct hardware device without any conversions
        
        plughw:CARD=ALSA,DEV=0
        
            bcm2835 ALSA, bcm2835 ALSA
        
            Hardware device with all software conversions</p><p>plughw:CARD=ALSA,DEV=1
        
            bcm2835 ALSA, bcm2835 IEC958/HDMI
        
            Hardware device with all software conversions

        Play a file using ecasound

        ecasound -z:mixmode,sum          -a:all -tl -i home/pi/downloads/chan_labels_6.wav         -a:woofer -efl:300 -efl:300 -chorder:1,0,0,0,0,0,0,1          -a:woofer -f:16,8,44100 -o:alsahw,0,0 -z:nodb -b:2048

        This command tells ecasound to make a chain called "all" which plays the 6 channel wav file through the filter “woofer” which has a low-pass filter at 300hz (so we can ensure the filters are actually being engaged)

        chorder:1,0,0,0,0,0,0,1 means that only channels 1 and 8 are turned on, using channel 1 as the input

        Move the speaker around and figure out which channel is 1 and which is 8.

        You have to have 2 channels on because it’s a stereo signal and filter (this took me a while to figure out)

        Note which input = which output. This is *very* important.

        Turn on different channels by calling ecasound with different channels turned on, e.g.

        ecasound -z:mixmode,sum -a:all -tl -i home/pi/downloads/chan_labels_6.wav -a:woofer -efl:300 -efl:300 -chorder:1,0,0,0,0,0,1,0 <br>-a:woofer -f:16,8,44100 -o:alsahw,0,0 -z:nodb -b:2048

        Alterative/additional method:
        Use speaker-test - speaker-test is a looping signal of pink noise.

        speaker-test -c 8 -r 48000 -D hw:0,0


        this command calls speaker-test for (-c) 8 channels with a sample rate (-r) of 48khz for device (-D) hw:0,0
        The output looks like:

          Playback device is hw:0,1
          Stream parameters are 48000Hz, S16_LE, 8 channels<br>Using 16 octaves of pink noise
          Rate set to 48000Hz (requested 48000Hz) <br>Buffer size range from 64 to 4096Period size range from 64 to 4096<br>Using max buffer size 4096 Periods = 4<br>was set period_size = 1024<br>was set buffer_size = 4096 <br>0 - Front Left 
          4 - Center <br>1 - Front Right 
          7 - Side Right <br>3 - Rear Right <br>2 - Rear Left <br>6 - Side Left <br>5 - LFE
          ***

          You'll want to pay attention to the numbers here, not the names of the channels. You are going to match up the channel numbers form ALSA with the names on your receiver. Note that ALSA uses 0-indexed channel numbers

          Step 5: Step 5: Crossing Over

          Now you have your channels written down (you wrote them down, right? RIGHT?!) it’s time to start implementing your crossover

          First make sure that you have the right channels going to the right places by running

          ecasound -z:mixmode,sum \        <br>-a:all -i /media/usbstick/04\ -\ South\ Side\ of\ the\ Sky.flac*  \        <br>-a:woofer -efl:300 -efl:300 -chorder:1,2,0,0,0,0,0,0 \         <br>-a:mid -efl:2500 -efl:2500 -efh:300 -efh:300 -chorder:0,0,1,2,0,0,0,0 \        <br>-a:tweeter -efh:2500 -efh:2500 -chorder:0,0,0,0,0,0,1,2\        <br>-a:woofer,mid,tweeter -f:16,8,44100 -o:alsahw,0,0 -z:nodb -b:2048

          Notice that we are now playing a song and we are sending channels 1 and 2 (left and right) to the different channels (1-8 denoted by their position in the chorder list)

          The -efl:300 means a low pass filter at 300 hz, -efh: means a high pass filter

          Once you have confirmed that the those settings work. it’s time to start hacking for realz, haXX0rz

          *Replace the name of this song with a song that you have on your media drive. It doesn't have to be classic Prog Rock, but it helps

          Step 6: Step 6: ALSA Hacking

          Here's where we put our crossover into production:

          First download and install all the various LADSPA plugins.

          sudo apt-get install cmt #computer music toolkit<br>sudo apt-get install swh-plugins #steve harris plugins
          wget <a href="http://faculty.tru.ca/rtaylor/rt-plugins/index.html" rel="nofollow">http://faculty.tru.ca/rtaylor/rt-plugins/index.htm...</a> ~/downloads #richard taylors plugins for xovers <br>wget <a href="http://audio.claub.net/software/LADSPA/ACD-plugins-v1.03.tar" rel="nofollow">http://audio.claub.net/software/LADSPA/ACD-plugins...</a> ~/downloads

          full instructions on the RT plugins come from Richard Taylor http://faculty.tru.ca/rtaylor/rt-plugins/index.htm...
          Charlie Laub also has instructions in the readme file in the download. Read them. http://audio.claub.net/ACD-plugins.html

          Alsa hacking with LASPA pluginsDownload all the LADSPA pluginsinstall the RT plugins Install the ACDf plugins (these are what we’ll be using, but you may want to use others from the toolkit)Configure ALSAUse ACDf worksheets to figure out the proper configurationapply that to your also configuration

          I installed the the RT plugins and the ACD plugins to the main LADSPA plugin directory, not the local directory. To accomplish this, you'll need to open up the make file and change the line that says "/usr/local/lib/ladspa " to "/usr/local/ladspa"

          <p>cd ~/downloads<br>tar -xvf ACD-plugins-v1.03.tar<br>cd ACD-plugins-v1.03<br>sudo nano MAKEFILE # now go edit the install directory from "/usr/local/lib/ladspa/" to "/usr/lib/ladspa/"<br>make<br>sudo make install</p><p>tar xfz rt-plugins-0.0.4.tar.gz <br>cd rt-plugins-0.0.4<br>cd build<br>sudo nano MAKEFILE # now go edit the install directory from "/usr/local/lib/ladspa/" to "/usr/lib/ladspa/"<br>cmake<br>sudo make install <br></p>

          Now it's time to put these into action by configuring ALSA. I use the approach of directly hosting the LADSPA plugins within ALSA, others use Ecasound and pipe audio into ecasound and back out to ALSA. Richard Taylor uses the ecasound approach in his excellent tutorial. I don't, because... I guess I'm just hard-headed and don't want the extra step in the audio chain (also Ecasound requires resampling, which I'm not opposed to on moral grounds, but it does add to the processor load)

          open ~/.asoundrc , this is your "local" user sound configuration. We'll start our hacking here before we stash the file in a permanent (and root) spot.

          Download the example asoundrc file from my github

          wget <a href="https://github.com/jrubinstein/raspiDSP/blob/master/alsa%20configuration%20asoundrc-experimental2.cpp" rel="nofollow">https://github.com/jrubinstein/raspiDSP/blob/maste...</a> ~/dowloads
          sudo nano ~/.asoundrc #paste in the contents of the asoundrc file you downloaded from me, save and exit
          <p>alsactl kill rescan #tells alsa to reread the conf file</p>

          now lets try speaker test again

          speaker-test -c2 -D default

          you should hear highs coming from the tweeter channels, mids coming from the mid channels, and woofs coming from the woofers. The astute amongst you will notice that the command above only has 2 channels now, that's because ALSA and LADSPA should be chopping up the frequencies for us, so we only want to input 2 channels.

          Step 7: Step 7: Wiring

          Hopefully this part is self explanatory: wire up the right speakers to the right channels (and the left speakers to the left ones!)
          A couple of protips:

          Get a few different types of speaker wire to help you sort out your mids from highs Get a dymo labeller (or similar)

          After it's all wired up,

          speaker-test -c2 -D default

          Hopefully you now have tweets tweeting, mids midding, and woofers woofing. If not, you can either arrange wires until you do or tweak the t.table in the .asoundrc file until you do.

          Step 8: Step 8a: Understanding the .asoundrc File

          asoundrc is the local configuration of ALSA for *your user* so if you run sudo speaker-test, you'll get different results than if you run just speaker-test.

          The configuration file has a few distinct sections that each tell ALSA which LADSPA plugins to run and which channels to send the output to.

          Here is a super-commented version of the .asoundrc file shared on my github

          <p>#asound rc new version jrubinstein - experimental with charlies plugin<br>pcm.!default {  #this is the device name  </p><p>#which is why we call speaker-test -D default .. this makes this config "default"</p><p>     type plug #it's a plugin
               slave.pcm filtereq  #send the output to the slave "device" plugin
          }
          ctl.!default { # i don't actually know what this means, but it seems to tell alsa we want to send the audio that <br>#isn't default to hardware device 0<br>    type hw<br>     card 0</p><p>}
          pcm.filtereq { #the name of the next device in the chain
               type ladspa #type - ladspa plugins
               slave.pcm filtercross #where does the output go
               path "/usr/lib/ladspa" #where do these plugins live
               channels 8  #how many channels. Even if you are only using 2 (left and right) at this stage, this stays at 8
               plugins # the list of plugins
               {</p><p>        0 {
                         label ACDf  #call ACDf for Charlies Active Crossover Design plugins
                         policy none #dunno what policy none means. don't change it
                         input.bindings.0 "Input" #channel 0 (left) is the input
                         output.bindings.0 "Output" #channel0 (left) is the output
                         input { controls [28 1 6 120 1 1 1] } #2nd order bass shelf boosting 6db at 120hz <br>#calls filter type 28, with normal polarity, 6db boost, at 120hz, Qp = 1, Fz (not used) =1, Qz  (not used) =1</p><p>
                  }
                  1{
                         label ACDf
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.0 "Output"
                         input { controls [26 1 -6 164 4 1 1] } #parametric digital eq cutting 6db at 164hz with q of 4
                  }
                  2{
                         label ACDf
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.0 "Output"
                         input { controls [26 1 -6 250 4 1 1] }#parametric digital eq cutting 6db at 164hz with q of 4
                  }
                  3{
                         label ACDf
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.0 "Output"
                         input { controls [26 1 6 193 5 1 1] }#parametric digital eq boosting 6db at 193 with q of 5
                  }
                  4{
                         label ACDf
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [28 1 6 120 1 1 1] }
                  }
                  5{
                         label ACDf
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [26 1 -6 164 4 1 1] }
                  }
                  6{
                         label ACDf
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [26 1 -6 250 4 1 1] }
                  }
                  7{
                         label ACDf
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [26 1 6 193 5 1 1] }
                  }
                  8{
                         label ACDf 
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.0 "Output"
                         input { controls [22 1 0 40 1 1 1] } #2nd order highpass at 40 hz to act as subsonic filter
                    }
                  9{
                         label ACDf 
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.0 "Output"
                         input { controls [22 1 0 40 1 1 1] } #2nd order highpass at 40 hz
                    }
                  10{
                         label ACDf 
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [22 1 0 40 1 1 1] } #2nd order highpass at 40 hz to act as subsonic filter
                    }
                  11{
                         label ACDf 
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.1 "Output"
                         input { controls [22 1 0 40 1 1 1] } #2nd order highpass at 40 hz
                    }
              }
          }
          pcm.filtercross { #the name of the next device in our chain
               type ladspa
               slave.pcm speaker #sends output to 'speaker'
               path "/usr/lib/ladspa"
               channels 8
               plugins
               {
                    0 {
                         label ACDf #lowpass for woofer output to channel2
                         policy none
                         input.bindings.0 "Input" 
                         output.bindings.2 "Output"
                         input { controls [21 -1 -10 300 0.5 1 1] }   # [filter type polarity dbgain frequency q]
                    }
                  1 {
                         label ACDf #lowpass for woofer output to channel3
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.3 "Output"
                         input { controls [21 -1 -10 300 0.5 1 1] } #2nd order lowpass at 300hz
                    }
                  2 {
                         label ACDf #highpass for tweeter output to channel4
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.4 "Output"
                         input { controls [22 -1 -23 3000 1 1 1] } #2nd order highpass at 3000 hz
                    }
                  3 {
                         label ACDf #highpass for tweeter output to channel4  filter 2
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.4 "Output"
                         input { controls [22 1 0 3000 1 1 1] } #2nd order highpass at 3000 hz
                    }
                  4 {
                         label ACDf #highpass for tweeter output to channel4
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.5 "Output"
                         input { controls [22 -1 -23 3000 1 1 1] } #2nd order highpass at 3000 hz
                    }
                  5 {
                         label ACDf #highpass for tweeter output to channel4  filter 2
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.5 "Output"
                         input { controls [22 1 0 3000 1 1 1] } #2nd order highpass at 3000 hz
                    }
                  6 {
                         label ACDf #lowpass1 for mid output to channel0
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.6 "Output"
                         input { controls [21 1 -14 3000 0.707 1 1] } # 2nd order lowpass at 3000 hz -4db cut
                    }
                  7 {
                         label ACDf #lowpass2 for mid output to channel0
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.6 "Output"
                         input { controls [21 1 0 3000 0.707 1 1] } # 2nd order lowpass at 3000 hz 
                    } 
                  8 {
                         label ACDf #highpass for mid output to channel0
                         policy none
                         input.bindings.0 "Input"
                         output.bindings.6 "Output"
                         input { controls [22 1 0 300 0.5 1 1] } # 2nd order highpass at 300 hz
                    }  
                  9 {
                         label ACDf #lowpass1 for mid output to channel1
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.7 "Output"
                         input { controls [21 1 -14 3000 0.707 1 1] } # 2nd order lowpass at 3000 hz -4db cut
                    }
                  10 {
                         label ACDf #lowpass2 for mid output to channel1 
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.7 "Output"
                         input { controls [21 1 0 3000 0.707 1 1] } # 2nd order lowpass at 3000 hz
                    }  
                  11 {
                         label ACDf #highpass for mid output to channel1
                         policy none
                         input.bindings.1 "Input"
                         output.bindings.7 "Output"
                         input { controls [22 1 0 300 0.5 1 1] } # 2nd order highpass at 300 hz 
                    }          
               }
          }
          pcm.speaker { 
              type plug
              slave {
               pcm "t-table"  
               channels 8
               rate "unchanged" #rate unchanged means don't resample the audio signal . <br>#if you have a 44.1 DAC, change this
              }
          }
          pcm.t-table  { #t-table isn't statistics it's a channel table
              type route
              slave {
               pcm "hw:0,0" #we output sound from here to hw:0,0 which is the address of the HDMI output
               channels 8
              }
              ttable {
                0.0   0  # use this channel map to route signals to the right place<br># your channel map may be different, make sure it works for you based on the channel mappings from earlier
                1.1   0  # channel in.channel out  on/off
                2.0   1  #left bass this gives me the low filter for left woofer onto channel 0 = front left
                3.7   1  #right bass  = SBR
                4.6   1  #left tweeter = SBL 
                5.5   1  #right tweeter = Surr Right 
                6.4   1  #left mid = surr left 
                7.3   1  #right mid = center 
              }
          }</p><p>pcm.plughw.slave.rate = "unchanged";</p>

          Step 9: Step 8b: Setting Up Your Own Filters for Your System

          Up until now, you've be using my filters, which are tuned for my room and system. You probably want to get things tuned for *your* system. Here's what to do.

          Download Charlie Laub's excellent ACD spreadsheets and documentation from http://audio.claub.net/software/ACD/ACD.html

          Read the documentation. There's a technical manual and a tutorial, read them both. You will learn more about filter design than you ever could from me.... but basically what you want to do is

          Measure your system, make filters to adapt your system to your room, measure your system... repeat

          Even if you choose to use the RT plugins, the ACD software is very helpful for thinking through system design and getting you close.

          Step 10: Step 9: Measure Twice, Cut (or Boost) Once

          To measure your system response you'll need:

          1. Computer or phone to use as a measurement platform
          2. MS excel or open office calc
          3. Charlie's ACD-L spreadsheets (L is for LADSPA, that's good enough for me)
          4. Calibrated measurement microphone (Parts-Express has some on sale, or you can go a little fancier)
          5. 3.5mm to RCA cable
          6. Measurement Software ( I used Room EQ Wizard, which is a bit crashy, but the price is right and it works)

          Connect the the 3.5mm plug to your lappy, then connect one rca plug to an analog input on your receiver. match the analog input to the channel you want to measure, e.g. Surround Back Left == Left Tweeter. Once so connected, turn the volume down, turn on some pink noise and slowly ramp the volume up. If you are measuring tweeters, I suggest running your measurement frequency sweeps using 1khz and up signals, or if you want to crossover at 1khz, a bit below that. I ran my frequency sweeps full range through my tweeters and blew one of them :( don't be like me, practice safe tweeting. Woofer and mid are next. Save the files as text, copy/paste into the appropriate workbook for ACD-L.

          Start tweaking your signals around in ACD-L to set your crossovers up. Get things crossed over, then start with EQ and phase adjustments. You can see that I did mine in 2 steps - global EQ and crossovers.

          After you have edited the asoundrc file to your liking, you can run speaker-test again, using REW's "RTA" feature to look at the pink noise frequency response. Tweak as desired for your personal sound preference, too. Have fun with it :)

          One thing to note: ACD-L will happily let you boost signals in a way that doesn't make sense. If you boost the overall signal over 0db, you'll get distortion, so you'll need to cut signals somewhere else to remove this distortion. That's why I have 12-ish DB of boost at the low end, but I reduce the woofer signal by -13db. I could have just set my levels so there was an "all pass" filter that reduced the sound levels by 12 db. Whatever makes sense for you. I also added a high-pass filter at 40hz because my open baffle speakers were literally flapping in the breeze trying to reproduce 20hz signals, which is just wasted energy. If you have some sealed box woofers, you can remove this and let the beat ........ drop.

          Step 11: Step 10: Kicking Out the Jams

          So, everything is set up to your liking and you can now listen to pink noise with the flattest frequency response of any stereo system ever. That's exciting, but it's not going to get booties on the dance floor, now is it?! You, my friend, need music

          To get music out of your super sweet raspberry pi you need a music player. I used Squeezelite and Logitech Music Server, as well as some airplay goodness from shairplay-sync. There are other players like MPD, but Squeezelite works for me, so I'm going with that.

          If you don't have a logitech music server sitting on some NAS or something, you'll want to put that on your Pi.

          Look at Gerrelt's super awesome tutorial for how to install LMS

          If you just need Squeezelite here's Gerrelt's tutorial for that

          Shairport-sync can be found on Mike's Github

          To install all 3:

          <p># install some libs<br>sudo apt-get install -y libsox-fmt-all libflac-dev libfaad2 libmad0
          # get the latest nightly build (from downloads.slimdevices.com):
          wget -O logitechmediaserver_all.deb $(wget -q -O - "http://www.mysqueezebox.com/update/?version=7.9.0&revision=1&geturl=1&os=deb")
          sudo dpkg -i logitechmediaserver_all.deb
          # fix permissions:<br>sudo chown -R squeezeboxserver:nogroup /usr/share/squeezeboxserver/# get the latest nightly build (from downloads.slimdevices.com):</p><p>###</p><p>sudo mkdir squeezelite<br>chown pi squeezelite/
             sudo chown pi squeezelite/
             cd squeezelite/
             wget -O squeezelite-armv6hf <a href="http://ralph_irving.users.sourceforge.net/pico/squeezelite-armv6hf-noffmpeg" rel="nofollow">  http://ralph_irving.users.sourceforge.net/pico/sq...>
             sudo mv squeezelite-armv6hf /usr/bin
             sudo chmod a+x /usr/bin/squeezelite-armv6hf
             sudo /usr/bin/squeezelite-armv6hf -l</p><p>
          ###</p><p>cd ~/downloads<br>   git clone <a href="https://github.com/mikebrady/shairport-sync.git" rel="nofollow">  http://ralph_irving.users.sourceforge.net/pico/sq...>
             cd shairport-sync/
             autoreconf -i -f
             sudo apt-get install autoconf libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev
             apt-get install avahi-daemon libavahi-client-dev
             sudo apt-get install avahi-daemon libavahi-client-dev
             sudo apt-get install libssl-dev
             autoreconf -i -f
             ./configure --with-alsa --with-avahi --with-ssl=openssl --with-metadata --with-systemd
             make
             getent group shairport-sync &>/dev/null || sudo groupadd -r shairport-sync >/dev/null
             getent passwd shairport-sync &> /dev/null || sudo useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync >/dev/null
             sudo make install
             sudo systemctl enable shairport-sync
             sudo reboot</p>

          Configuring Squeezelite

          sudo nano /usr/local/bin/squeezelite_settings.sh 
          <p>sudo /etc/init.d/squeezelite force-reload<br>sudo /etc/init.d/squeezelite stop<br>sudo /etc/init.d/squeezelite play<br>sudo /etc/init.d/squeezelite stop</p>
          <p># ===========================v1.5==============================<br>
          #              CHANGE THESE VALUES IF NEEDED</p><p># The name for the squeezelite player (default the hostname</p><p># will be used):</p><p>#SL_NAME="Framboos"</p><p>#        Note: "Framboos" is Dutch for Raspberry... :-)</p><p># ----- SOUNDCARD -----</p><p># Set the soundcard</p><p>#SL_SOUNDCARD="default:CARD=ALSA"</p><p>#SL_SOUNDCARD="sysdefault:CARD=ALSA"</p><p>#</p><p># For Logilink USB soundcard UA0053, use:</p><p>#SL_SOUNDCARD="front:CARD=Set,DEV=0"</p><p>#</p><p># For Behringer UCA 202 USB soundcard, use:</p><p>#SL_SOUNDCARD="front:CARD=CODEC,DEV=0"</p><p>#</p><p># For alsaequal, use:</p><p>#SL_SOUNDCARD="equal"</p><p># ----- MAC ADDRESS -----</p><p># Uncomment the next line (remove hash) if you want to change the mac address (-m option):</p><p>#SL_MAC_ADDRESS="00:00:00:00:00:01"</p><p>#        Note: when left commented squeezelite will use the mac address of your ethernet card or </p><p>#              wifi adapter, which is what you want. </p><p>#              If you change it to something different, it will give problems if you use mysqueezebox.com .</p><p># ----- SERVER IP ADDRESS -----</p><p># Uncomment the next line (remove hash) if you want to point squeezelite </p><p># at the IP address of your squeezebox server (-s option). And change the IP address of course..</p><p>#SB_SERVER_IP="192.168.0.100"</p><p>#        Note: if this is not set, Squeezelite will use auto discovery to find </p><p>#              the LMS server, which works fine too.</p><p>#</p><p># For the standalone LMS server tutorial, use:</p><p>SB_SERVER_IP="127.0.0.1"</p><p># ----- AUTO PLAY -----</p><p># Uncomment the next line if you want squeezelite to start playing on startup. BE AWARE: If you use this, you</p><p># should also uncomment and fill-in SB_SERVER_IP (see above). Otherwise this will not work.</p><p>#SL_AUTO_PLAY="Yes"</p><p># ----- MISC SETTINGS -----</p><p># Uncomment the next line (remove hash) if you want to set ALSA parameters (-a option, set to buffer size 80):</p><p>#       -a 
          </p><p>#              Specify parameters used when opening  an  audio  output  device.</p><p>#              For  ALSA,  the  format <strong>:</strong>:: is used where  is the</p><p>#              buffer time in milliseconds (values less than 500)  or  size  in</p><p>#              bytes  (default 40ms);  is the period count (values less than</p><p>#              50) or size in bytes (default 4  periods);    is  the  sample</p><p>#              format  (possible values: 16, 24, 24_3 or 32);  is whether to</p><p>#              use mmap (possible values: 0 or 1).  For PortAudio, the value is</p><p>#              simply  the  target  latency in milliseconds. When the output is</p><p>#              sent to standard output, the value can be 16, 24  or  32,  which</p><p>#              denotes the sample size in bits.</p><p>#</p><p>#       -b :</p><p>#              Specify internal stream and output buffer sizes in kilobytes.</p><p>SL_ALSA_PARAMS="80:4::0"</p><p># Uncomment the next TWO lines to turn on logging (-f and -d option):</p><p>#SL_LOGFILE="/var/log/squeezelite.log"</p><p>#SL_LOGLEVEL="all=debug"</p><p># Uncomment the next line if you want to start the squeezelite daemon with a specific user.</p><p>SL_USER="pi"</p><p># Uncomment the next line if you want to start the squeezelite daemon with a specific working directory</p><p>#SL_WORKING_DIR="/home/pi"</p><p># Uncomment and change the next line if you want to use a different squeezelite version.</p><p>#SL_DOWNLOAD_URL="url to squeezelite executable"</p><p>#</p><p># Default:</p><p>#SL_DOWNLOAD_URL="http://ralph_irving.users.sourceforge.net/pico/squeezelite-armv6hf-noffmpeg"</p><p>#    This is a small version about 1 MB which despite its small size can play pcm, (wav/aiff), </p><p>#    flac, mp3, ogg and aac without any extra sound codec. So you don't need the flac.tcz libvorbis.tcz etc.</p><p>#</p><p># Alternative:</p><p>#SL_DOWNLOAD_URL="http://ralph_irving.users.sourceforge.net/pico/squeezelite-armv6hf-ffmpeg"</p><p>#    A larger version with build in ffmpeg. It is about 12 MB in size and can play all kind of formats. </p><p>#    Again you don't need any audio codec.tcz packages at all.</p><p>#</p><p># Older version:</p><p>#SL_DOWNLOAD_URL="http://squeezelite-downloads.googlecode.com/git/squeezelite-armv6hf"</p><p># If you want to use different squeezelite options, not set by this script, use the next line:</p><p>#SL_ADDITIONAL_OPTIONS=""</p><p># =========================================================</p>

          Configuring Shairpoirt-Sync

          sudo nano /etc/shairport-sync.conf
          
          <p>// Sample Configuration File for Shairport Sync<br>
          // Commented out settings are generally the defaults, except where noted.</p><p>// General Settings</p><p>general =</p><p>{</p><p>//	name = "Shairport Sync Player"; // This is the name the service will advertise to iTunes. The default is "Shairport Sync on "</p><p>//	password = "secret"; // leave this commented out if you don't want to require a password</p><p>	interpolation = "soxr"; // aka "stuffing". Default is "basic", alternative is "soxr". Use "soxr" only if you have a reasonably fast processor.</p><p>	output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one.</p><p>//	mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one.</p><p>//	port = 5000; // Listen for service requests on this port</p><p>// 	udp_port_base = 6001; // start allocating UDP ports from this port number when needed</p><p>//	udp_port_range = 100; // look for free ports in this number of places, starting at the UDP port base (only three are needed).</p><p>//	statistics = "no"; // set to "yes" to print statistics in the log</p><p>//	drift = 88; // allow this number of frames of drift away from exact synchronisation before attempting to correct it</p><p>	resync_threshold = 0; // a synchronisation error greater than this will cause resynchronisation; 0 disables it</p><p>//	log_verbosity = 0; // "0" means no debug verbosity, "3" is most verbose.</p><p>  ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.</p><p>//  volume_range_db = 60 ; // use this to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range.</p><p>};</p><p>// How to deal with metadata, including artwork</p><p>metadata =</p><p>{</p><p>//	enabled = "no"; // et to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe</p><p>//	include_cover_art = "no"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes".</p><p>//	pipe_name = "/tmp/shairport-sync-metadata";</p><p>};</p><p>// Advanced parameters for controlling how a Shairport Sync runs</p><p>sessioncontrol = </p><p>{</p><p>//	run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. It it's a script, include the #!... stuff on the first line</p><p>//	run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. It it's a script, include the #!... stuff on the first line</p><p>//	wait_for_completion = "no"; // set to "yes" to get Shairport Sync to wait until the "run_this..." applications have terminated before continuing</p><p>//	allow_session_interruption = "no"; // set to "yes" to allow another device to interrupt Shairport Sync while it's playing from an existing audio source</p><p>//	session_timeout = 120; // wait for this number of seconds after a source disappears before terminating the session and becoming available again.</p><p>};</p><p>// Back End Settings</p><p>// These are parameters for the "alsa" audio back end, the only back end that supports synchronised audio.</p><p>alsa =</p><p>{</p><p>  output_device = "default"; // the name of the alsa output device. Use "alsamixer" or "aplay" to find out the names of devices, mixers, etc.</p><p>  mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software.</p><p>  mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this.</p><p>//  audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.</p><p>  audio_backend_buffer_desired_length = 200; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying.</p><p>};</p><p>// These are parameters for the "pipe" audio back end, a back end that directs raw CD-style audio output to a pipe. No interpolation is done.</p><p>pipe =</p><p>{</p><p>//  name = "/path/to/pipe"; // there is no default pipe name for the output</p><p>//  audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.</p><p>//  audio_backend_buffer_desired_length = 44100; // Having started to send audio at the right time, send all subsequent audio this many frames ahead of time, creating a buffer this size.</p><p>};</p><p>// These are parameters for the "stdout" audio back end, a back end that directs raw CD-style audio output to stdout. No interpolation is done.</p><p>stdout =</p><p>{</p><p>//  audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.</p><p>//  audio_backend_buffer_desired_length = 44100; // Having started to send audio at the right time, send all subsequent audio this many frames ahead of time, creating a buffer this size.</p><p>};</p><p>// These are parameters for the "ao" audio back end. No interpolation is done.</p><p>ao =</p><p>{</p><p>//  audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.</p><p>//  audio_backend_buffer_desired_length = 44100; // Having started to send audio at the right time, send all subsequent audio this many frames ahead of time, creating a buffer this size.</p><p>};</p><p>// Static latency settings are deprecated and the settings have been removed.</p>
          
          
          

          If everything is working, you can move the ~/.asoundrc file to /etc/asound.conf

          <p>sudo mv ~/.asoundrc ~/.asoundrc-1 #make a quick backup<br>sudo mv ~/.asoundrc /etc/asound.conf #move to asound.conf to enable for all user</p>

          Step 12: Step 11: Rock Out!

          That's it, you should be able to play some music stored on your pi or stream to the pi from your iDevice.

          Go to yourservernamehere.local:9000 and you should see squeezelite. Now, just tell it to import your tunes by going to settings>basic settings>media folders , then recan

          Now you're ready to rock out!!!

          It's time to dance like no one is watching!

          References and Acknowledgements

          First, I have to thank my wife for letting me have lots of wires running across the house and speakers all over the damn place.

          Second, I have to thank Charlie Laub, Richard Taylor, DIY Audio Member Fracolargo, DIY-HIFI-Forum.edu member The Alchemist, Mike Brady, Gerrelt, And everyone who makes the Raspberry Pi possible. Thank you.

          References

          In no order whatsoever

          http://www.diyaudio.com/forums/pc-based/274331-lad...

          https://github.com/francolargo/BBB-audio/blob/mast...

          https://wiki.archlinux.org/index.php/Talk:Advanced...

          http://src.gnu-darwin.org/ports/audio/ecasound/wor...

          http://alsa.opensrc.org/Ladspa_(plugin)

          http://plugin.org.uk/ladspa-swh/docs/ladspa-swh.ht...

          https://www.raspberrypi.org/forums/viewtopic.php?f...

          http://forums.slimdevices.com/showthread.php?97046...

          https://code.google.com/archive/p/squeezelite/

          https://www.raspberrypi.org/forums/viewtopic.php?f...

          http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_...

          http://manpages.ubuntu.com/manpages/trusty/man1/sq...

          http://www.alsa-project.org/main/index.php/Asoundr...

          https://github.com/raspberrypi/linux/pull/1257

          https://github.com/bmc0/dsp/wiki/System-Wide-DSP-G...

          http://crunchbang.org/forums/viewtopic.php?pid=182...

          http://nairobi-embedded.org/alsa_ladspa_example_us...

          http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html