#!/bin/bash # configure-system.sh — runs inside chroot to configure the RPi Audio Mixer image # Called from build.sh Step 5. set -euo pipefail # ─── Variables (populated by build.sh via env) ─────────────────────────────── MIXER_HOSTNAME="${MIXER_HOSTNAME:-pi-mixer}" MIXER_API_KEY="${MIXER_API_KEY:-mixer-local}" MIXER_WIFI_SSID="${MIXER_WIFI_SSID:-}" MIXER_WIFI_PASS="${MIXER_WIFI_PASS:-}" MIXER_VERSION="${MIXER_VERSION:-dev}" export DEBIAN_FRONTEND=noninteractive log() { echo "[CONFIGURE] $*"; } # ─── 1. Basic system configuration ─────────────────────────────────────────── log "1/8: Configuring hostname and locale..." echo "$MIXER_HOSTNAME" > /etc/hostname sed -i "s/raspberrypi/$MIXER_HOSTNAME/g" /etc/hosts 2>/dev/null || true # Enable required locales sed -i 's/^# en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen 2>/dev/null || true locale-gen en_US.UTF-8 2>/dev/null || true update-locale LANG=en_US.UTF-8 2>/dev/null || true # ─── 2. Update package cache ───────────────────────────────────────────────── log "2/8: Updating package cache..." apt-get update -qq # ─── 3. Install system packages ────────────────────────────────────────────── log "3/8: Installing audio and system packages..." apt-get install -y -qq \ jackd2 \ jackd2-firewire \ libjack-jackd2-0 \ libjack-jackd2-dev \ jack-tools \ a2jmidid \ alsa-utils \ alsa-tools \ pulseaudio \ pulseaudio-module-jack \ rtirq-init \ cpufrequtils \ i2c-tools \ python3 \ python3-pip \ python3-venv \ python3-dev \ python3-numpy \ python3-scipy \ libpython3-dev \ git \ wget \ curl \ vim-tiny \ tmux \ sudo \ ufw \ ntp \ iptables \ usbutils \ pciutils \ net-tools \ wireless-tools \ wpasupplicant \ avahi-daemon \ libusb-1.0-0-dev \ libasound2-dev \ libsndfile1-dev \ liblo-dev \ libfftw3-dev \ libcairo2-dev \ libgirepository1.0-dev \ gir1.2-gtk-3.0 \ xserver-xorg \ x11-xserver-utils \ xinit \ libsdl2-dev \ libsdl2-image-dev \ libsdl2-mixer-dev \ libsdl2-ttf-dev \ mesa-utils \ libgles2-mesa-dev \ pkg-config log " Cleaning apt cache..." apt-get clean rm -rf /var/lib/apt/lists/* # ─── 4. Install Python packages ────────────────────────────────────────────── log "4/8: Installing Python packages..." # Create virtual environment for the mixer python3 -m venv /opt/rpi-mixer/venv --system-site-packages /opt/rpi-mixer/venv/bin/pip install --no-cache-dir --upgrade pip setuptools wheel /opt/rpi-mixer/venv/bin/pip install --no-cache-dir \ fastapi \ uvicorn \ websockets \ python-multipart \ pydantic \ pytest \ pytest-asyncio \ pytest-cov \ python-jack-client \ python-rtmidi \ pyyaml \ aiohttp 2>/dev/null || true # ─── 5. Create audio user/group ────────────────────────────────────────────── log "5/8: Creating 'pi' user and 'audio' group..." # The 'pi' user already exists in RPiOS, add to audio group usermod -a -G audio pi 2>/dev/null || true usermod -a -G input pi 2>/dev/null || true # For evdev keyboard controls usermod -a -G video pi 2>/dev/null || true # For framebuffer/Kivy # Set real-time priorities for audio group cat > /etc/security/limits.d/99-audio.conf <<'EOF' # Realtime audio group limits for low-latency JACK @audio - rtprio 99 @audio - memlock unlimited @audio - nice -20 @audio - priority 99 EOF # ─── 6. Install systemd services ───────────────────────────────────────────── log "6/8: Installing systemd services..." # CPU performance governor cat > /etc/systemd/system/cpu-performance.service <<'EOF' [Unit] Description=Set CPU governor to performance After=multi-user.target [Service] Type=oneshot ExecStart=/bin/sh -c 'for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do [ -f "$cpu" ] && echo performance > "$cpu" || true; done' RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF # JACK2 audio server cat > /etc/systemd/system/jackd.service <<'EOF' [Unit] Description=JACK2 Audio Server After=sound.target network.target cpu-performance.service Wants=sound.target cpu-performance.service [Service] Type=simple User=pi Group=audio Environment=JACK_NO_AUDIO_RESERVATION=1 Environment=JACK_PROMISCUOUS_SERVER=1 LimitRTPRIO=99 LimitMEMLOCK=infinity LimitNICE=-20 CPUAffinity=1-3 IOSchedulingClass=realtime IOSchedulingPriority=0 ExecStart=/usr/bin/jackd -P 70 -t 2000 -d alsa -d hw:USB -r 48000 -p 128 -n 3 -M -X seq -S Restart=on-failure RestartSec=2 [Install] WantedBy=multi-user.target EOF # Mixer API server cat > /etc/systemd/system/mixer-api.service <<'EOF' [Unit] Description=RPi Audio Mixer — Network API Server After=jackd.service network.target Wants=jackd.service Requires=jackd.service [Service] Type=simple User=pi Group=audio WorkingDirectory=/opt/rpi-mixer ExecStart=/opt/rpi-mixer/venv/bin/python3 -m src.network.run --host 0.0.0.0 --port 8080 --api-key MIXER_API_KEY_PLACEHOLDER Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF # Replace API key placeholder sed -i "s/MIXER_API_KEY_PLACEHOLDER/$MIXER_API_KEY/" /etc/systemd/system/mixer-api.service # Mixer Kivy touch UI cat > /etc/systemd/system/mixer-ui.service <<'EOF' [Unit] Description=RPi Audio Mixer — Touchscreen UI After=mixer-api.service Wants=mixer-api.service [Service] Type=simple User=pi Group=audio WorkingDirectory=/opt/rpi-mixer Environment=MIXER_HOST=127.0.0.1 Environment=MIXER_PORT=8080 Environment=MIXER_API_KEY=MIXER_API_KEY_PLACEHOLDER ExecStart=/opt/rpi-mixer/venv/bin/python3 main_touch.py --host 127.0.0.1 --port 8080 --api-key MIXER_API_KEY_PLACEHOLDER Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF sed -i "s/MIXER_API_KEY_PLACEHOLDER/$MIXER_API_KEY/g" /etc/systemd/system/mixer-ui.service # First-boot setup wizard cat > /etc/systemd/system/mixer-firstboot.service <<'EOF' [Unit] Description=RPi Audio Mixer — First-Boot Setup Wizard After=network.target Before=getty@tty1.service ConditionPathExists=/opt/rpi-mixer/build/first-boot/setup-wizard.sh [Service] Type=oneshot ExecStart=/bin/bash /opt/rpi-mixer/build/first-boot/setup-wizard.sh StandardInput=tty StandardOutput=tty TTYPath=/dev/tty1 RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF # ─── 7. Install config files and udev rules ────────────────────────────────── log "7/8: Installing config files..." # udev rule for MIDI devices cp /opt/rpi-mixer/config/99-midi.rules /etc/udev/rules.d/ 2>/dev/null || true # rtirq config cp /opt/rpi-mixer/config/rtirq /etc/default/rtirq 2>/dev/null || true # ALSA config for USB audio cp /opt/rpi-mixer/config/alsa-usb.conf /etc/modprobe.d/ 2>/dev/null || true # USB audio quirks cp /opt/rpi-mixer/config/usb-audio-quirks.conf /etc/modprobe.d/ 2>/dev/null || true # JACK config files mkdir -p /etc/jack cp /opt/rpi-mixer/config/jackdrc /etc/jackdrc 2>/dev/null || true # Carla config files mkdir -p /home/pi/.config/carla cp /opt/rpi-mixer/config/carla-8ch-default.carxp /home/pi/.config/carla/ 2>/dev/null || true cp /opt/rpi-mixer/config/carla-patchbay.xml /home/pi/.config/carla/ 2>/dev/null || true chown -R pi:pi /home/pi/.config/carla 2>/dev/null || true # ─── 8. Enable services and finalize ───────────────────────────────────────── log "8/8: Enabling services..." # Enable boot-time services systemctl enable cpu-performance.service 2>/dev/null || true # Don't enable jackd yet — it needs audio interface detection from first-boot # (the first-boot wizard handles this) # Enable mixer services (will start after first-boot completes) systemctl enable mixer-firstboot.service 2>/dev/null || true # Configure first-boot wizard to auto-disable itself # (runs once, then disables itself) # Make setup-wizard executable chmod +x /opt/rpi-mixer/build/first-boot/setup-wizard.sh 2>/dev/null || true # Create /data directory structure for persistence mkdir -p /data/{sessions,recordings,presets,logs,plugins} chown -R pi:pi /data 2>/dev/null || true # Symlink data directories into pi home ln -sf /data/sessions /home/pi/sessions 2>/dev/null || true ln -sf /data/recordings /home/pi/recordings 2>/dev/null || true ln -sf /data/presets /home/pi/presets 2>/dev/null || true chown -h pi:pi /home/pi/{sessions,recordings,presets} 2>/dev/null || true # Set bashrc with helpful aliases cat >> /home/pi/.bashrc <<'EOF' # ─── RPi Audio Mixer aliases ─── alias mixer-status='systemctl status jackd mixer-api mixer-ui' alias mixer-start='sudo systemctl start jackd && sleep 2 && sudo systemctl start mixer-api' alias mixer-stop='sudo systemctl stop mixer-ui mixer-api jackd' alias mixer-restart='sudo systemctl restart mixer-api' alias mixer-log='journalctl -u mixer-api -f' alias jack-log='journalctl -u jackd -f' alias jack-ports='jack_lsp -c' alias jack-load='jack_cpu_load' alias audio-test='speaker-test -t sine -f 440 -l 0' alias mixer-build='/opt/rpi-mixer/build/build.sh' EOF chown pi:pi /home/pi/.bashrc 2>/dev/null || true # Disable unnecessary services for appliance mode log " Disabling non-essential services..." systemctl disable bluetooth 2>/dev/null || true systemctl disable hciuart 2>/dev/null || true systemctl disable triggerhappy 2>/dev/null || true systemctl disable ModemManager 2>/dev/null || true systemctl mask ModemManager 2>/dev/null || true systemctl disable polkit 2>/dev/null || true systemctl disable dphys-swapfile 2>/dev/null || true # No swap on SD card # Configure NTP cat > /etc/systemd/timesyncd.conf <<'EOF' [Time] NTP=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org FallbackNTP=0.debian.pool.ntp.org EOF # Configure SSH to start on boot systemctl enable ssh 2>/dev/null || true # Enable Avahi for mDNS (pi-mixer.local) systemctl enable avahi-daemon 2>/dev/null || true # ─── 9. Boot config ────────────────────────────────────────────────────────── log "Finalizing boot config..." # config.txt adjustments cat >> /boot/config.txt <<'EOF' # RPi Audio Mixer — audio appliance optimizations # Disable WiFi/BT for reduced latency (enable via setup wizard if needed) dtoverlay=disable-wifi dtoverlay=disable-bt # Fast boot boot_delay=0 disable_splash=1 # GPU memory (minimal — no desktop) gpu_mem=16 # Force HDMI hotplug (for touchscreen) hdmi_force_hotplug=1 # Disable camera auto-detect (we use USB cameras) camera_auto_detect=0 # Enable I2C for GPIO expanders dtparam=i2c_arm=on # Disable audio (we use USB audio interface) dtparam=audio=off EOF # cmdline.txt adjustments # Keep existing and append sed -i 's/console=serial0,115200 //g' /boot/cmdline.txt 2>/dev/null || true echo " quiet loglevel=3 usbcore.autosuspend=-1 fsck.mode=skip" >> /boot/cmdline.txt # ─── 10. WiFi pre-configuration (optional) ─────────────────────────────────── if [[ -n "$MIXER_WIFI_SSID" ]]; then log "Pre-configuring WiFi..." # Remove WiFi disable overlay sed -i 's/^dtoverlay=disable-wifi/#dtoverlay=disable-wifi/' /boot/config.txt 2>/dev/null || true cat > /etc/wpa_supplicant/wpa_supplicant.conf < /etc/mixer-version echo "Installed: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> /etc/mixer-version log "System configuration complete."