e9c504c0d7
Lint & Validate / lint (push) Has been cancelled
Adds complete SD card image builder for the RPi Audio Mixer: - build/build.sh: Main builder (RPiOS Lite base + chroot config + optional RT kernel cross-compile) - build/configure-system.sh: Chroot configuration (packages, services, audio config) - build/first-boot/setup-wizard.sh: Interactive first-boot setup (audio, WiFi, hostname, API key, JACK) - build/README.md: Flashing instructions and build documentation - Updated .gitignore to track build scripts but ignore artifacts - Updated README.md with current project status 735/735 tests pass. Build runs on x86_64 without a Raspberry Pi.
417 lines
12 KiB
Bash
Executable File
417 lines
12 KiB
Bash
Executable File
#!/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 <<WIFIEOF
|
|
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
|
update_config=1
|
|
country=US
|
|
|
|
network={
|
|
ssid="$MIXER_WIFI_SSID"
|
|
psk="$MIXER_WIFI_PASS"
|
|
key_mgmt=WPA-PSK
|
|
}
|
|
WIFIEOF
|
|
chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
|
|
fi
|
|
|
|
# ─── Done ────────────────────────────────────────────────────────────────────
|
|
|
|
# Write version marker
|
|
echo "rpi-audio-mixer $MIXER_VERSION" > /etc/mixer-version
|
|
echo "Installed: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> /etc/mixer-version
|
|
|
|
log "System configuration complete."
|