use Raspberry Pi imager on mac to install Raspberry Pi OS on a microSD card
- use the advanced setup option to pre-configure ssh access and wifi
From mac terminal:
- ssh into pi (ssh [email protected], then enter password)
- install
tightvnc
:sudo apt update
(this might take a while) thensudo apt install tightvncserver
- start the VNC server (will need to be restarted on each reboot, but you only specify the password the first time):
vncserver
then enter apassword
(password has 8 character limit)
On Mac: open Finder and press cmd+K
enter vnc://[email protected]:5901
(5901 is the default port for tightvnc) then your password
(tightvnc password, not rpi password)
sudo apt update
Install vim for convenience
``sudo apt install vim`
sudo apt install qjackctl bristol
qjackctl
starts a program that routes midi and audio
- click Start if it doesn't start automatically
- if you are using a USB audio interface, choose that device if not chosen automatically
- for low latency, I chose these settings in the Setup -> Settings panel: Interface: (chose the one showing my USB audio interface), Frames/Period=128, Periods/Buffer=2 (seems to be able to keep up on my Rpi 4B)
startBristol -mini -midi alsa -nnp
starts Bristol synth with a Moog Mini synth emulator module and alsa for midi routing, last-note-played mode (default is that higher notes are held)
In QuackCtl's "Connections" pane (Click the connections button):
- route midi controller to Bristol with click-and-drag (midi input is on left, bristol is in center, outputs are on right)
- route audio from Bristol to system playback_1 and playback_2
Start playing!
Guitarix is great for amp simulation and effects.
sudo apt-get guitarix
guitarix
starts the program. I added a Stereo Delay and Digital Stereo Delay in sequence. using the qjackctl "Connections" pane again, I changed my audio routing to route audio from Bristol to GuitarIX then to the outputs.
Re-loading: we can load a pre-saved connection bay in qjackctl:
qjackctl -a /home/pi/bristol_and_guitarix.xml &
#assumes that when you last set up your bristol connections, you saved your patch bay to `/home/pi/bristol_and_guitarix.xml` (connections window -> new -> save)
To get this working in headless mode is taking some extra work, because qjackctl is specifically a Qt program (designed for a gui). I need to script the actual jack commands instead.
Bristol saves presets by clicking the S in the numeric panel in the -mini GUI
For guitarrix, you start a session under a specific "name", ie guitarrix -n Moog
. Then, you can re-load into that session (note that this is different from the "banks" of presets you save in the program) by running guitarix --load-file /home/pi/.config/guitarix/Moog_rc
and add the -N flag for no GUI and -K flag for no auto-save.
we have to do things in a specific order:
- start the jack audio server and set up parameters
- start bristol* and guitarrix, loading the desired programs
- Make audio and midi connections between programs with jack_connect
*Note: starting bristol with -gui for no gui seems to cause errors; instead, I enabled the GUI on pi startup following this raspi-config option (which as far as I understand means that the rpi starts the gui/window server even if you boot headless) and avoided the
-gui
flag that suppresses Bristol's gui.
For setting up Jack, we'll need to know what our desired sound output device is called. Find the name of your soundcard by looking at the list of cards with the command cat /proc/asound/cards
.
They are listed as number [Name ] : description, you can use the Name
in the script below (Mine was called USB
for my Focusrite 2i2 usb audio interface).
For audio and midi connections, jack_lsp -c
will list all available ports and their current connections and jack_connect connects two audio or midi ports to eachother. It will only see connections for programs that are running - for example, you need to start Bristol to see it's midi and audio ports with jack_lsp and connect them with jack_connect.
Let's make a startup script /home/pi/start.sh
that performs jack setup, starts bristol and guitarix, and connects audio and midi between the MIDI controller - Bristol - Guitarix - Sound output
#!/bin/sh
echo sleeping
sleep 5
#set up jack audio
jack_control ds alsa
jack_control dps device hw:USB
jack_control dps rate 48000
jack_control dps nperiods 2
jack_control dps period 64
jack_control start
echo "started jack"
sleep 3
# start alsa-to-jack connector
# -u flag removes inconsistent numbers from device names, so that each time we boot the devices have the same name
a2jmidid -ue &
echo "started a2j"
# start Bristol synth
startBristol -mini -load 1 -midi alsa -nnp &
#translation: start bristol without gui, use Moog Mini emulator with preset 1, use alsa for midi, don't hold previously played notes prefferentially over newly played notes (no note preference), run in background
echo "started bristol"
# start guitarix effects rack
guitarix -N --load-file ~/.config/guitarix/Moog_rc -K &
#translation: start guitarix without gui, loading the file from the session called Moog_rc (if you started guiarix last time using `guitarix -n Moog` to name your session); -K means don't auto-save
echo started guitarix
#wait a good bit of time to make sure guitarix and bristol are running before trying to make connections
sleep 5
# Now, let's plug everything in!
# (virtually, but we still have to connect virtual midi and audio cables to route things)
#The guitarix connections to audio out were set up automatically for me, but they could be manually
# connected in the same way. To list available ports and their current connections, use `jack_lsp -c`
# We need to add manual connections for bristol -> guitarrix:
jack_connect bristol:out_left gx_head_amp:in_0
jack_connect bristol:out_right gx_head_amp:in_0
# Connect midi controller -> bristol
jack_connect "a2j:WORLDE (capture): WORLDE MIDI 1" "a2j:bristol (playback): bristol input"
echo "connected jack audio and midi ports"
To run this at startup, I had the best luck with autostart (rather than rc.local) - I think this was because one of the programs needed the window server to start. auto start is easy to use: create a folder and file, is
mkdir /home/pi/.config/autostart
vim /home/pi/.config/autostart/synth.desktop
my synth.desktop
file looks like this (you can name the file anything but keep the .desktop
extension to tell autostart what kind of startup program it is):
[Desktop Entry]
Name=PiSynth
Exec=/usr/bin/bash /home/pi/start.py > /home/pi/start.out 2>&1 &
The part after Exec= tells autostart what comand to run. This command runs our script start.py and logs output to start.out in the home directory. The 2>&1 logs any errors to standard output, and & at the end runs the command in the background.
It took a bit of extra playing around with the placement of delays (sleep) in the script to make sure commands didn't start before the necessary processes had started running.
Another issue with the current set up is that if anything briefly disconnects (especially the midi keyboard), you'd have to reboot the entire pi to get it to re-connect if you are headless. qjackctl patchbay creates persistent connections, but this isn't possible headless. I think I need to write a script that constantly tries to connect ports using jack_connect if/in case they are disconnected.
Bristol defaults: the file containing the saved presets is in ~/.bristol/memory/mini/mini1.mem, and similar the file with the midi mappings is in ~/.bristol/memory/profiles/mini, and can be edited directly to change midi mappings in the GUI, you have to middle-mouse-click a command then move the midi control. Doing so again will remove the mapping. However, this got buggy when I tried to remove default mappings. The solution was to quit bristol, remove the unwanted mappings in the file ~/.bristol/memory/profiles/mini, then re-start Bristol. (Mappings created in the bristol GUI are also saved to that file). That file is per-emulator (eg mini) rather than per-patch (eg program 1).
Saving and loading presets in the mini
emulator is done with S and L buttons on the key pad, which took me a while to figure out.
the first time around, nothing was working properly. I re-flashed the rpi boot SD card with the OS and started over, and it went better the second time (maybe I forgot something I did the first time that messed it up)
First attempt: At some point things things got hairy for me. I could play a sound with the built in audio player on the headphone jack or USB audio interface. I could start bristol, start qjackctl, route midi from my controller to bristol, recieve midi input and even make sounds. But the sounds were barely audible hisses rather than musical notes. Seemed like a sampling rate issue or something weird like that.
Second attempt: I installed amsynth and was able to make sounds with it, even with the midi controller (used qjackctl for routing midi) but it has about 100ms latency. It seems like using jack instead of alsa audio might help but haven't gotten it to work yet. Jack kept giving errors.
(After these attempts, I started fresh and followed the sequence given above, and things worked great.)
vnc was causing a very anoying issue where I couldn't close windows. This solution worked for me:
TLDR: change 'mutter' to 'openbox-lxde-pi' in /etc/xdg/lxsession/LXDE-pi/desktop.conf (editing this file requires sudo)