# Build Guide — SD Card Image Builder This guide walks through building a ready-to-flash Raspberry Pi SD card image containing the full audio mixer stack with a PREEMPT_RT kernel. ## Prerequisites ### Host System Requirements - **OS:** Any modern Linux distribution (x86_64) — Ubuntu 22.04+, Debian 12+, Fedora 38+ - **CPU:** 4+ cores recommended (cross-compilation is parallel) - **RAM:** 4GB+ (8GB recommended for full PREEMPT_RT kernel build) - **Disk:** 15GB free space (build artifacts + downloaded OS image) - **Architecture:** x86_64 host for QEMU user-static chroot emulation - **macOS** is supported via a Linux VM or Docker container ### Install Build Dependencies ```bash # Ubuntu / Debian sudo apt update sudo apt install -y \ wget xz-utils \ dosfstools e2fsprogs \ qemu-user-static \ parted \ git # For PREEMPT_RT kernel cross-compilation (optional) sudo apt install -y \ gcc-aarch64-linux-gnu \ build-essential \ flex bison \ libssl-dev \ bc \ rsync # Verify QEMU binary is registered sudo update-binfmts --enable qemu-aarch64 ``` ```bash # Fedora sudo dnf install -y wget xz dosfstools e2fsprogs qemu-user-static parted git ``` ## Build Modes The build script supports three modes, controlled by command-line flags: | Command | Description | Time | Requires | |---------|-------------|------|----------| | `./build/build.sh --skip-kernel` | Quick build, stock RPi kernel | 10-15 min | Basic deps only | | `./build/build.sh` | Full build with PREEMPT_RT kernel | 30-45 min | + gcc-aarch64 + kernel deps | | `./build/build.sh --kernel-only` | Build only the RT kernel .deb | 10-15 min | + gcc-aarch64 + kernel deps | ## Step-by-Step: Quick Build (Recommended First Build) This builds the mixer image using the stock Raspberry Pi OS kernel — fastest way to get a working image for testing. No cross-compilation toolchain needed. ```bash # 1. Clone the repository git clone https://github.com/your-org/rpi-audio-mixer.git cd rpi-audio-mixer # 2. Run the quick build ./build/build.sh --skip-kernel # 3. The compressed image lands here: ls -lh build/out/rpi-audio-mixer-*.img.xz ``` **What happens during the build:** 1. Downloads Raspberry Pi OS Lite (64-bit, Bookworm) base image (~1.1 GB) 2. Extracts and resizes the image (default 8 GB total) 3. Mounts the image partitions via loopback 4. Chroots into the ARM64 filesystem using QEMU user-static 5. Installs audio packages (JACK2, Carla, ALSA tools, Python deps) 6. Copies the mixer source code and systemd service files 7. Configures real-time audio limits, CPU governor, udev rules 8. Sets up first-boot wizard 9. Unmounts and compresses the final image ## Step-by-Step: Full PREEMPT_RT Build The full build includes a custom PREEMPT_RT kernel cross-compiled for aarch64 on your x86_64 host. This is what you want for production use. ```bash # 1. Ensure cross-compilation toolchain is installed sudo apt install -y gcc-aarch64-linux-gnu build-essential flex bison libssl-dev bc # 2. Run the full build ./build/build.sh # This will: # a. Cross-compile linux-rpi-6.12.y with PREEMPT_RT patch # b. Package the kernel as a .deb # c. Build the SD card image with the RT kernel installed # d. Configure boot/config.txt to use the RT kernel # 3. Output ls -lh build/out/rpi-audio-mixer-*.img.xz ``` ### Kernel Cross-Compilation Details The build script: - Clones `raspberrypi/linux` at branch `rpi-6.12.y` - Applies the `PREEMPT_RT` patch for 6.12 - Uses `make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig` - Configures: `CONFIG_PREEMPT_RT=y`, `CONFIG_HZ=1000` - Builds with `make -j$(nproc)` for parallel compilation - Packages into a `.deb` using `make deb-pkg` ## Build Customization ### Image Size The default image is 8 GB. Adjust with `--image-size` (in MB): ```bash ./build/build.sh --skip-kernel --image-size 16384 # 16 GB image ./build/build.sh --skip-kernel --image-size 32768 # 32 GB image ``` ### Pre-configure WiFi Set these environment variables before building: ```bash MIXER_WIFI_SSID="MyNetwork" \ MIXER_WIFI_PASS="secret123" \ ./build/build.sh --skip-kernel ``` ### Custom Hostname ```bash MIXER_HOSTNAME="stage-mixer" ./build/build.sh --skip-kernel ``` ### Custom API Key for CI ```bash MIXER_API_KEY="my-pre-shared-key" ./build/build.sh --skip-kernel ``` ### Custom OS Base Image ```bash RPIOS_URL="https://downloads.raspberrypi.com/raspios_lite_arm64/images/..." \ ./build/build.sh ``` ### Version Tagging ```bash export MIXER_VERSION="$(git describe --tags --always)" ./build/build.sh --skip-kernel ``` ## Cleaning Up ```bash # Remove all build artifacts ./build/build.sh --clean # This deletes: # build/work/ (mounted image, chroot state, temporary files) # build/downloads/ (cached OS image downloads) # build/out/ (built images) # To preserve downloads (they're cached for faster rebuilds): rm -rf build/work/ build/out/ ``` ## Image Contents ``` Partition layout: ├── /boot (FAT32, 256 MB) │ ├── kernel8.img Stock 64-bit kernel (fallback) │ ├── kernel8-rt.img PREEMPT_RT kernel (default if built) │ ├── config.txt GPU, boot, kernel selection │ ├── cmdline.txt Kernel parameters (isolcpus, threadirqs, etc.) │ └── *.dtb, overlays/ Device tree blobs │ ├── / (ext4, ~4 GB used of 6 GB) │ ├── /opt/rpi-mixer/ Mixer Python source + virtualenv │ ├── /etc/systemd/system/ Service unit files │ │ ├── cpu-performance.service │ │ ├── jackd.service │ │ ├── mixer-api.service │ │ ├── mixer-ui.service │ │ └── mixer-firstboot.service │ ├── /etc/security/limits.d/99-audio.conf │ ├── /etc/modprobe.d/alsa-usb.conf │ └── /home/pi/ Default user home │ └── /data (ext4, remaining space) ├── sessions/ Saved mixer session JSON files ├── recordings/ Multi-track WAV recordings ├── presets/ Plugin presets └── logs/ System and audio logs ``` ## Flashing to SD Card ### Linux ```bash # Decompress the image xz -d build/out/rpi-audio-mixer-*.img.xz # Identify your SD card lsblk # Look for /dev/sdb or /dev/mmcblk0 (NOT your system drive!) # Flash the image sudo dd if=build/out/rpi-audio-mixer-*.img \ of=/dev/sdX \ bs=4M \ status=progress \ conv=fsync # Sync and eject sync sudo eject /dev/sdX ``` ### macOS ```bash # Decompress xz -d build/out/rpi-audio-mixer-*.img.xz # Find the SD card diskutil list # Look for /dev/diskX (external, physical) # Unmount diskutil unmountDisk /dev/diskX # Flash (use rdisk for faster writes) sudo dd if=build/out/rpi-audio-mixer-*.img \ of=/dev/rdiskX \ bs=4m \ status=progress # Eject diskutil eject /dev/diskX ``` ### Windows Use [Raspberry Pi Imager](https://www.raspberrypi.com/software/) or [balenaEtcher](https://www.balena.io/etcher/): 1. Decompress the `.img.xz` file with 7-Zip or WinRAR 2. Open Raspberry Pi Imager → "Choose OS" → "Use custom" 3. Select the `.img` file 4. Choose your SD card and flash ## First Boot 1. Insert the SD card into a Raspberry Pi 4B 2. Connect your USB audio interface 3. (Optional) Connect HDMI touchscreen + Ethernet 4. Power on The **first-boot setup wizard** will appear on the HDMI display (or serial console). It walks through: 1. Audio interface detection and selection 2. WiFi network configuration 3. Hostname setting 4. API key generation (for web UI access) 5. JACK buffer size and latency preferences After the wizard completes, the system reboots into normal operation. The wizard auto-disables itself — it only runs once. ### Default Access | Service | URL / Command | Default Credentials | |---------|--------------|---------------------| | 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 immediately: ```bash ssh pi@pi-mixer.local passwd ``` ### Service Management ```bash # Check all mixer services systemctl status jackd mixer-api mixer-ui # View logs journalctl -u mixer-api -f journalctl -u jackd -f # Restart services (must follow order: jackd → mixer-api → mixer-ui) sudo systemctl stop mixer-ui mixer-api jackd sudo systemctl start jackd sleep 2 sudo systemctl start mixer-api mixer-ui ``` ## CI / Automated Builds Build without a Raspberry Pi (CI-friendly) on any x86_64 host: ```bash 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 ``` Example GitHub Actions / Gitea CI workflow: ```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 artifact uses: actions/upload-artifact@v4 with: name: sd-card-image path: build/out/rpi-audio-mixer-*.img.xz ``` ## Kernel-Only Build When you only need a new PREEMPT_RT kernel (e.g., to upgrade an existing image without rebuilding everything): ```bash # Build only the kernel .deb package ./build/build.sh --kernel-only # The .deb lands in build/out/ ls build/out/linux-image-*.deb # Install on the RPi: # scp build/out/linux-image-*.deb pi@pi-mixer.local:/tmp/ # ssh pi@pi-mixer.local # sudo dpkg -i /tmp/linux-image-*.deb # sudo reboot ``` ## Troubleshooting Build Issues | Problem | Solution | |---------|----------| | `qemu-aarch64-static not found` | `sudo apt install qemu-user-static && sudo update-binfmts --enable qemu-aarch64` | | `apt fails in chroot` | DNS not resolving — check host `/etc/resolv.conf` is accessible | | `No space left on device` | Increase image size: `--image-size 16384` | | `kernel build fails` | Install missing deps: `sudo apt install gcc-aarch64-linux-gnu flex bison libssl-dev bc` | | `loop device busy` | Run `sudo losetup -D` to detach stale loop devices | | `permission denied` | Build must run with passwordless sudo or as root | For runtime issues after flashing, see [docs/troubleshooting.md](troubleshooting.md). ## 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 needed - 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](research/base-os-decision.md) for the full analysis.