Boot-to-synth Raspberry Pi Zero project for the Pimoroni Pirate Audio Headphone Amp.
crates/engine- wavetable oscillator engine + key-to-frequency tuningcrates/audio_alsa- ALSA playback loopcrates/ui- ST7789 menu renderer + Pirate Audio buttons (active-low GPIO)crates/controller- debounced synth controller; bridges UI state to audio commandscrates/visuals_drm- HDMI scope/visualizer (DRM)crates/app- synth binary wiring config, UI, and audioassets/wavetables- source wavetable filesassets/WAV- granular source WAV files (PCM16/float32), including a defaultplaceholder.wavsdcard/boot/firmware/pirate-synth- first-boot installer/services/config staged for SD cardscripts/package.sh- cross-build + SD card bundle packaging
- Wavetable synthesis with 8 built-in waveforms (sine, triangle, sawtooth, square, pulse, etc.)
- Granular synthesis engine (auto-selected when WAV files are present in
wav_dir) - Bytebeat synthesis engine with 10 algorithms (Basic, Sierpinski, Melody, Harmony, Acid, Wobble, Glitch, Pulse, Storm, Echo) plus a random-cycling mode; up to 8 pitched oscillators with per-oscillator drift LFO and stereo spread
- Independent on/off and volume controls for Wavetable, Granular, and Bytebeat layers; all fade in/out over 5 seconds
- Per-oscillator fade when oscillator count changes (only added/removed oscillators fade)
- Per-voice fade when granular voice count changes (only added/removed sources fade)
- Per-oscillator detune, drift LFO, stereo spread
- Effects: reverb (Schroeder), tremolo, crossfade, filter sweep, FM, subtractive
- Class-compliant USB MIDI keyboard note input (monophonic, most recent note latches key/octave)
- MIDI CC control for cents detune (
midi_cents_ccin config) - 9 scale modes (N/A, major, minor, pentatonic, dorian, etc.)
- 14-item ST7789 240×240 display menu via SPI (includes
VIDEO: OFF|ON|NO HDMIstatus) - First-boot installer for Raspberry Pi OS Lite
- Offline UI rendering (
--render-ui) for development without hardware - Safe shutdown: hold Up and Down simultaneously for 5 seconds; the display shows "Powering down" and the system halts cleanly via
shutdown -h now
sudo apt-get update
sudo apt-get install -y --no-install-recommends libasound2-dev gcc-arm-linux-gnueabihf libc6-dev-armhf-cross zip
./scripts/package.shOutputs:
dist/pirate-synth-sdcard.tar.gzdist/pirate-synth-sdcard.zipdist/pirate-synth-sdcard-armv6.tar.gzdist/pirate-synth-sdcard-armv6.zipdist/pirate-synth-sdcard-gpi.tar.gzdist/pirate-synth-sdcard-gpi.zip
Artifact targets:
pirate-synth-sdcard.*: existingarm-unknown-linux-gnueabihfbuild (ARMv7+ armhf Linux target, e.g. Pi 2/3/4 running 32-bit Raspberry Pi OS)pirate-synth-sdcard-armv6.*: Pi Zero / Zero W compatible ARMv6 buildpirate-synth-sdcard-gpi.*: GPi CASE bundle for Pi Zero / Zero W using the GPi hardware profile
To package with a prebuilt ARMv6 binary (used by CI), set:
PIRATE_SYNTH_ARMV6_BINARY=/absolute/path/to/pirate_synth ./scripts/package.sh- Flash Raspberry Pi OS Lite to SD card.
- Mount the boot partition.
- Extract
pirate-synth-sdcard.tar.gz(or zip). - Copy
boot/firmware/*from the extracted bundle onto the SD card boot partition. - Boot the Pi Zero.
The GPi CASE uses the Retroflag GPi CASE with a Raspberry Pi Zero.
Prerequisites:
- Raspberry Pi Zero or Zero W
- Retroflag GPi CASE
- Raspberry Pi OS Lite
Install steps:
- Download the
pirate-synth-sdcard-gpibundle (.tar.gzor.zip). - Flash Raspberry Pi OS Lite to SD card.
- Mount the boot partition.
- Extract the GPi bundle.
- Copy
boot/firmware/*from the extracted bundle onto the SD card boot partition. - Insert SD card into the GPi CASE and boot.
Controls:
- D-pad Up/Down: navigate menu
- D-pad Left: Back
- D-pad Right: Select
- Hold Up + Down for 5 seconds: safe shutdown
Note: A/B/X/Y/Start/Select buttons are not used.
pirate-synth-firstboot.service runs the appropriate hardware-specific installer once on first boot — typically /boot/firmware/pirate-synth/install.sh, or /boot/firmware/pirate-synth/install-gpi.sh for the GPi bundle — and then writes sentinel file /var/lib/pirate-synth/firstboot.done.
Installer actions:
- Ensures
/boot/firmware/config.txtcontains:dtoverlay=hifiberry-dacdtparam=audio=offdtparam=spi=ongpio=25=op,dh
- Installs runtime dependency
alsa-utils - Installs binary to
/usr/local/bin/pirate_synth - Installs config to
/etc/pirate-synth/config.toml - Copies wavetables to
/var/lib/pirate-synth/wavetables - Copies granular WAV sources to
/var/lib/pirate-synth/WAV - Enables
pirate-synth.service - Reboots (only if
config.txtwas changed to apply device-tree settings; otherwise restartspirate-synth.serviceimmediately)
Config file: /etc/pirate-synth/config.toml
The installed config.toml is fully commented and contains all available settings. Code defaults (used when a key is absent from the config file) are shown below for reference; the shipped config.toml overrides many of these for the target hardware.
sample_rate = 48000
buffer_frames = 256
spi_device = "/dev/spidev0.1"
root_key = "A"
root_octave = 1
fine_tune_cents = 0.0
midi_cents_cc = 1
scale_index = 7 # 0=N/A 1=Major 2=Minor 3=Penta 4=Dorian 5=Mixo 6=Whole 7=Hirajoshi 8=Lydian
stereo_spread = 100 # 0–100
oscillators = 8
oscillators_active = false
wavetable_dir = "/var/lib/pirate-synth/wavetables"
bank_index = 0 # 0=A 1=B 2=C 3=D
volume = 50 # master output 0–100
transition_secs = 3.0 # smooth transition duration for cents/scale/bank changes
note_transition_ms = 0.0 # glide time in ms; 0 = snap immediately
reverb_enabled = true
reverb_wet = 0.20
reverb_feedback = 0.84
reverb_damp = 0.20
reverb_comb_count = 4
granular_reverb_enabled = true
granular_reverb_wet = 0.45
granular_reverb_feedback = 0.88
granular_reverb_damp = 0.12
granular_reverb_comb_count = 8
tremolo_enabled = true
tremolo_depth = 0.35
crossfade_enabled = true
crossfade_rate = 0.05
filter_sweep_enabled = true
filter_sweep_min = 0.15
filter_sweep_max = 0.80
filter_sweep_rate_hz = 0.008
fm_enabled = false
fm_depth = 0.15
subtractive_enabled = false
subtractive_depth = 0.30
wav_dir = "/var/lib/pirate-synth/WAV"
granular_active = false
granular_wavs = 8
granular_volume = 50
granular_grain_size_ms = 120.0
granular_density_hz = 24.0
granular_max_overlap = 16
granular_position = 0.5
granular_position_jitter = 0.15
granular_attack_ms = 10.0
granular_release_ms = 25.0
granular_note_ms = 4000.0
granular_spawn_jitter = 0.5
hdmi_visuals_enabled = falseoscillatorscontrols simultaneous oscillators (allocated at startup);oscillators_activesets whether the wavetable layer starts active at bootroot_key,root_octave,fine_tune_centstune the drone via the UI- USB MIDI note-on updates
root_keyandroot_octavelive (note-off is ignored so notes latch) midi_cents_ccdefines which MIDI CC number (0-127) controlsfine_tune_centsfrom -100 to +100scale_indexselects the scale mode (0=N/A means no scale constraint / free chromatic)stereo_spread(0–100) controls how wide oscillators are spread across the stereo fieldbank_indexselects which wavetable bank (0=A, 1=B, 2=C, 3=D) is active at startupvolumesets the master output level (0–100)transition_secssmooths cents/scale/bank changes over that many seconds (±20% per-oscillator jitter)note_transition_mssets the glide time in milliseconds for note/frequency changes (0 = snap)- Engine selection is automatic by source-folder contents:
- if
wav_dircontains.wavfiles, granular synthesis mode is used - otherwise the wavetable engine uses
wavetable_dir
- if
- Granular mode currently supports WAV PCM16 and float32 sources (TODO: add more WAV variants/modulation features)
granular_wavscontrols the number of active granular source lanes (adjustable asGR Voicesin the UI):0disables granular playback- values above loaded WAV file count round-robin the available files
granular_volumesets the initial granular output level (0–100); adjustable asGR Volin the UIgranular_activesets whether the granular layer starts active at boot; toggleable asGranularin the UI- The Bytebeat layer is controlled entirely from the UI (
Bytebeat →submenu): On/Off, Volume (0–100), Algorithm (10 named + Random), and Oscillator count (1–8) hdmi_visuals_enabledgates HDMI visualizer startup (Video: Off/On/No HDMIin the UI)- All three synthesis layers (Wavetable, Granular, and Bytebeat) can be active simultaneously; their volumes are mixed independently
- Buttons are active-low on BCM GPIO 5, 6, 16, 24 (mapped to Up/Down/Select/Back)
- Holding Up + Down together for 5 seconds triggers a safe system shutdown (display shows "Powering down")
- Display uses ST7789 over SPI device
/dev/spidev0.1 - Display control pins: DC BCM9, backlight BCM13
- Runtime access is through
rppal(GPIO character-device/dev/gpiomem+ SPI/dev/spidev*), not legacy/sys/class/gpio pirate-synth.servicelogs startup and hardware init to journald (journalctl -u pirate-synth -b)
See CONTRIBUTING.md for build prerequisites, running tests, and pull request guidelines.
MIT — see LICENSE. Copyright 2026 Daniel Vincent.