Raspberry Pi
MIDI Synthesizer Engine
Instructions for configuring a Raspberry Pi 3B or 4 as a portable, headless, ultra-low latency MIDI synthesizer using the FluidSynth synthesizer.
Shopping list:
Raspberry Pi 3B/3B+ (lower power requirement when using a battery bank):
(Discontinued, but still widely available)
OR
Raspberry Pi 4 (2GB+ allows for larger SoundFont options):
USB sound card (required for low latency):
Devices that I have personally tested as working flawlessly, though I'm sure most others also work well:
Pyle PAD20MXU (recommended for EQ adjustment): https://www.amazon.com/dp/B009GU4UHY/
Sabrent AU-MMSA (much cheaper): https://www.amazon.com/dp/B00IRVQ0F8/
USB MIDI device
A USB MIDI keyboard, guitar, or perhaps something that you have built yourself?
Setup Instructions:
Download the latest version of Raspbian Lite from: https://www.raspberrypi.org/downloads/raspbian/
Burn the image to a 4GB+ MicroSD card, and follow the excellent instructions for headless SSH configuration at: https://www.tomshardware.com/reviews/raspberry-pi-headless-setup-how-to,6028.html
Run sudo apt update && sudo apt upgrade to upgrade all installed software to current versions.
Run sudo raspi-config, change the default password, configure the device for your locale, and enable console autologin in the boot options.
Run sudo apt install alsa-utils fluidsynth screen to install the required synthesizer packages.
Run cd ~/ && wget https://raw.githubusercontent.com/KOOPInstruments/raspi-synth/master/synth.sh && chmod +x ~/synth.sh to download the headless raspi-synth script, and mark it for execution.
Run nano ~/.bashrc, add the line /home/pi/synth.sh to the end of the file, and press Ctrl-o Ctrl-x to save and quit nano. This will tell the Pi to launch the script automatically on login (which also means launch on boot due to console autologin enabled earlier).
Run cat /proc/asound/cards to get a list of connected sound devices, and take note of the unique sound device name string on the same line as the device number, example in bold: 2 [CODEC ]: USB-Audio - USB AUDIO CODEC.
Run nano ~/synth.sh and edit the line beginning with audioDevice=$ to insert your unique sound device name between the double-quotes in the 'grep' statement. Press Ctrl-o Ctrl-x to save and quit nano.
Run alsamixer press F6 to select your sound card, set the master volume to a reasonable level, and press Esc to exit.
Run sudo reboot and test out your new synthesizer!
Using Different SoundFonts:
Download your General MIDI SoundFont (.SF2) file to the /home/pi/ folder, run nano ~/synth.sh and change the end of the line beginning with screen -dmS FluidSynth0 to modify the path for your SoundFont file name and location, for example: /home/pi/Timbres\ Of\ Heaven\ GM_GS_XG_SFX\ V\ 3.4\ Final.sf2
RasPi-Synth Script:
## Raspberry Pi 4 FluidSynth MIDI Sound Engine
## 2019 - KOOP Instruments (koopinstruments@gmail.com)
# Remount the file system read/write to allow modifications
sudo mount -o remount,rw /
# Update the clock
sudo ntpd -q -g
echo "Starting synth script in 5 seconds (press Ctrl-c to cancel)..."
sleep 1
echo "4..."
sleep 1
echo "3..."
sleep 1
echo "2..."
sleep 1
echo "1..."
sleep 1
# If the script was not cancelled, remount the file system readonly
sudo mount -o remount,r /
## Optional power saving (uncomment if desired):
# sudo ifconfig wlan0 down # Disable the Wi-Fi adapter
sudo tvservice --off # Disable HDMI video output
echo "Killing any existing fluidsynth processes..."
sudo killall -s SIGKILL fluidsynth &>/dev/null
# Run the rest of the script on a loop in case a new sound card is connected, the FluidSynth server crashes, or a new MIDI instrument is connected
while [[ 1 -eq 1 ]]; do
# Run 'cat /proc/asound/cards' to get a list of audio devices, and modify the grep statement below with a unique identifying string
# Examples:
# audioDevice=$(cat /proc/asound/cards | grep "bcm2835_alsa" | awk -F" " '{ print $1 }') # Raspberry Pi on-board audio (high latency - only use if no other options)
# audioDevice=$(cat /proc/asound/cards | grep "USB-Audio - USB Audio Device" | awk -F" " '{ print $1 }') # Sabrent USB sound card
# audioDevice=$(cat /proc/asound/cards | grep "USB-Audio - Logitech" | awk -F" " '{ print $1 }') # Logitech USB sound card
audioDevice=$(cat /proc/asound/cards | grep "USB-Audio - USB AUDIO" | awk -F" " '{ print $1 }') # Peavy USB mixer sound card
if pgrep -x "fluidsynth" > /dev/null
then
sleep 1
else
echo "Starting fluidsynth server..."
# Blink both lights to let the user know that the synth is starting
sudo echo 1 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 1 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sudo echo 1 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 1 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sudo echo 1 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 1 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led0/brightness &>/dev/null
sudo echo 0 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
# Start the FluidSynth server in a new screen session to allow reattaching for troubleshooting purposes
screen -dmS FluidSynth0 bash -c "sudo nice -n -20 fluidsynth -i -s -g 0.6 -a alsa -o audio.alsa.device=hw:$audioDevice -c 1 -z 1 -o synth.cpu-cores=4 -o synth.polyphony=128 /usr/share/sounds/sf2/FluidR3_GM.sf2"
sleep 5
fi
# Scrape the ALSA port number of the FluidSynth Server
fsClientNum=$(aconnect -l | grep "FLUID Synth" | awk -F" " '{ print $2 -0 }')
# Enable one light to let the user know that device discovery is running
echo 1 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
myCounter=1
while [[ $myCounter -lt $fsClientNum ]]; do
aconnect $myCounter:0 $fsClientNum:0 2>/dev/null
let myCounter=myCounter+1
done
echo 0 | sudo tee /sys/class/leds/led1/brightness &>/dev/null
sleep 1
done