Using a raspberry pi as a DIY fx pedal

publié le 23 August 2023

Not quite ten years ago (!), I wrote about creating your own real-time guitar effects with python. Since then, I've been using pyo more and more in my gigs for low latency audio processing. But until now, I've always been using it on a laptop, with a bulky sound interface, foot controllers, a reverb rack, etc.

All those things make sense for a project like Dragonfly where the quantity of stuff to take with me for a gig is huge anyway, but I've been dreaming more and more to have a smaller, much portable version, e.g. for occasions where I only take my harpejji with me and would like to travel by train.

rpi-base fx box

So I looked at this raspberry pi that was sleeping on a shelf and thought that maybe...

Many hours (days!) later, I can confirm that it is absolutely possible to turn a rpi into a low latency audio processing device. It's even relatively easy... if you know how to do it!

This post will describe succinctly the main steps necessary for getting there. It's more a reminder for the later myself than a tutorial... but if you're familiar with the Linux audio world, you should be able to follow these steps yourself.

Warning Although this post solves all computer-related problems, I'm still having problems with electrical noises in the output. See the update at the end of this article for more details before you spend time trying to replicate this process!

Material

I've been using

Kernel and distrib

I've tried, measured, tweaked, measured again... but I couldn't get under 10ms roundtrip latency with a mainline kernel. I won't play on a device that has a higher latency than this threshold, so I had to switch to an rt-kernel.

After trying different things, I found that the easiest way to do that was switching from Raspberry Pi Os to Debian, because the latter provides an rt-kernel in its repos.

Basic install

(https://raspberrytips.com/install-debian-on-raspberry-pi/ saved me some time there)

  • Download and flash the image
  • Boot the rpi and - please! - begin with setting a root password (passwd)
  • Edit /etc/network/interfaces.d/wlan0 for getting online
  • update your system (apt update and apt upgrade)
  • create a normal user (adduser <name>)
  • apt install sudo
  • add your user to groups audio and sudo

Post-install tweaks

  • install a realtime kernel (apt install linux-image-rt-arm64) and reboot (test with uname -a)
  • install python3, jackd2, linux-cpupower, alsa-utils, python3-pyo
  • setup passwordless sudo (sudo visudo and add the line user_name ALL=(ALL) NOPASSWD:ALL)

Some soundcards have volumes turned to 0 on boot. Launch alsamixer, set the volumes to your liking, quit and run sudo alsactl store. We will handle restore later.

You should now be able to set your cpu governor to performance (sudo cpupower frequency-set -g performance) and launch a low-latency jackd without xruns (jackd -S -P70 -dalsa -dhw:U24XL -r48000 -p64 -n3 -D, which results in a very honorable 4ms 'jack' latency).

If you encounter xruns, rtcqs should help you nailing down what's going wrong.

If you feel so inclined, you can now measure the roundtrip latency with jack_iodelay. In my case I got ~6.7ms, which is very comfortably under my 10ms requirement!

The main script

Write the pyo script you need for your effects (take inspiration there if required).

I'm using pyo-nk2 to interact with the nanoKONTROL2. In particular, I added

quitter = pyo.TrigFunc(nk2.Press('stop'), quit)

to my script, with quit() being the function that terminates the server and stops the script.

I also like to have

def cycle_blink():
    nk2.led_blink('cycle')

alive = pyo.Pattern(cycle_blink).play()

Now I can verify that my script is running by checking the "cycle" led on my nanoKONTROL2 :-)

Launching the script at boot time

After some research I decided to create a custom systemd unit.

I want to be able to connect to the rpi and see what's happening in case of a problem; that's why I decided to launch jackd and my script in a byobu session with tmux backend (don't forget to apt install byobu!). Using the technique described in this answer that yields:

#!/usr/bin/bash

sudo alsactl restore
sudo cpupower frequency-set -g performance

# start new detached byobu session
byobu new-session -d -s harpejji
byobu split-window -h

byobu select-pane -t 0
byobu send-keys "JACK_NO_AUDIO_RESERVATION=1 jackd -S -P70 -dalsa -dhw:U24XL -r48000 -p64 -n3 -D" ENTER

sleep 1

byobu select-pane -t 1
byobu send-keys "python3 /home/amiguet/harpejji/harpejji.py; sudo poweroff" Enter

I must admit this is not very elegant. I'm still thinking about ways to get the same result in a nicer way.

Anyway, we can now create a systemd unit:

# copy to /etc/systemd/system/harpejji.service
[Unit]
Description=Harpejji

[Service]
# see https://bbs.archlinux.org/viewtopic.php?pid=1306587#p1306587
LimitRTPRIO=infinity
LimitMEMLOCK=infinity

# Type is forking because of byobu
Type=forking
User=amiguet
Group=audio
ExecStart=/home/amiguet/harpejji/harpejji

[Install]
WantedBy=multi-user.target

# networking startup takes too long, let's start this one before
WantedBy=networking.service

Enable the service (sudo systemctl enable harpejji.service) and reboot. All going well, the "cycle" led should start to blink after a while. You can then use your fx box. When you're finished, just press "stop" on the nanoKONTROL2 and the rpi will shut down.

Limitations

When ssh'ing into my rpi, there seem to be some interference between network and audio that causes noise (not xruns!) in the output of my sound interface. I will probably completely disable networking when I'm sure everything works well (that will also allow for a faster boot), but it's handy to keep it as long as I'm tweaking things. Anyway, the noise disappears as soon as I disconnect from the machine.

Conclusion

This is a puzzle with many small pieces and getting them all together was not an easy task, but there's nothing really difficult in there. And transforming a $40 single board computer into a low latency audio processing unit is quite amazing!

Update 28.08.2023

I thought I was there, but I've been having electrical noise problems that I could solve yet. After some analysis, and with the kind help of the excellent Dario Ciani, I could identify two different sources of noise:

  1. A loud low hum, very typical of a ground loop... except that that is impossible as the pi is not grounded. As it happens, it's not a ground loop, but a hum created by a floating ground. The hum disappears if I plug a monitor through hdmi into the pi, as the pi is then grounded through this connection. It also disappears if I route the signal through a mixer instead of plugging my sound interface directly into the amp.

  2. A high hiss, provoked by the electrical noise of the Switched mode power supply of the pi.

If I replace my U24XL with my Presonus audio box 1818VSL, which is powered from the wall socket, it solves both problems. But it defeats the initial project of having something very portable.

(And of course I also tested the sound interface from a laptop to be sure it is capable of delivering a good sound!)

I'll have to think about it and find how I can solve these problems and have my raspberry pi deliver a usable sound... I will describe the solutionin a new post on this blog soon (hopefully!).