# 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 ```bash # 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 ```bash # 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 ```bash # 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. ```bash # 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 ```bash # 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](https://www.raspberrypi.com/software/) or [balenaEtcher](https://www.balena.io/etcher/): 1. Decompress the `.img.xz` with 7-Zip or WinRAR 2. Open Raspberry Pi Imager 3. Choose "Use custom" → select the `.img` file 4. Select your SD card and flash ## First Boot 1. Insert the SD card into a Raspberry Pi 4B (4GB+ RAM recommended) 2. Connect your USB audio interface 3. (Optional) Connect HDMI touchscreen + Ethernet 4. 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: ```bash 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 | ```bash # 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): ```bash # 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: ```yaml - 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 ```bash MIXER_WIFI_SSID="MyNetwork" MIXER_WIFI_PASS="secret123" \ MIXER_HOSTNAME="stage-mixer" ./build/build.sh --skip-kernel ``` ### Custom image size ```bash ./build/build.sh --image-size 16384 # 16 GB image ``` ### Different Raspberry Pi OS base ```bash 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 install` for 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](../docs/research/base-os-decision.md) for the full analysis. ## Troubleshooting ### Build fails on "qemu-aarch64-static not found" ```bash 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 jackd` to check JACK - Run `aplay -l` to 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`