start_jack() was checking for /dev/shm/jack_default_0_0 which never exists on JACK 1.9.22 (used on RPi/DietPi). The check made start_jack() always timeout and return False. Boot sequence ignored the return value (luck), but the profile change handler checked it via 'if not ok: rollback', causing all buffer/rate changes to fail silently every other time. Replace with _jack_is_operational() using jack_lsp which tests actual JACK IPC and works correctly on all JACK versions.
Pi Multi-FX Pedal 🎸
A real-time guitar multi-FX pedal running on Raspberry Pi 4B with 32 DSP effects, NAM A2 neural amp modeling, IR cab simulation, 4-Cable Method routing, and a web UI on pedal.local.
Quick Install (Focusrite 2i2 Test)
Get the pedal running on a fresh RPi with a Focusrite 2i2 in under 10 minutes.
1. OS Setup
Start with Raspberry Pi OS Lite (64-bit) or DietPi.
Configure hostname, enable SSH, and connect to WiFi:
sudo hostnamectl set-hostname pedal
# Or on DietPi: use dietpi-config
2. System Packages
sudo apt update && sudo apt upgrade -y
sudo apt install -y \
git python3 python3-pip python3-venv python3-dev \
jackd2 libjack-jackd2-dev \
libsndfile1-dev \
cmake build-essential \
avahi-daemon # for pedal.local mDNS
3. Clone the Repo
git clone https://gitea.ourpad.casa/shawn/pi-multifx-pedal.git ~/projects/pi-multifx-pedal
cd ~/projects/pi-multifx-pedal
4. Python Environment
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
Note:
neural-amp-modelerneeds PyTorch. On RPi 4B aarch64:pip install torch --index-url https://download.pytorch.org/whl/nightly/cpu
5. Focusrite-Specific Config
Kill PulseAudio (it grabs USB audio):
pulseaudio --kill
Verify Focusrite is detected:
aplay -l
# Look for: card 1: USB Audio [Scarlett 2i2 USB], device 0
arecord -l
# Look for same
Copy the mono test config:
mkdir -p ~/.pedal
cp etc/config-focusrite-mono.yaml ~/.pedal/config.yaml
6. Run the Pedal
cd ~/projects/pi-multifx-pedal
source .venv/bin/activate
python3 main.py
7. Open the Web UI
From any device on the same network:
- Browser: http://pedal.local
- Or use the IP: http://192.168.x.x
WiFi Hotspot (No Network? No Problem)
If there's no WiFi network to join — stage, rehearsal space, park bench — the Pi can create its own:
# Run once to set up
sudo bash scripts/setup-wifi-ap.sh
# Or with custom SSID/password:
sudo bash scripts/setup-wifi-ap.sh --ssid "Pi-Pedal" --psk "yourpassword"
# Phone connects to "Pi-Pedal" → open http://pedal.local
# To turn it off and go back to client mode:
sudo bash scripts/setup-wifi-ap.sh --disable
Pro tip: Add a USB WiFi dongle for dual-network — Pi creates hotspot on built-in WiFi, dongle handles internet access for git pulls and updates.
8. Wire It Up
Mono mode (standard pedal):
Guitar jack → Focusrite Input 1 (front)
Focusrite Output 1 (rear) → Amp
4CM mode (four cable method):
Guitar → Focusrite Input 1
Amp FX Send → Focusrite Input 2
Focusrite Output 1 → Amp Input (Send)
Focusrite Output 2 → Amp FX Return (Output)
Then switch config:
cp etc/config-focusrite-4cm.yaml ~/.pedal/config.yaml
Toggle 4CM on/off from the Settings page in the web UI.
Port & Privileges
The web UI runs on port 80 (standard HTTP), so URLs are clean — just http://pedal.local with no port number.
Binding to port 80 requires elevated privileges. Either:
-
Run as root (simplest):
sudo python3 main.py -
Grant the
cap_net_bind_servicecapability (safer — no root shell):sudo setcap 'cap_net_bind_service=+ep' $(readlink -f $(which python3)) python3 main.py # no sudo needed -
Override the port via code change (development only): Change
DEFAULT_PORTinsrc/web/server.pyandport=80inmain.py.
For development, option 2 is recommended.
WiFi / Bluetooth Permissions
The WiFi hotspot and Bluetooth management features call sudo internally for system operations (hostapd, dnsmasq, iwlist, systemctl). If your user does not have passwordless sudo, these features will fail silently.
Add a sudoers entry to grant passwordless access for the pedal user (pi or your username):
sudo visudo -f /etc/sudoers.d/pi-pedal
Add one of the following, replacing pi with your actual username:
Minimal (only the WiFi setup script):
pi ALL=(ALL) NOPASSWD: /home/pi/projects/pi-multifx-pedal/scripts/setup-wifi-ap.sh *
pi ALL=(ALL) NOPASSWD: /usr/sbin/iwlist *
pi ALL=(ALL) NOPASSWD: /usr/sbin/iwconfig *
pi ALL=(ALL) NOPASSWD: /usr/bin/wpa_cli *
pi ALL=(ALL) NOPASSWD: /usr/bin/systemctl * hostapd
pi ALL=(ALL) NOPASSWD: /usr/bin/systemctl * dnsmasq
pi ALL=(ALL) NOPASSWD: /usr/bin/systemctl * pi-bt-midi
Permissive (full passwordless sudo — convenient on a dedicated device):
pi ALL=(ALL) NOPASSWD: ALL
The web UI surfaces permission errors with an "insufficient_permissions" key when sudo calls fail due to missing sudoers configuration.
Hardware
| Component | Spec |
|---|---|
| SBC | Raspberry Pi 4B (2GB+ RAM) |
| Audio I/O | I2S DAC + ADC (PCM1808 + PCM5102 custom HAT, or USB via Focusrite 2i2) |
| Footswitches | 3-6 momentary soft-touch (e.g. MXR-style) |
| LEDs | RGB WS2812B or APA102 per footswitch + status LEDs |
| Display | 128x64 OLED SSD1306 (I2C) or 5" DSI touch |
| MIDI | 5-pin DIN in/out via UART + optoisolator |
| Power | 5V/3A USB-C (with buck for 3.3V rail) |
| Enclosure | 1590BB or 3D-printed custom |
32 Effects
| Category | Effects |
|---|---|
| Dynamics | Noise Gate, Compressor, Expander, De-esser, Transient Shaper, Sidechain Compressor |
| Drive | Boost, Overdrive, Distortion, Fuzz, Bitcrusher, Wavefolder, Rectifier |
| Amp + Cab | NAM Amp Model (any .nam file), IR Cab Sim (any .wav) |
| Modulation | Chorus, Flanger, Phaser, Tremolo, Vibrato, Ring Mod, Auto-Wah, Envelope Filter, Rotary Speaker, Uni-Vibe, Auto-Pan, Stereo Widener |
| Time | Delay, Ping-pong Delay, Multi-tap Delay, Reverse Delay, Tape Echo, Reverb, Shimmer Reverb, Early Reflections, Looper |
| Pitch | Octaver, Detune, Harmonizer, Whammy, Pitch Shifter |
| Filter | EQ, Parametric EQ, High-Pass, Low-Pass, Band-Pass, Notch, Formant, Wah |
| Utility | Volume, Tuner |
Project Structure
├── src/
│ ├── dsp/ Audio processing pipeline
│ │ ├── pipeline.py FX chain orchestration (mono + 4CM split)
│ │ ├── ir_loader.py IR convolution engine
│ │ ├── nam_host.py NAM model loading & inference
│ ├── midi/ MIDI subsystem
│ │ ├── handler.py MIDI event parse & dispatch
│ ├── presets/ Preset system
│ │ ├── manager.py Save/load/bank/preset CRUD + 4CM routing
│ │ └── types.py Preset & chain data model + FXType enum
│ ├── ui/ Hardware UI layer
│ │ ├── footswitch.py Debounced GPIO input
│ │ ├── leds.py RGB LED controller
│ │ └── display.py OLED display manager
│ ├── web/ Web UI (FastAPI + WebSocket)
│ │ ├── server.py REST + WebSocket endpoints
│ │ ├── templates/ Jinja2 HTML pages
│ │ └── static/ CSS + JS
│ └── system/ System integration
│ ├── audio.py ALSA/JACK/I2S configuration
│ └── config.py YAML config loader
├── etc/ Config file templates
├── scripts/ Build & utility scripts
├── tests/ Tests
├── docs/ Documentation + test plans
└── hardware/ KiCad/PCB schematics
Development
# Tests
source .venv/bin/activate
python -m pytest tests/ -v
# All 574 tests should pass
Early development — initial scaffold.