P7-R1: Build Script / SD Card Image — automated build system
Lint & Validate / lint (push) Has been cancelled
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.
This commit is contained in:
Executable
+416
@@ -0,0 +1,416 @@
|
||||
#!/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."
|
||||
Reference in New Issue
Block a user