├── fir └── README.md ├── config ├── brutefir.service ├── shairport-sync.conf └── brutefir_config ├── LICENSE ├── README.md └── install.sh /fir/README.md: -------------------------------------------------------------------------------- 1 | You need to put two files in this folder: 2 | 3 | * left.wav -> FIR Impulse response file for left channel correction 4 | * right.wav -> FIR Impulse response file for right channel correction 5 | 6 | Both files need to be mono file in 32/44.1 wav format, such as exported from 7 | Room EQ Wizard's "Export Filters Impulse Response as WAV" 8 | 9 | -------------------------------------------------------------------------------- /config/brutefir.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=BruteFIR 3 | After=multi-user.target 4 | 5 | [Service] 6 | Type=forking 7 | ExecStart=/usr/bin/brutefir -daemon -quiet -nodefault /etc/brutefir_config 8 | Restart=always 9 | User=shairport-sync 10 | Group=shairport-sync 11 | LimitRTPRIO=99 12 | LimitMEMLOCK=infinity 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Arve 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/shairport-sync.conf: -------------------------------------------------------------------------------- 1 | // Sample Configuration File for Shairport Sync 2 | // Commented out settings are generally the defaults, except where noted. 3 | 4 | // General Settings 5 | general = 6 | { 7 | name="%H"; 8 | output_backend = "alsa"; 9 | alac_decoder = "hammerton"; 10 | interpolation = "basic"; 11 | 12 | }; 13 | 14 | dsp = 15 | { 16 | convolution = "no"; 17 | loudness = "no"; 18 | }; 19 | 20 | // How to deal with metadata, including artwork 21 | metadata = 22 | { 23 | enabled = "no"; 24 | }; 25 | 26 | // Advanced parameters for controlling how a Shairport Sync runs 27 | sessioncontrol = 28 | { 29 | allow_session_interruption = "no"; 30 | session_timeout = 30; 31 | }; 32 | 33 | // Back End Settings 34 | 35 | // These are parameters for the "alsa" audio back end, the only back end that supports synchronised audio 36 | alsa = 37 | { 38 | use_mmap_if_available = "no"; 39 | output_device = "hw:Loopback,1"; 40 | output_rate = 44100; 41 | output_format = "S16"; 42 | audio_backend_latency_offset_in_seconds = -0.197; 43 | audio_backend_buffer_desired_length_in_seconds = 0.35; 44 | disable_synchronization = "no"; 45 | // period_size = 512; // Use this optional advanced setting to set the alsa period size near to this value 46 | // buffer_size = 8192; // Use this optional advanced setting to set the alsa buffer size near to this value 47 | mute_using_playback_switch = "no"; // Use this optional advanced setting to control whether the snd_mixer_selem_set_playback_switch_all call can be used for muting. Default is yes. 48 | mixer_control_name = "PCM"; 49 | mixer_device = "hw:OUTPUT_DAC"; 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /config/brutefir_config: -------------------------------------------------------------------------------- 1 | float_bits: 32; 2 | filter_length: 4096,16; # length of filters 3 | sampling_rate: 44100; 4 | overflow_warnings: false; 5 | show_progress: false; 6 | max_dither_table_size: 0; 7 | allow_poll_mode: false; 8 | modules_path: "."; # extra path where to find BruteFIR modules 9 | monitor_rate: true; # monitor sample rate 10 | powersave: true; # pause filtering when input is zero 11 | lock_memory: true; # try to lock memory if realtime prio is set 12 | sdf_length: -1; # subsample filter half length in samples 13 | safety_limit: 8.0; # if non-zero max dB in output before aborting 14 | convolver_config: "/home/shairport-sync/.brutefir_convolver"; # location of convolver config file 15 | # config_file: "/home/shairport-sync/.brutefir_config"; # standard location of main config file 16 | 17 | 18 | coeff "left_convolver" { 19 | filename: "/home/shairport-sync/left.raw"; 20 | format: "S32_LE"; 21 | blocks: -1; 22 | skip: 0; 23 | shared_mem: false; 24 | }; 25 | 26 | # Right channel 27 | coeff "right_convolver" { 28 | filename: "/home/shairport-sync/right.raw"; 29 | format: "S32_LE"; 30 | blocks: -1; 31 | skip: 0; 32 | shared_mem: false; 33 | }; 34 | 35 | 36 | input "left_in", "right_in" { 37 | device: "alsa" { device: "hw:Loopback,0"; ignore_xrun: true; }; 38 | sample: "S16_LE"; 39 | channels: 2/0,1; 40 | delay: 0,0; 41 | maxdelay: -1; 42 | mute: false, false; 43 | }; 44 | 45 | 46 | # Likewise, configure where BruteFIR sends its output 47 | output "left_out", "right_out" { 48 | device: "alsa" { device: "hw:OUTPUT_DAC"; ignore_xrun: true; }; 49 | sample: "S24_LE"; 50 | channels: 2/0,1; 51 | delay: 0,0; 52 | maxdelay: -1; 53 | subdelay: 0,0; 54 | mute: false, false; 55 | dither: false; 56 | }; 57 | 58 | 59 | filter "drc_l" { 60 | process: -1; 61 | from_inputs: "left_in"; 62 | to_outputs: "left_out"/8.0; 63 | coeff: "left_convolver"; 64 | crossfade: false; 65 | delay: 0; 66 | }; 67 | 68 | filter "drc_r" { 69 | process: -1; 70 | from_inputs: "right_in"; 71 | to_outputs: "right_out"/8.0; 72 | coeff: "right_convolver"; 73 | crossfade: false; 74 | delay: 0; 75 | }; 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # brutepi 2 | 3 | Automating installation and configuration of BruteFIR and shairport-sync for Raspberry Pi 4 | 5 | ## Introduction 6 | 7 | This script is used to ease the installation of shairport-sync and BruteFIR 8 | 9 | ## Prerequisites 10 | 11 | Before you start, it's assumed that you have some familiarity with all of the above: 12 | 13 | 1. Familiar with installing [Raspbian Jessie Lite](https://www.raspberrypi.org/downloads/raspbian/) on a Raspberry Pi of your choice - [Instructions here](https://www.raspberrypi.org/documentation/installation/installing-images/README.md) 14 | 2. Capable of setting up [WiFi via the command line](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md) 15 | 3. Somewhat familiar with the bash shell. 16 | 4. Room correction using software like [Room EQ Wizard](https://www.roomeqwizard.com/). 17 | 18 | ## First steps 19 | 20 | ## Exporting and converting impulse responses 21 | 22 | After you have created your correction filters in Room EQ Wizard, you need to export them using: 23 | 24 | File -> Export -> Export Filters Impulse Response as WAV 25 | 26 | When exporting export each channel individually using the following settings: 27 | 28 | 1. Mono 29 | 2. 32-bit 30 | 3. Check "Normalize samples to peak value" 31 | 4. Save the filter for the left channel as "left-fir.wav", and the right channel as "right-fir.wav" 32 | 33 | Save the files in the `impulses` directory. 34 | 35 | ## Preparing the Raspberry Pi installation before first boot 36 | 37 | Before continuing: 38 | 39 | 1. [Download Raspbian Jessie Lite](https://www.raspberrypi.org/downloads/raspbian/) 40 | 2. [Install the downloaded Raspbian](https://www.raspberrypi.org/documentation/installation/installing-images/README.md)) 41 | 42 | ### Enable SSH 43 | 44 | Since this Raspberry Pi installation will run as a headless installation (no keyboard or display connected), we'll need to enable SSH. Do this by creating an empty text file named 'ssh' on te 45 | 46 | ### Copy scripts, configuration files and impulse responses 47 | 48 | Before booting the Raspberry Pi for the first time, we'll need to prepare the image by copying the directory with this guide to the image, including the saved impulse responses to the prepared image. 49 | 50 | ## Configuring Raspbian 51 | 52 | It's now time to boot the Raspberry Pi for the first time. 53 | 54 | 1. Connect network cable, and any other peripherals, such as your DAC. 55 | 2. Connect the Raspberry Pi to power 56 | 3. After booting, ssh into the machine: `ssh pi@raspberrypi.local` - log in using the password 'raspberry' 57 | 58 | ### Update the Raspberry 59 | 60 | sudo apt-get update && sudo apt-get dist-upgrade 61 | 62 | This may take a long time. Just be patient and enjoy a cup of coffee 63 | 64 | ### Change the password 65 | 66 | sudo raspi-config 67 | 68 | Select option 1 "Change User Password Change password for the default user (pi)", and pick a secure password. While your Pi won't be visible to the internet at large, this is still good practice. 69 | 70 | ### (Optionally) change the hostname 71 | 72 | While still in the raspi-config application, you may want to change the hostname to something more recognizable, using item 2: "Hostname Set the visible name for this Pi on a network" option 73 | 74 | ### Set overclocking options 75 | 76 | If you are running an older Raspberry Pi, such as the Model B or Model B+, you may want to overclock the Pi slightly. Choose "Overclocking" (but take note of the warnings, as the author of this document will not be responsible for you damaging your Pi). Using the option "Moderate" is sufficient. 77 | 78 | If your Pi is a Pi 2, Pi 3 or Pi Zero, you will not need to overclock. 79 | 80 | ### Reboot and log back in 81 | 82 | At this stage, you may want to reboot, so the hostname option takes effect. 83 | 84 | ### Installing, the easy way 85 | 86 | SSH back into your Raspberry Pi, and execute the following commands: 87 | 88 | sudo -s 89 | mv /boot/brutepi . 90 | cd brutepi 91 | chmod +x install.sh 92 | ./install.sh 93 | 94 | The installation script is interactive, and will ask you a few questions before commencing installation. Follow on-screen instructions, and pay attention during installation. 95 | 96 | After the installation, you can remove the repository: 97 | 98 | cd /home/pi 99 | sudo rm -rf brutepi 100 | 101 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | select_dac () 6 | { 7 | echo "Select your DAC:" 8 | echo "0) Abort installation of BrutePi" 9 | # echo "$@" 10 | select option; do # in "$@" is the default 11 | if [ "$REPLY" -eq "0" ]; 12 | then 13 | echo "Exiting..." 14 | exit 1 15 | break 16 | elif [ 1 -le "$REPLY" ] && [ "$REPLY" -le $(($#)) ]; 17 | then 18 | DAC=$option 19 | break; 20 | else 21 | echo "Incorrect Input: Select a number 1-$#" 22 | fi 23 | done 24 | } 25 | 26 | if [[ $UID != 0 ]]; then 27 | echo "This script needs to use sudo:" 28 | echo "sudo $0 $*" 29 | exit 1 30 | fi 31 | 32 | if [ `grep "Raspbian" /etc/issue | wc -l` -lt 1 ] ; then 33 | echo "ABORTING: Script is only intended for running on Raspbian distributions" 34 | exit 1 35 | fi 36 | 37 | 38 | if ! [ -f "fir/left.wav" ]; then 39 | echo -n "ERROR: Missing convolution file fir/left.wav for left audio channel." 40 | echo -n "Make sure fir/left.wav (and fir/right.wav) exists and rerun script. " 41 | exit 1 42 | fi 43 | 44 | if ! [ -f "fir/right.wav" ]; then 45 | echo -n "ERROR: Missing convolution file fir/right.wav for right audio channel." 46 | echo -n "Make sure fir/right.wav (and fir/left.wav exists and rerun script. " 47 | exit 1 48 | fi 49 | 50 | 51 | echo "This script will install BruteFIR and shairport-sync on a standard Raspbian installation." 52 | echo 53 | echo -n "Please enter a name for your device. Press return to use default (" `hostname` ")" 54 | echo 55 | echo -n "Name: " 56 | read devname 57 | 58 | ## Blacklist onboard sound, and install snd-aloop 59 | 60 | echo "Setting up audio devices" 61 | 62 | MODULE="snd_bcm2835" 63 | if lsmod | grep "$MODULE" &> /dev/null ; then 64 | echo "Removing $MODULE" 65 | rmmod $MODULE 66 | fi 67 | 68 | MODULE="snd-aloop" 69 | if lsmod | grep "$MODULE" &> /dev/null ; then 70 | echo "$MODULE already loaded" 71 | else 72 | echo "Loading $MODULE" 73 | modprobe snd-aloop 74 | fi 75 | 76 | 77 | ## Query for output DAC 78 | 79 | declare DAC 80 | declare -a DACS 81 | DACS=( $(aplay -l | grep "card" | grep -v "Loopback" | awk '{print $3}') ) 82 | 83 | select_dac "${DACS[@]}" 84 | 85 | echo "Selected $DAC" 86 | 87 | ## Install packages. 88 | 89 | echo "Installing updates" 90 | # sudo apt-get update 91 | # sudo apt-get upgrade 92 | sudo apt-get install autoconf automake avahi-daemon build-essential brutefir git libasound2-dev libavahi-client-dev libconfig-dev libdaemon-dev libpopt-dev libsoxr-dev libssl-dev libtool sox xmltoman 93 | 94 | mkdir -p tmp 95 | cd tmp 96 | 97 | ## Install shairport-sync 98 | echo "Installing shairport-sync" 99 | git clone https://github.com/mikebrady/shairport-sync.git 100 | cd shairport-sync 101 | git checkout development 102 | git pull 103 | autoreconf -fi 104 | ./configure --sysconfdir=/etc --with-alsa --with-avahi --with-ssl=openssl --with-metadata --with-soxr --with-systemd 105 | make 106 | make install 107 | cd ../.. 108 | 109 | 110 | sed -e "s/OUTPUT_DAC/$DAC/g" config/brutefir_config > /etc/brutefir_config 111 | sed -e "s/OUTPUT_DAC/$DAC/g" config/shairport-sync.conf > /etc/shairport-sync.conf 112 | 113 | if [ -n "$devname" ]; then 114 | sed -i -e "s/\%H/$devname/" /etc/shairport-sync.conf 115 | fi 116 | 117 | cp config/brutefir.service /lib/systemd/system/ 118 | 119 | mkdir -p /home/shairport-sync 120 | 121 | sox fir/left.wav -b 32 -e signed-integer -c 1 -r 44100 -t raw /home/shairport-sync/left.raw 122 | sox fir/right.wav -b 32 -e signed-integer -c 1 -r 44100 -t raw /home/shairport-sync/right.raw 123 | 124 | chown -R shairport-sync:shairport-sync /home/shairport-sync 125 | 126 | systemctl enable brutefir.service 127 | systemctl enable shairport-sync.service 128 | 129 | ## Now on to the ugly truths of system tweaking 130 | 131 | for i in `pgrep ksoftirqd`; do chrt -p 99 $i; done 132 | 133 | 134 | # Tweaks. Yes. Patching rc.local is a tad ugly, but required 135 | cp /etc/rc.local /etc/rc.local.old 136 | echo "Patching /etc/rc.local - original file copied to /etc/rc.local.old" 137 | 138 | sed -i -e "s/exit 0/for irqdps in \`pgrep ksoftirqd\`; do chrt -p 99 \$irqdps; done\n/" /etc/rc.local 139 | echo -e "echo \"performance\" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor\n" >> /etc/rc.local 140 | echo -e "\n\nexit 0" >> /etc/rc.local 141 | 142 | ## Change default swappiness 143 | sysctl -w vm.swappiness=1 144 | 145 | cp /etc/sysctl.conf /etc/sysctl.conf.old 146 | echo "Patching /etc/sysctl.conf - original file copied to /etc/sysctl.conf.old" 147 | 148 | echo -e "\nvm.swappiness=1" >> /etc/sysctl.conf 149 | 150 | systemctl start brutefir 151 | systemctl start shairport-sync 152 | 153 | cd .. 154 | 155 | echo "Cleaning up … " 156 | echo "options snd-aloop index=0 pcm_substreams=1" > /etc/modprobe.d/snd-aloop.conf 157 | echo "snd-aloop" > /etc/modules-load.d/snd-aloop.conf 158 | echo "blacklist snd_bcm2835" > /etc/modprobe.d/blacklist-snd_2835.conf 159 | 160 | rm -rf tmp 161 | 162 | echo "Done!" 163 | 164 | 165 | 166 | exit 0 167 | --------------------------------------------------------------------------------