RPi Audio Mixer — Disk Image Builder (SD / USB / NVMe)
This directory contains the automated build system for creating a ready-to-flash Raspberry Pi 4B disk image with the full audio mixer stack. The same image works for SD cards, USB SSDs, and NVMe HATs — just choose your target.
Quick Start
# Install build dependencies
sudo apt install wget xz-utils dosfstools e2fsprogs qemu-user-static
# Optional: for PREEMPT_RT kernel cross-compilation
sudo apt install gcc-aarch64-linux-gnu
# Build the image
./build/build.sh
# The image lands in:
ls -lh build/out/rpi-audio-mixer-*.img.xz
Build Modes
| Command | Description | Time (approx) |
|---|---|---|
./build/build.sh |
Full build with PREEMPT_RT kernel | 30-45 min |
./build/build.sh --skip-kernel |
Quick build, stock kernel | 10-15 min |
./build/build.sh --kernel-only |
Only build RT kernel | 10-15 min |
./build/build.sh --clean |
Remove all build artifacts | < 1 min |
./build/build.sh --target usb |
Build for USB SSD boot | same as default |
./build/build.sh --target nvme |
Build for NVMe HAT (NVMe driver enabled) | +1 min |
Flashing to SD Card
From Linux
# 1. Decompress the image
xz -d build/out/rpi-audio-mixer-*.img.xz
# 2. Find your SD card device
lsblk
# Look for /dev/sdX or /dev/mmcblkX (e.g., /dev/sdb or /dev/mmcblk0)
# 3. Flash the image (WARNING: this overwrites the target device!)
sudo dd if=build/out/rpi-audio-mixer-*.img of=/dev/sdX bs=4M status=progress conv=fsync
# 4. Sync and eject
sync
sudo eject /dev/sdX
Flashing to USB SSD / NVMe
The same image works for USB SSD and NVMe HAT — it's just flashed to a different device.
USB SSD
# 1. Find your USB SSD device
lsblk # look for /dev/sda, /dev/sdb, etc.
# 2. Flash the image
sudo dd if=build/out/rpi-audio-mixer-*.img of=/dev/sda bs=4M status=progress conv=fsync
# 3. On first boot, the wizard can configure EEPROM for USB boot
# Or do it manually:
# sudo rpi-eeprom-config --edit → BOOT_ORDER=0xf41
NVMe HAT
Requires an NVMe HAT (official RPi, Pimoroni, or Geekworm) connected via PCIe.
# 1. Flash to NVMe SSD (connect via USB adapter or directly on Pi)
sudo dd if=build/out/rpi-audio-mixer-*.img of=/dev/nvme0n1 bs=4M status=progress conv=fsync
# 2. Configure EEPROM for NVMe boot:
# sudo rpi-eeprom-config --edit → BOOT_ORDER=0xf614
# 3. Boot order: NVMe → USB → SD → Restart
⚠️ USB performance note: During audio operation, minimize writes to USB SSDs as they share the USB bus with audio interfaces. NVMe HAT is recommended for production use — it uses dedicated PCIe lanes with no USB bus contention.
From macOS
# 1. Decompress
xz -d build/out/rpi-audio-mixer-*.img.xz
# 2. Find the SD card
diskutil list
# Look for /dev/diskX (external, physical)
# 3. Unmount (do NOT eject)
diskutil unmountDisk /dev/diskX
# 4. Flash
sudo dd if=build/out/rpi-audio-mixer-*.img of=/dev/rdiskX bs=4m status=progress
# 5. Eject
diskutil eject /dev/diskX
From Windows
Use Raspberry Pi Imager or balenaEtcher:
- Decompress the
.img.xzwith 7-Zip or WinRAR - Open Raspberry Pi Imager
- Choose "Use custom" → select the
.imgfile - Select your SD card and flash
First Boot
- Insert the SD card into a Raspberry Pi 4B (4GB+ RAM recommended)
- Connect your USB audio interface
- (Optional) Connect HDMI touchscreen + Ethernet
- Power on
The first-boot setup wizard will appear automatically on the HDMI display (or serial console). It will guide you through:
- Audio interface detection and selection
- WiFi configuration
- Hostname setting
- API key generation
- JACK buffer/latency settings
After the wizard completes, the system reboots into normal operation.
Default Access
| Service | URL / Command | Default |
|---|---|---|
| Web UI | http://pi-mixer.local:8080 |
API key from wizard |
| SSH | ssh pi@pi-mixer.local |
Password: raspberry |
| Touch UI | HDMI display (auto-starts) | — |
Important: Change the default password on first login:
passwd
Image Contents
Partition layout:
├── /boot (FAT32, 256MB)
│ ├── kernel8-rt.img PREEMPT_RT kernel (if built)
│ ├── config.txt GPU/boot configuration
│ ├── cmdline.txt Kernel command line
│ └── *.dtb, overlays/ Device Tree Blobs
├── / (ext4, ~4GB used of 6GB)
│ ├── /opt/rpi-mixer/ Mixer source + venv
│ ├── /etc/systemd/system/ Service files
│ └── /home/pi/ pi user home
└── /data (ext4, remaining space)
├── sessions/ Saved mixer sessions
├── recordings/ Multi-track recordings
├── presets/ Plugin presets
└── logs/ System/audio logs
Systemd Services
| Service | Description | Auto-start |
|---|---|---|
cpu-performance.service |
Lock CPU to max frequency | Boot |
jackd.service |
JACK2 real-time audio server | After first-boot |
mixer-api.service |
REST API + WebSocket + OSC server | After first-boot |
mixer-ui.service |
Kivy touchscreen UI | After first-boot |
mixer-firstboot.service |
First-boot setup wizard | First boot only |
# Check service status
systemctl status jackd mixer-api mixer-ui
# View logs
journalctl -u mixer-api -f
journalctl -u jackd -f
# Manual control
sudo systemctl stop mixer-ui mixer-api jackd
sudo systemctl start jackd && sleep 2 && sudo systemctl start mixer-api
CI / Automated Builds
Build without a Raspberry Pi (CI-friendly):
# On any x86_64 Linux host with qemu-user-static
export MIXER_VERSION="$(git describe --tags --always)"
export MIXER_HOSTNAME="ci-mixer"
export MIXER_API_KEY="ci-test-key-12345"
./build/build.sh --skip-kernel
For GitHub Actions / Gitea CI, add a workflow step:
- name: Build SD card image
run: |
sudo apt install -y wget xz-utils dosfstools e2fsprogs qemu-user-static
./build/build.sh --skip-kernel
- name: Upload image artifact
uses: actions/upload-artifact@v4
with:
name: sd-card-image
path: build/out/rpi-audio-mixer-*.img.xz
Customization
Pre-configure WiFi and hostname
MIXER_WIFI_SSID="MyNetwork" MIXER_WIFI_PASS="secret123" \
MIXER_HOSTNAME="stage-mixer" ./build/build.sh --skip-kernel
Custom image size
./build/build.sh --image-size 16384 # 16 GB image
Different Raspberry Pi OS base
RPIOS_URL="https://example.com/my-custom-base.img.xz" ./build/build.sh
Architecture Decision
This project uses Raspberry Pi OS Lite (64-bit, Bookworm) + post-install configuration rather than Buildroot or Yocto. Rationale:
- Full Debian package ecosystem for rapid audio stack iteration
apt installfor JACK, Carla, debugging tools — no image rebuilds- Standard systemd-based system — familiar and well-documented
- PREEMPT_RT kernel built via cross-compilation on x86 host
- Buildroot/Yocto can be revisited for production appliance images
See docs/research/base-os-decision.md for the full analysis.
Troubleshooting
Build fails on "qemu-aarch64-static not found"
sudo apt install qemu-user-static
sudo update-binfmts --enable qemu-aarch64
Build fails on apt in chroot
This usually means DNS isn't resolving inside the chroot. The configure script
uses the host's /etc/resolv.conf — make sure your host has working DNS.
Image won't boot on RPi
- Check you're using a Raspberry Pi 4B (not Pi 3 or Pi 5)
- Verify the SD card is at least 16 GB (Class A2 recommended)
- Try a different power supply (RPi4 needs 5V/3A)
- Check the HDMI display for kernel panic messages
No audio on first boot
- Run
sudo systemctl status jackdto check JACK - Run
aplay -lto list audio devices - Re-run the setup wizard:
sudo touch /force-firstboot && sudo reboot
Web UI not accessible
- Check the service:
sudo systemctl status mixer-api - Find the IP:
ip addr show | grep inet - Check the API key in the service file:
grep API_KEY /etc/systemd/system/mixer-api.service