P1-R3: Initial project infrastructure
- Project directory structure: docs/, src/, build/, recipes/, scripts/, wiki/ - README.md with project overview and status - .editorconfig for consistent code style - .gitignore for Python, C/C++, kernel, and build artifacts - docs/research/: system research report + base OS decision - docs/audio-stack-config.md: audio stack configuration notes - config/: JACK, ALSA, CPU performance configs - Preserved research from P1-R1 and P1-R2 tasks
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yaml,yml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.{sh,bash}]
|
||||||
|
indent_style = tab
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
# Raspberry Pi RT Audio Mixer — .gitignore
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
/build/*
|
||||||
|
!/build/.gitkeep
|
||||||
|
*.egg
|
||||||
|
.eggs/
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# C/C++
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Kernel build artifacts
|
||||||
|
*.ko
|
||||||
|
*.mod.c
|
||||||
|
*.mod
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
.tmp_versions/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Cross-compilation
|
||||||
|
toolchains/
|
||||||
|
sysroot/
|
||||||
|
|
||||||
|
# Package files
|
||||||
|
*.deb
|
||||||
|
*.rpm
|
||||||
|
*.tar.gz
|
||||||
|
*.tar.xz
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Raspberry Pi Real-Time Audio Mixer
|
||||||
|
|
||||||
|
RPi4B-based multi-channel real-time audio mixer with custom PREEMPT_RT kernel.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Platform:** Raspberry Pi 4 Model B (8GB RAM)
|
||||||
|
- **OS:** Raspberry Pi OS Lite (64-bit, Bookworm) + custom PREEMPT_RT kernel
|
||||||
|
- **Audio:** USB audio interface with multi-channel I/O
|
||||||
|
- **Goal:** Low-latency (<10ms round-trip) multi-channel audio mixing
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── docs/ Research, architecture decisions, manuals
|
||||||
|
│ ├── research/ Technical research reports
|
||||||
|
│ ├── decisions/ Architecture Decision Records (ADRs)
|
||||||
|
│ └── manuals/ Hardware/software manuals and references
|
||||||
|
├── src/ Source code (audio engine, DSP, UI)
|
||||||
|
├── build/ Build scripts and cross-compilation tooling
|
||||||
|
├── recipes/ Buildroot/Yocto recipes (if applicable)
|
||||||
|
├── scripts/ Utility and deployment scripts
|
||||||
|
└── wiki/ Local wiki pages
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**Phase 1: Research & Planning (in progress)**
|
||||||
|
- [x] P1-R1: System Research — USB audio, PREEMPT_RT kernel analysis
|
||||||
|
- [x] P1-R2: Base OS Selection — RPiOS Lite 64-bit + custom PREEMPT_RT
|
||||||
|
- [ ] P1-R3: Project Infrastructure (current)
|
||||||
|
- [ ] P2: Kernel Build — custom PREEMPT_RT kernel compilation
|
||||||
|
- [ ] P3: Audio Engine — JACK/PipeWire integration, DSP pipeline
|
||||||
|
- [ ] P4: Hardware Integration — USB interface, GPIO control surface
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
*Documentation coming in Phase 2+*
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
TBD
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Realtime audio group limits
|
||||||
|
# Members of the 'audio' group get realtime scheduling and unlimited memory lock
|
||||||
|
@audio - rtprio 99
|
||||||
|
@audio - memlock unlimited
|
||||||
|
@audio - nice -20
|
||||||
|
@audio - priority 99
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# USB audio buffer tuning
|
||||||
|
# Low-latency USB audio configuration
|
||||||
|
|
||||||
|
# nrpacks=1: disable implicit feedback sync buffering
|
||||||
|
# Reduces USB transfer latency at cost of slightly higher CPU
|
||||||
|
options snd-usb-audio nrpacks=1
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
console=serial0,115200 console=tty1 root=PARTUUID=xxxxxxxx-02 rootfstype=ext4 fsck.repair=yes rootwait isolcpus=1-3 nohz_full=1-3 rcu_nocbs=1-3 threadirqs irqaffinity=0 quiet
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Set CPU governor to performance
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/sh -c 'echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor'
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=JACK2 Audio Server
|
||||||
|
After=sound.target network.target
|
||||||
|
Wants=sound.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=pi
|
||||||
|
Group=audio
|
||||||
|
Environment=JACK_NO_AUDIO_RESERVATION=1
|
||||||
|
Environment=JACK_PROMISCUOUS_SERVER=1
|
||||||
|
|
||||||
|
# Real-time scheduling
|
||||||
|
LimitRTPRIO=99
|
||||||
|
LimitMEMLOCK=infinity
|
||||||
|
LimitNICE=-20
|
||||||
|
|
||||||
|
# CPU affinity — pin JACK server to audio cores 1-3
|
||||||
|
CPUAffinity=1-3
|
||||||
|
|
||||||
|
# I/O scheduling — realtime class
|
||||||
|
IOSchedulingClass=realtime
|
||||||
|
IOSchedulingPriority=0
|
||||||
|
|
||||||
|
# JACK daemon command
|
||||||
|
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 crash (but not on clean shutdown)
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# /etc/jackdrc — system-wide JACK2 default settings
|
||||||
|
/usr/bin/jackd -P 70 -t 2000 -d alsa -d hw:USB -r 48000 -p 128 -n 3 -M -X seq
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# /etc/default/rtirq — realtime IRQ thread priority tuning
|
||||||
|
|
||||||
|
# List of IRQ thread names to boost (space-separated regex patterns)
|
||||||
|
RTIRQ_NAME_LIST="xhci_hcd snd usb"
|
||||||
|
|
||||||
|
# Highest priority for listed threads
|
||||||
|
RTIRQ_PRIO_HIGH=95
|
||||||
|
|
||||||
|
# Decrement priority per subsequent match
|
||||||
|
RTIRQ_PRIO_DECR=1
|
||||||
|
|
||||||
|
# Lowest priority for unlisted threads
|
||||||
|
RTIRQ_PRIO_LOW=50
|
||||||
|
|
||||||
|
# Reset IRQ thread priorities before applying (0=no, 1=yes)
|
||||||
|
RTIRQ_RESET_IRQ=0
|
||||||
@@ -0,0 +1,979 @@
|
|||||||
|
# Audio Stack Configuration — Low-Latency USB Audio on RPi4B
|
||||||
|
|
||||||
|
**Date:** 2026-05-19
|
||||||
|
**Parent Task:** P1-R1 Research + P1-R2 Base OS Decision
|
||||||
|
**Target Platform:** Raspberry Pi 4B, RPi OS Lite 64-bit (Bookworm), PREEMPT_RT 6.12.y kernel
|
||||||
|
**Scope:** JACK2 + ALSA realtime audio stack for multi-channel USB audio interfaces
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Architecture Overview](#1-architecture-overview)
|
||||||
|
2. [Package Installation](#2-package-installation)
|
||||||
|
3. [Kernel Command-Line Tuning](#3-kernel-command-line-tuning)
|
||||||
|
4. [PAM Limits for Realtime Audio](#4-pam-limits-for-realtime-audio)
|
||||||
|
5. [CPU Frequency Governor](#5-cpu-frequency-governor)
|
||||||
|
6. [ALSA Buffer and Period Tuning](#6-alsa-buffer-and-period-tuning)
|
||||||
|
7. [JACK2 Realtime Configuration](#7-jack2-realtime-configuration)
|
||||||
|
8. [Multi-Channel Routing](#8-multi-channel-routing)
|
||||||
|
9. [IRQ Priority Tuning (rtirq)](#9-irq-priority-tuning-rtirq)
|
||||||
|
10. [USB Audio Quirks](#10-usb-audio-quirks)
|
||||||
|
11. [Latency Testing with jack_delay](#11-latency-testing-with-jack_delay)
|
||||||
|
12. [Full Session Startup Script](#12-full-session-startup-script)
|
||||||
|
13. [Verification Checklist](#13-verification-checklist)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ User Space │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │
|
||||||
|
│ │ Ardour / │ │ Carla │ │ Custom DSP (Python) │ │
|
||||||
|
│ │ DAW │ │ Rack │ │ │ │
|
||||||
|
│ └────┬──────┘ └────┬──────┘ └──────────┬───────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └──────────────┼────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌───────▼────────┐ │
|
||||||
|
│ │ JACK2 │ Realtime SCHED_FIFO │
|
||||||
|
│ │ jackd (RT) │ Priority 70-80 │
|
||||||
|
│ └───────┬────────┘ │
|
||||||
|
│ │ ALSA backend (hw:USB) │
|
||||||
|
├──────────────────────┼───────────────────────────────────┤
|
||||||
|
│ ┌───────▼────────┐ │
|
||||||
|
│ Kernel │ snd-usb-audio │ Threaded IRQ (xhci_hcd) │
|
||||||
|
│ Space │ (xhci_hcd) │ IRQ prio 95 (rtirq) │
|
||||||
|
│ └───────┬────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────────────▼────────────┐ │
|
||||||
|
│ │ VIA VL805 xHCI │ │
|
||||||
|
│ │ USB 3.0 Host Ctrl │ │
|
||||||
|
│ └────────────┬───────────┘ │
|
||||||
|
│ │ │
|
||||||
|
└──────────────────────┼────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ USB Audio I/F │
|
||||||
|
│ (class UAC2) │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Core design decisions:**
|
||||||
|
|
||||||
|
| Parameter | Value | Rationale |
|
||||||
|
|-----------|-------|-----------|
|
||||||
|
| Sample rate | 48000 Hz | Standard for pro audio; lower CPU than 96k |
|
||||||
|
| Buffer size | 128 samples | ~2.7ms per period; sweet spot for latency vs xruns |
|
||||||
|
| Periods/buffer | 3 | JACK default; 2 for aggressive, 3 for stability |
|
||||||
|
| JACK priority | 70 (server), 80 (clients) | Below IRQ handlers (95), above normal tasks |
|
||||||
|
| Audio CPU cores | 1-3 | Core 0 left for system/IRQ handling |
|
||||||
|
| JACK backend | ALSA (hw:) | Direct hardware access, no dmix/dsnoop overhead |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Package Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Core audio stack
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y \
|
||||||
|
jackd2 \
|
||||||
|
jackd2-firewire \
|
||||||
|
libjack-jackd2-0 \
|
||||||
|
libjack-jackd2-dev \
|
||||||
|
alsa-utils \
|
||||||
|
alsa-tools \
|
||||||
|
alsa-tools-gui \
|
||||||
|
rtirq-init \
|
||||||
|
rt-tests \
|
||||||
|
cpufrequtils \
|
||||||
|
usbutils \
|
||||||
|
a2jmidid
|
||||||
|
|
||||||
|
# Optional but recommended for development
|
||||||
|
sudo apt install -y \
|
||||||
|
carla \
|
||||||
|
python3-jack-client \
|
||||||
|
python3-pip
|
||||||
|
|
||||||
|
# jack_delay (compile from source for latency measurement)
|
||||||
|
sudo apt install -y build-essential libjack-jackd2-dev libasound2-dev
|
||||||
|
cd /tmp
|
||||||
|
git clone https://github.com/jackaudio/jack_delay.git
|
||||||
|
cd jack_delay
|
||||||
|
make
|
||||||
|
sudo make install # installs to /usr/local/bin/jack_delay
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verify installations:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jackd --version # jackd version 1.9.22+ expected
|
||||||
|
aplay -l # List playback devices
|
||||||
|
arecord -l # List capture devices
|
||||||
|
cat /proc/asound/cards # Kernel view of sound cards
|
||||||
|
cyclictest --version # rt-tests installed
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Kernel Command-Line Tuning
|
||||||
|
|
||||||
|
Edit `/boot/firmware/cmdline.txt` (RPi OS Bookworm) or `/boot/cmdline.txt` (older).
|
||||||
|
|
||||||
|
### Full recommended cmdline.txt
|
||||||
|
|
||||||
|
```
|
||||||
|
console=serial0,115200 console=tty1 root=PARTUUID=xxxxxxxx-02 rootfstype=ext4 fsck.repair=yes rootwait isolcpus=1-3 nohz_full=1-3 rcu_nocbs=1-3 threadirqs irqaffinity=0 quiet
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameter reference
|
||||||
|
|
||||||
|
| Parameter | Value | Effect |
|
||||||
|
|-----------|-------|--------|
|
||||||
|
| `isolcpus=1-3` | CPUs 1-3 | Removes audio cores from scheduler load-balancing |
|
||||||
|
| `nohz_full=1-3` | CPUs 1-3 | Disables periodic timer tick on audio cores (adaptive-ticks) |
|
||||||
|
| `rcu_nocbs=1-3` | CPUs 1-3 | Offloads RCU callbacks from audio cores to CPU 0 |
|
||||||
|
| `threadirqs` | — | Forces all IRQ handlers to run as kernel threads (required for rtirq priority tuning) |
|
||||||
|
| `irqaffinity=0` | CPU 0 | Pins hardware IRQs to core 0 by default |
|
||||||
|
| `quiet` | — | Reduces console spam (optional) |
|
||||||
|
|
||||||
|
### Verification after reboot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check kernel cmdline
|
||||||
|
cat /proc/cmdline
|
||||||
|
|
||||||
|
# Verify CPU isolation
|
||||||
|
cat /sys/devices/system/cpu/isolated # should show 1-3
|
||||||
|
cat /sys/devices/system/cpu/nohz_full # should show 1-3
|
||||||
|
cat /sys/kernel/rcu_expedited # 1 if available
|
||||||
|
|
||||||
|
# Check threaded IRQs
|
||||||
|
ps -eo pid,comm,psr | grep 'irq/' # IRQ threads should run on CPU 0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. PAM Limits for Realtime Audio
|
||||||
|
|
||||||
|
Create `/etc/security/limits.d/99-audio.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Realtime audio group limits
|
||||||
|
# Apply to members of the 'audio' group
|
||||||
|
@audio - rtprio 99
|
||||||
|
@audio - memlock unlimited
|
||||||
|
@audio - nice -20
|
||||||
|
@audio - priority 99
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apply and verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add your user to the audio group
|
||||||
|
sudo usermod -a -G audio $USER
|
||||||
|
|
||||||
|
# Verify current session (requires logout/login)
|
||||||
|
ulimit -r # should show 99 (max realtime priority)
|
||||||
|
ulimit -l # should show unlimited (max locked memory)
|
||||||
|
|
||||||
|
# Check PAM limit configuration
|
||||||
|
sudo chmod 644 /etc/security/limits.d/99-audio.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-time group membership check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
groups | grep audio # must include 'audio'
|
||||||
|
sudo -u nobody jackd -d alsa -d hw:USB 2>&1 | head -1
|
||||||
|
# Should NOT fail with "cannot use real-time scheduling"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. CPU Frequency Governor
|
||||||
|
|
||||||
|
Set `performance` governor to prevent clock scaling from adding latency jitter.
|
||||||
|
|
||||||
|
### One-time set
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set all cores to performance
|
||||||
|
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||||
|
# Each core should output: performance
|
||||||
|
```
|
||||||
|
|
||||||
|
### Persistent via systemd service
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/cpu-performance.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Set CPU governor to performance
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/sh -c 'echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor'
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable cpu-performance.service
|
||||||
|
sudo systemctl start cpu-performance.service
|
||||||
|
sudo systemctl status cpu-performance.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify current frequency
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cpufreq-info | grep -E 'governor|current CPU frequency'
|
||||||
|
# Should show "performance" and max frequency (typically 1.5 GHz for RPi4B)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ALSA Buffer and Period Tuning
|
||||||
|
|
||||||
|
### 6.1 Identify USB audio device
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List ALSA devices
|
||||||
|
aplay -l
|
||||||
|
arecord -l
|
||||||
|
cat /proc/asound/cards
|
||||||
|
|
||||||
|
# Typical output for USB interface:
|
||||||
|
# card 1: CODEC [USB Audio CODEC], device 0: USB Audio [USB Audio]
|
||||||
|
# The hw: designation is hw:1,0 or hw:USB,0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 ALSA device configuration
|
||||||
|
|
||||||
|
Create `/etc/modprobe.d/alsa-usb.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# USB audio buffer tuning
|
||||||
|
# Larger preallocated buffer reduces xruns at cost of ~20MB RAM per device
|
||||||
|
options snd-usb-audio index=0 vid=0x0000 pid=0x0000 nrpacks=1
|
||||||
|
|
||||||
|
# nrpacks=1: disable implicit feedback sync buffering
|
||||||
|
# - Reduces USB transfer latency at cost of slightly higher CPU
|
||||||
|
# - Critical for round-trip latency < 10ms
|
||||||
|
# - Default is 8 (aggressive buffering for reliability)
|
||||||
|
#
|
||||||
|
# index=0: force USB device to be card 0
|
||||||
|
# vid/pid: filter specific device (optional; 0x0000 = match any)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 ALSA buffer confirmation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show current ALSA buffer/period limits
|
||||||
|
cat /proc/asound/card1/pcm0p/sub0/hw_params # playback
|
||||||
|
cat /proc/asound/card1/pcm0c/sub0/hw_params # capture
|
||||||
|
# (card number depends on your interface)
|
||||||
|
|
||||||
|
# Query via alsacap
|
||||||
|
alsacap -d hw:USB
|
||||||
|
|
||||||
|
# JACK will request these values; ALSA exposes caps:
|
||||||
|
# The USB driver typically supports:
|
||||||
|
# - Period size: 16–4096 frames
|
||||||
|
# - Periods: 2–32
|
||||||
|
# - Sample rate: 8000–192000 Hz
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 JACK ALSA backend parameters
|
||||||
|
|
||||||
|
When launching JACK, the key ALSA parameters are:
|
||||||
|
|
||||||
|
| JACK flag | Meaning | Recommended |
|
||||||
|
|-----------|---------|-------------|
|
||||||
|
| `-d alsa` | Use ALSA backend | Required for USB audio |
|
||||||
|
| `-d hw:USB` | Device name | Match your interface |
|
||||||
|
| `-r 48000` | Sample rate | 48000 (or 44100/96000) |
|
||||||
|
| `-p 128` | Frames per period | 128 (~2.67ms at 48k) |
|
||||||
|
| `-n 3` | Periods per buffer | 3 (total buffer = 384 frames / 8ms) |
|
||||||
|
| `-S` | 16-bit samples | Use only if 32-bit causes issues |
|
||||||
|
| `-M` | MIDI sequencer | Enable if using MIDI |
|
||||||
|
| `-X seq` | MIDI driver | ALSA sequencer |
|
||||||
|
|
||||||
|
**Latency math (48 kHz):**
|
||||||
|
- Period latency: `128 / 48000 = 2.67 ms`
|
||||||
|
- Total buffer: `3 × 2.67 = 8.0 ms`
|
||||||
|
- Target round-trip: `8.0 + USB overhead + DSP ≈ 10-12 ms`
|
||||||
|
|
||||||
|
**Aggressive profile** (for direct monitoring rigs):
|
||||||
|
```
|
||||||
|
-p 64 -n 2 # 1.33ms period, 2.67ms buffer — risk of xruns
|
||||||
|
```
|
||||||
|
|
||||||
|
**Safe profile** (for recording with plugins):
|
||||||
|
```
|
||||||
|
-p 256 -n 3 # 5.33ms period, 16ms buffer — stable for DSP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. JACK2 Realtime Configuration
|
||||||
|
|
||||||
|
### 7.1 System-wide JACK config
|
||||||
|
|
||||||
|
Create `/etc/jackdrc` (system default):
|
||||||
|
|
||||||
|
```
|
||||||
|
# /etc/jackdrc — JACK2 system-wide defaults
|
||||||
|
# Paired with PAM limits and RT kernel tuning
|
||||||
|
|
||||||
|
# ALSA backend, USB audio interface (adjust hw:USB to your device)
|
||||||
|
-d alsa -d hw:USB -r 48000 -p 128 -n 3 -M -X seq
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 JACK DBus service (auto-start with systemd)
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/jackd.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=JACK2 Audio Server
|
||||||
|
After=sound.target network.target
|
||||||
|
Wants=sound.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=pi
|
||||||
|
Group=audio
|
||||||
|
Environment=JACK_NO_AUDIO_RESERVATION=1
|
||||||
|
Environment=JACK_PROMISCUOUS_SERVER=1
|
||||||
|
|
||||||
|
# Real-time scheduling
|
||||||
|
LimitRTPRIO=99
|
||||||
|
LimitMEMLOCK=infinity
|
||||||
|
LimitNICE=-20
|
||||||
|
|
||||||
|
# CPU affinity — pin JACK server to audio cores 1-3
|
||||||
|
CPUAffinity=1-3
|
||||||
|
|
||||||
|
# I/O scheduling — realtime class
|
||||||
|
IOSchedulingClass=realtime
|
||||||
|
IOSchedulingPriority=0
|
||||||
|
|
||||||
|
# JACK daemon command
|
||||||
|
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 crash (but not on clean shutdown)
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
**JACK flags explained:**
|
||||||
|
|
||||||
|
| Flag | Value | Purpose |
|
||||||
|
|------|-------|---------|
|
||||||
|
| `-P 70` | Priority 70 | SCHED_FIFO prio for JACK server thread |
|
||||||
|
| `-t 2000` | Timeout 2s | Client timeout (ms) — clients killed if unresponsive |
|
||||||
|
| `-d alsa` | Backend | ALSA direct hardware driver |
|
||||||
|
| `-S` | 16-bit | Force 16-bit samples (lower USB bandwidth, OK for mixer) |
|
||||||
|
|
||||||
|
### 7.3 Per-user JACK config
|
||||||
|
|
||||||
|
Create `~/.jackdrc` (overrides system-wide):
|
||||||
|
|
||||||
|
```
|
||||||
|
# ~/.jackdrc — user-specific JACK config
|
||||||
|
# This file is read by qjackctl and other JACK frontends
|
||||||
|
# Same format as /etc/jackdrc
|
||||||
|
/usr/bin/jackd -P 70 -t 2000 -d alsa -d hw:USB -r 48000 -p 128 -n 3 -M -X seq
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 Real-time scheduling notes
|
||||||
|
|
||||||
|
- JACK server threads run at `SCHED_FIFO` priority 70 (set by `-P 70`)
|
||||||
|
- JACK client callbacks inherit priority 80 (server + 10)
|
||||||
|
- xHCI USB IRQ handler runs at priority 95 (set by rtirq, see §9)
|
||||||
|
- Kernel IRQ handlers > JACK server > JACK clients > normal processes
|
||||||
|
|
||||||
|
**Priority hierarchy:**
|
||||||
|
```
|
||||||
|
95: xHCI USB IRQ (hardware interrupt thread)
|
||||||
|
90: System timer (hrtimer)
|
||||||
|
80: JACK client process callbacks
|
||||||
|
75: JACK watchdog
|
||||||
|
70: JACK server (ALSA backend, engine)
|
||||||
|
50: Other audio IRQs (I2S, GPIO audio)
|
||||||
|
0: All other processes (CFS scheduler)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Multi-Channel Routing
|
||||||
|
|
||||||
|
### 8.1 JACK port naming convention
|
||||||
|
|
||||||
|
For a typical 8-in/8-out USB interface:
|
||||||
|
|
||||||
|
```
|
||||||
|
system:capture_1 → Input channel 1 (left)
|
||||||
|
system:capture_2 → Input channel 2 (right)
|
||||||
|
system:capture_3 → Input channel 3
|
||||||
|
...
|
||||||
|
system:capture_8 → Input channel 8
|
||||||
|
|
||||||
|
system:playback_1 → Output channel 1 (left main)
|
||||||
|
system:playback_2 → Output channel 2 (right main)
|
||||||
|
...
|
||||||
|
system:playback_8 → Output channel 8
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Automatic routing script
|
||||||
|
|
||||||
|
Create `/usr/local/bin/jack-route-default`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Auto-connect JACK system captures to playback (through-patch)
|
||||||
|
# Connect each input to its corresponding output for direct monitoring
|
||||||
|
# Run after JACK is started
|
||||||
|
|
||||||
|
jack_wait -w
|
||||||
|
|
||||||
|
# Get available ports
|
||||||
|
captures=$(jack_lsp -c system | grep capture)
|
||||||
|
playbacks=$(jack_lsp -c system | grep playback)
|
||||||
|
|
||||||
|
# Route capture → playback in pairs
|
||||||
|
# Capture 1 → Playback 1, Capture 2 → Playback 2, etc.
|
||||||
|
for i in $(seq 1 8); do
|
||||||
|
jack_connect "system:capture_${i}" "system:playback_${i}" 2>/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
# Optional: route MIDI through a2jmidid
|
||||||
|
a2j_control start 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod +x /usr/local/bin/jack-route-default
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 Complex routing with jack-plumbing
|
||||||
|
|
||||||
|
Create `~/.jack-plumbing`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# jack-plumbing rules for persistent connections
|
||||||
|
# Format: (connect "source" "destination")
|
||||||
|
# Applied on JACK start and maintained across disconnects
|
||||||
|
|
||||||
|
(connect "system:capture_1" "system:playback_1")
|
||||||
|
(connect "system:capture_2" "system:playback_2")
|
||||||
|
(connect "system:capture_3" "system:playback_3")
|
||||||
|
(connect "system:capture_4" "system:playback_4")
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start plumbing daemon after JACK is running
|
||||||
|
jack-plumbing &
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.4 Patch persistence with jack_snapshot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Save current connections
|
||||||
|
jack_snapshot -s > ~/jack-patch-session.xml
|
||||||
|
|
||||||
|
# Restore connections
|
||||||
|
jack_snapshot -r ~/jack-patch-session.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.5 Carla patchbay (graphical)
|
||||||
|
|
||||||
|
For complex setups, Carla's patchbay can save/restore entire connection graphs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
carla &
|
||||||
|
# File → Save Project → mixer-session.carxp
|
||||||
|
# File → Load Project → mixer-session.carxp
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. IRQ Priority Tuning (rtirq)
|
||||||
|
|
||||||
|
### 9.1 RTIRQ configuration
|
||||||
|
|
||||||
|
Edit `/etc/default/rtirq`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# /etc/default/rtirq — realtime IRQ thread priority tuning
|
||||||
|
# RTIRQ_NAME_LIST: IRQ threads to boost (space-separated regex)
|
||||||
|
RTIRQ_NAME_LIST="xhci_hcd snd usb"
|
||||||
|
|
||||||
|
# RTIRQ_PRIO_HIGH: highest priority for listed threads
|
||||||
|
RTIRQ_PRIO_HIGH=95
|
||||||
|
|
||||||
|
# RTIRQ_PRIO_DECR: decrement per match (so xhci=95, snd=94, usb=93)
|
||||||
|
RTIRQ_PRIO_DECR=1
|
||||||
|
|
||||||
|
# RTIRQ_PRIO_LOW: lowest priority for unlisted threads
|
||||||
|
RTIRQ_PRIO_LOW=50
|
||||||
|
|
||||||
|
# RTIRQ_RESET_IRQ: reset IRQ thread priorities before applying
|
||||||
|
RTIRQ_RESET_IRQ=0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 Enable and start rtirq
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable rtirq
|
||||||
|
sudo systemctl start rtirq
|
||||||
|
sudo systemctl status rtirq
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.3 Verify IRQ priorities
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show all IRQ threads with priorities
|
||||||
|
ps -eo pid,comm,rtprio,psr | grep 'irq/' | sort -k3 -rn
|
||||||
|
|
||||||
|
# Expected output:
|
||||||
|
# PID COMMAND RTPRIO PSR
|
||||||
|
# 123 irq/51-xhci_hcd 95 0
|
||||||
|
# 124 irq/52-snd_usb 94 0
|
||||||
|
# 125 irq/53-usb3 93 0
|
||||||
|
|
||||||
|
# Also check with:
|
||||||
|
rtirq status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. USB Audio Quirks
|
||||||
|
|
||||||
|
### 10.1 Common quirks
|
||||||
|
|
||||||
|
| Interface | Issue | Quirk Parameter |
|
||||||
|
|-----------|-------|-----------------|
|
||||||
|
| Focusrite Scarlett 18i20 (3rd Gen) | Channel routing requires ALSA mixer | `device_setup=1` |
|
||||||
|
| Behringer UMC1820 | Clock source selection | `implicit_fb=1` |
|
||||||
|
| Behringer UMC404HD | Sometimes needs sync mode set | `implicit_fb=1` |
|
||||||
|
| RME Babyface Pro FS | Class-compliant mode | No quirks needed |
|
||||||
|
| Zoom UAC-8 | USB 3.0 bandwith negotiation | `nrpacks=1` |
|
||||||
|
| M-Audio M-Track 8X4 | Sample rate switching | `autoclock=0` |
|
||||||
|
|
||||||
|
### 10.2 Quirks via modprobe
|
||||||
|
|
||||||
|
Create `/etc/modprobe.d/usb-audio-quirks.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Apply USB audio device quirks
|
||||||
|
# General: disable implicit feedback for lower latency
|
||||||
|
options snd-usb-audio nrpacks=1
|
||||||
|
|
||||||
|
# Focusrite Scarlett (vid=1235, pid=8210 for 18i20 3rd Gen)
|
||||||
|
# options snd-usb-audio vid=0x1235 pid=0x8210 device_setup=1
|
||||||
|
|
||||||
|
# Behringer UMC interfaces
|
||||||
|
# options snd-usb-audio vid=0x1397 pid=0x0508 implicit_fb=1
|
||||||
|
|
||||||
|
# Disable ALSA sequencer for pure audio use (saves CPU)
|
||||||
|
# Not needed for most USB audio interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.3 Dynamic quirk application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Reload snd-usb-audio with new parameters
|
||||||
|
sudo modprobe -r snd-usb-audio
|
||||||
|
sudo modprobe snd-usb-audio nrpacks=1
|
||||||
|
|
||||||
|
# Or via sysfs (if supported by kernel)
|
||||||
|
echo 1 > /sys/module/snd_usb_audio/parameters/nrpacks
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.4 Identify USB audio VID/PID
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsusb | grep -i audio
|
||||||
|
# Example: Bus 001 Device 005: ID 1397:0508 BEHRINGER International GmbH UMC1820
|
||||||
|
|
||||||
|
# The vid and pid are the hex values:
|
||||||
|
# vid=0x1397 pid=0x0508
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.5 ALSA USB mixer controls
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all mixer controls for the USB interface
|
||||||
|
amixer -c USB controls
|
||||||
|
|
||||||
|
# Common useful controls:
|
||||||
|
# - "Clock Source" — Internal vs SPDIF sync
|
||||||
|
# - "Sample Rate" — 44100/48000/96000
|
||||||
|
# - "Buffer Size" — if exposed by driver
|
||||||
|
|
||||||
|
# Get current value
|
||||||
|
amixer -c USB get "Clock Source"
|
||||||
|
|
||||||
|
# Set clock source to internal
|
||||||
|
amixer -c USB set "Clock Source" Internal
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Latency Testing with jack_delay
|
||||||
|
|
||||||
|
### 11.1 Hardware loopback setup
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────┐ 3.5mm TRS cable ┌──────────────────────┐
|
||||||
|
│ USB Audio Interface │ output ────► input │ USB Audio Interface │
|
||||||
|
│ Output 1 (L) ├──────────────────────────►│ Input 1 (L) │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ (physical loopback) │ │
|
||||||
|
└──────────────────────┘ └──────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect an output directly to an input using a short TRS/TS patch cable.
|
||||||
|
For balanced interfaces use TRS; for unbalanced use TS.
|
||||||
|
**Keep the cable as short as possible** (< 30cm) so cable delay doesn't skew measurement.
|
||||||
|
|
||||||
|
### 11.2 Run jack_delay
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# jack_delay latency measurement script
|
||||||
|
# Requires JACK running with appropriate buffer settings
|
||||||
|
|
||||||
|
# 1. Start JACK (if not running)
|
||||||
|
if ! jack_control status | grep -q "started"; then
|
||||||
|
echo "Starting JACK..."
|
||||||
|
jackd -P70 -t2000 -d alsa -d hw:USB -r48000 -p128 -n3 &
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Create physical loopback: connect output 1 → input 1 with patch cable
|
||||||
|
|
||||||
|
# 3. Run jack_delay measurement
|
||||||
|
# jack_delay uses an MLS (Maximum Length Sequence) to measure
|
||||||
|
# the round-trip delay including:
|
||||||
|
# - JACK output buffer
|
||||||
|
# - USB output transfer
|
||||||
|
# - DAC conversion
|
||||||
|
# - Cable propagation
|
||||||
|
# - ADC conversion
|
||||||
|
# - USB input transfer
|
||||||
|
# - JACK input buffer
|
||||||
|
|
||||||
|
jack_delay -O system:playback_1 -I system:capture_1 -c 128
|
||||||
|
|
||||||
|
# Sample output:
|
||||||
|
# 459.853 frames 9.580 ms
|
||||||
|
#
|
||||||
|
# The reported delay is the total round-trip in frames and milliseconds.
|
||||||
|
# Subtract cable + converter latency (~0.5-1.0ms for typical interfaces)
|
||||||
|
# to get the pure software+USB stack latency.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.3 Latency test script
|
||||||
|
|
||||||
|
Create `/usr/local/bin/jack-latency-test`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# jack-latency-test — measure USB audio round-trip latency at various buffer sizes
|
||||||
|
# Usage: jack-latency-test [device] [sample_rate]
|
||||||
|
|
||||||
|
DEVICE="${1:-hw:USB}"
|
||||||
|
RATE="${2:-48000}"
|
||||||
|
OUTPUT="system:playback_1"
|
||||||
|
INPUT="system:capture_1"
|
||||||
|
RESULT_FILE="/tmp/jack-latency-results.txt"
|
||||||
|
|
||||||
|
echo "=== JACK Latency Test ==="
|
||||||
|
echo "Device: $DEVICE"
|
||||||
|
echo "Sample rate: $RATE"
|
||||||
|
echo "Output port: $OUTPUT"
|
||||||
|
echo "Input port: $INPUT"
|
||||||
|
echo "Ensure physical loopback cable is connected: Output 1 → Input 1"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test various buffer sizes
|
||||||
|
for PERIOD in 32 64 128 256 512 1024; do
|
||||||
|
echo -n "Period $PERIOD frames: "
|
||||||
|
|
||||||
|
# Kill any running JACK
|
||||||
|
killall -9 jackd 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Start JACK with this period
|
||||||
|
jackd -P70 -t2000 -d alsa -d "$DEVICE" -r "$RATE" -p "$PERIOD" -n 3 -S \
|
||||||
|
> /tmp/jackd.log 2>&1 &
|
||||||
|
|
||||||
|
# Wait for JACK to be ready
|
||||||
|
timeout=10
|
||||||
|
while [ $timeout -gt 0 ]; do
|
||||||
|
if jack_control status 2>/dev/null | grep -q "started"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
timeout=$((timeout - 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $timeout -eq 0 ]; then
|
||||||
|
echo "JACK failed to start"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Run jack_delay measurement
|
||||||
|
RESULT=$(jack_delay -O "$OUTPUT" -I "$INPUT" -c 128 2>/dev/null | tail -1)
|
||||||
|
if [ -n "$RESULT" ]; then
|
||||||
|
echo "$RESULT"
|
||||||
|
echo "$PERIOD frames: $RESULT" >> "$RESULT_FILE"
|
||||||
|
else
|
||||||
|
echo "measurement failed (check loopback cable)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
killall -9 jackd jack_delay 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Results saved to: $RESULT_FILE"
|
||||||
|
cat "$RESULT_FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod +x /usr/local/bin/jack-latency-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.4 Expected latency targets
|
||||||
|
|
||||||
|
| Buffer Size (frames) | Period Latency (48kHz) | Target Round-Trip | Use Case |
|
||||||
|
|----------------------|----------------------|--------------------|----------|
|
||||||
|
| 32 | 0.67 ms | 3-5 ms | Direct monitoring (very aggressive — may xrun) |
|
||||||
|
| 64 | 1.33 ms | 5-8 ms | Live performance, real-time effects |
|
||||||
|
| **128** | **2.67 ms** | **8-12 ms** | **Pro audio mixer (target)** |
|
||||||
|
| 256 | 5.33 ms | 15-20 ms | Recording, DAW playback |
|
||||||
|
| 512 | 10.67 ms | 25-35 ms | Overdubs, non-critical monitoring |
|
||||||
|
|
||||||
|
If round-trip exceeds 15ms at 128 frames, investigate:
|
||||||
|
1. USB cable quality / length (< 2m)
|
||||||
|
2. Power delivery to interface (use powered hub)
|
||||||
|
3. Other USB devices on same bus
|
||||||
|
4. CPU governor not set to performance
|
||||||
|
5. IRQ priorities not applied
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Full Session Startup Script
|
||||||
|
|
||||||
|
Create `/usr/local/bin/audio-stack-start`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# audio-stack-start — initialize the full low-latency audio environment
|
||||||
|
# Run as root (sudo) for system-level tuning, then launches JACK as user
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Audio Stack Initialization ==="
|
||||||
|
|
||||||
|
# 1. Set CPU governor to performance
|
||||||
|
echo "1/6 Setting CPU governor → performance..."
|
||||||
|
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
||||||
|
echo performance > "$cpu" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# 2. Enable threaded IRQs and apply IRQ priorities
|
||||||
|
echo "2/6 Configuring IRQ priorities..."
|
||||||
|
if systemctl is-active --quiet rtirq; then
|
||||||
|
systemctl restart rtirq
|
||||||
|
else
|
||||||
|
systemctl start rtirq
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Verify PAM limits
|
||||||
|
echo "3/6 Checking PAM limits..."
|
||||||
|
CURRENT_RTPRIO=$(ulimit -r)
|
||||||
|
CURRENT_MEMLOCK=$(ulimit -l)
|
||||||
|
echo " rtprio: $CURRENT_RTPRIO (need 99)"
|
||||||
|
echo " memlock: $CURRENT_MEMLOCK (need unlimited)"
|
||||||
|
|
||||||
|
# 4. Load USB audio module with low-latency params
|
||||||
|
echo "4/6 Configuring USB audio driver..."
|
||||||
|
if lsmod | grep -q snd_usb_audio; then
|
||||||
|
echo " snd-usb-audio already loaded"
|
||||||
|
else
|
||||||
|
modprobe snd-usb-audio nrpacks=1
|
||||||
|
echo " snd-usb-audio loaded with nrpacks=1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Verify USB audio device present
|
||||||
|
echo "5/6 Checking USB audio device..."
|
||||||
|
if aplay -l | grep -qi usb; then
|
||||||
|
echo " USB audio interface detected"
|
||||||
|
else
|
||||||
|
echo " WARNING: No USB audio interface found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Start JACK
|
||||||
|
echo "6/6 Starting JACK2..."
|
||||||
|
if pgrep -x jackd > /dev/null; then
|
||||||
|
echo " JACK already running"
|
||||||
|
else
|
||||||
|
# Drop to pi user for JACK if running as root
|
||||||
|
if [ "$EUID" -eq 0 ] && id pi >/dev/null 2>&1; then
|
||||||
|
sudo -u pi jackd -P70 -t2000 -d alsa -d hw:USB -r48000 -p128 -n3 -S &
|
||||||
|
else
|
||||||
|
jackd -P70 -t2000 -d alsa -d hw:USB -r48000 -p128 -n3 -S &
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
echo " JACK started"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Audio Stack Ready ==="
|
||||||
|
echo "Check: jack_lsp -c system"
|
||||||
|
echo "Check: jack_delay -O system:playback_1 -I system:capture_1"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod +x /usr/local/bin/audio-stack-start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop script
|
||||||
|
|
||||||
|
Create `/usr/local/bin/audio-stack-stop`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Gracefully stop the audio stack
|
||||||
|
killall -15 jackd 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
killall -9 jackd 2>/dev/null || true
|
||||||
|
echo "Audio stack stopped"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod +x /usr/local/bin/audio-stack-stop
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Verification Checklist
|
||||||
|
|
||||||
|
Run through this checklist after deploying the configuration on the target Raspberry Pi.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- [ ] RPi OS Lite 64-bit (Bookworm) installed
|
||||||
|
- [ ] PREEMPT_RT kernel running (`uname -v | grep PREEMPT_RT`)
|
||||||
|
- [ ] USB audio interface connected and powered
|
||||||
|
- [ ] User is member of `audio` group
|
||||||
|
- [ ] Loopback cable available (output → input)
|
||||||
|
|
||||||
|
### Kernel
|
||||||
|
|
||||||
|
- [ ] `cat /proc/cmdline` shows: `isolcpus=1-3 nohz_full=1-3 rcu_nocbs=1-3 threadirqs`
|
||||||
|
- [ ] `cat /sys/devices/system/cpu/isolated` shows `1-3`
|
||||||
|
- [ ] `cat /sys/kernel/realtime` returns `1`
|
||||||
|
|
||||||
|
### CPU
|
||||||
|
|
||||||
|
- [ ] `cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor` all show `performance`
|
||||||
|
- [ ] `cpufreq-info | grep "current CPU frequency"` shows max speed (~1.5 GHz)
|
||||||
|
|
||||||
|
### Limits
|
||||||
|
|
||||||
|
- [ ] `ulimit -r` returns `99`
|
||||||
|
- [ ] `ulimit -l` returns `unlimited`
|
||||||
|
|
||||||
|
### USB Audio
|
||||||
|
|
||||||
|
- [ ] `aplay -l` lists the USB interface
|
||||||
|
- [ ] `arecord -l` lists the USB interface
|
||||||
|
- [ ] `cat /proc/asound/cards` shows the USB card
|
||||||
|
|
||||||
|
### IRQ
|
||||||
|
|
||||||
|
- [ ] `ps -eo pid,comm,rtprio | grep "irq/.*xhci"` shows priority ≥ 95
|
||||||
|
- [ ] `rtirq status` shows correct priorities
|
||||||
|
|
||||||
|
### JACK
|
||||||
|
|
||||||
|
- [ ] `jackd -P70 -t2000 -d alsa -d hw:USB -r48000 -p128 -n3 &` starts without errors
|
||||||
|
- [ ] `jack_lsp` shows `system:capture_*` and `system:playback_*` ports
|
||||||
|
- [ ] `jack_connect system:capture_1 system:playback_1` succeeds
|
||||||
|
|
||||||
|
### Latency
|
||||||
|
|
||||||
|
- [ ] Loopback cable connected: output 1 → input 1
|
||||||
|
- [ ] `jack_delay -O system:playback_1 -I system:capture_1` returns result
|
||||||
|
- [ ] Round-trip latency at 128 frames ≤ 12ms
|
||||||
|
- [ ] No xruns during 60-second latency measurement
|
||||||
|
|
||||||
|
### Stress test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run latency test while loading CPU
|
||||||
|
stress-ng --cpu 4 --timeout 60s &
|
||||||
|
jack_delay -O system:playback_1 -I system:capture_1 -s 60
|
||||||
|
# Round-trip should stay within ±2ms of idle measurement
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Files Reference
|
||||||
|
|
||||||
|
| File | Location | Purpose |
|
||||||
|
|------|----------|---------|
|
||||||
|
| [cmdline.txt](./config/cmdline.txt) | `/boot/firmware/cmdline.txt` | Kernel boot parameters |
|
||||||
|
| [99-audio.conf](./config/99-audio.conf) | `/etc/security/limits.d/99-audio.conf` | PAM realtime limits |
|
||||||
|
| [cpu-performance.service](./config/cpu-performance.service) | `/etc/systemd/system/` | CPU governor systemd unit |
|
||||||
|
| [jackd.service](./config/jackd.service) | `/etc/systemd/system/` | JACK2 systemd service |
|
||||||
|
| [jackdrc](./config/jackdrc) | `/etc/jackdrc` | JACK server defaults |
|
||||||
|
| [alsa-usb.conf](./config/alsa-usb.conf) | `/etc/modprobe.d/` | ALSA USB buffer tuning |
|
||||||
|
| [rtirq](./config/rtirq) | `/etc/default/rtirq` | IRQ priority config |
|
||||||
|
| [jack-route-default](./scripts/jack-route-default) | `/usr/local/bin/` | Auto-routing script |
|
||||||
|
| [jack-latency-test](./scripts/jack-latency-test) | `/usr/local/bin/` | Latency measurement script |
|
||||||
|
| [audio-stack-start](./scripts/audio-stack-start) | `/usr/local/bin/` | Full stack startup |
|
||||||
|
| [audio-stack-stop](./scripts/audio-stack-stop) | `/usr/local/bin/` | Full stack shutdown |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [JACK Audio Connection Kit — Linux RT Configuration](https://jackaudio.org/faq/linux_rt_config.html)
|
||||||
|
- [ALSA USB Audio — Kernel Documentation](https://www.kernel.org/doc/html/latest/sound/alsa-configuration.html)
|
||||||
|
- [Linux RT Kernel — Red Hat Documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_for_real_time/)
|
||||||
|
- [Raspberry Pi Audio — PiSound Documentation](https://blokas.io/pisound/docs/)
|
||||||
|
- Project Research Report: `docs/research/research-report.md`
|
||||||
|
- Base OS Decision: `docs/research/base-os-decision.md`
|
||||||
@@ -0,0 +1,622 @@
|
|||||||
|
# Base OS Selection — Buildroot/Yocto/RPiOS Lite + RT Kernel
|
||||||
|
|
||||||
|
**Date:** 2026-05-19
|
||||||
|
**Status:** Approved
|
||||||
|
**Parent:** P1-R1 (System Research Report)
|
||||||
|
**Next:** P2 (Kernel Build)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
**Decision: Raspberry Pi OS Lite (64-bit, Bookworm) + custom PREEMPT_RT kernel.**
|
||||||
|
|
||||||
|
RPiOS Lite is selected over Buildroot and Yocto for this project. It provides the
|
||||||
|
Debian/bookworm ecosystem for rapid audio-stack iteration, active Foundation
|
||||||
|
maintenance, and straightforward custom kernel integration. Buildroot and Yocto
|
||||||
|
are evaluated below and rejected for reasons of development velocity (Buildroot
|
||||||
|
requires full-image rebuild per package change) and disproportionate complexity
|
||||||
|
(Yocto's ~50GB build footprint and steep learning curve are unjustified for a
|
||||||
|
single-purpose audio appliance with one developer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Build System Comparison
|
||||||
|
|
||||||
|
### 1.1 Buildroot
|
||||||
|
|
||||||
|
| Aspect | Assessment |
|
||||||
|
|--------|-----------|
|
||||||
|
| **Version** | 2024.02+ (rolling releases every 3 months) |
|
||||||
|
| **RPi4 64-bit support** | First-class: `raspberrypi4_64_defconfig` |
|
||||||
|
| **Build time (clean)** | ~30-45 min on modern x86 (8-core) |
|
||||||
|
| **Build output** | ~120MB EXT4 rootfs image + FAT32 boot partition |
|
||||||
|
| **Init system** | BusyBox init (default) or systemd (optional) |
|
||||||
|
| **Cross-compilation** | Bootlin external toolchain (aarch64 glibc stable) |
|
||||||
|
| **Package format** | None at runtime — every change requires image rebuild |
|
||||||
|
| **Kernel config** | `BR2_LINUX_KERNEL_CUSTOM_TARBALL` → RPi kernel tarball |
|
||||||
|
| **PREEMPT_RT support** | Via kernel config fragment overlay (`BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES`) |
|
||||||
|
| **Read-only rootfs** | SquashFS built-in (`BR2_TARGET_ROOTFS_SQUASHFS`), initramfs/OverlayFS support |
|
||||||
|
|
||||||
|
**Verdict for this project: Rejected.**
|
||||||
|
|
||||||
|
Buildroot excels at producing minimal, reproducible appliance images. However,
|
||||||
|
the "rebuild image to change a package" model is a poor fit for audio development
|
||||||
|
where you need to iterate on JACK configs, test different DSP toolchains, and
|
||||||
|
install debugging tools rapidly. The 30-min rebuild cycle per package tweak
|
||||||
|
would severely hamper velocity. Buildroot is best for the "final appliance image"
|
||||||
|
stage — it could be revisited for production deployment after development
|
||||||
|
stabilizes on RPiOS Lite.
|
||||||
|
|
||||||
|
### 1.2 Yocto Project / OpenEmbedded
|
||||||
|
|
||||||
|
| Aspect | Assessment |
|
||||||
|
|--------|-----------|
|
||||||
|
| **Version** | Scarthgap 5.0 (LTS, April 2024) or Styhead 5.1 |
|
||||||
|
| **RPi4 64-bit support** | First-class via `meta-raspberrypi` layer (maintained by Andrei Gherzan) |
|
||||||
|
| **Build time (clean)** | ~2-4 hours first build; ~15-30 min incremental |
|
||||||
|
| **Build footprint** | ~50 GB disk for build directory |
|
||||||
|
| **Init system** | systemd (default in poky) |
|
||||||
|
| **Cross-compilation** | Built-in toolchain generation (cross + native + nativesdk) |
|
||||||
|
| **Package format** | opkg, rpm, or deb at runtime |
|
||||||
|
| **Kernel config** | `linux-raspberrypi` recipe in meta-raspberrypi; config fragments via `.bbappend` |
|
||||||
|
| **PREEMPT_RT support** | Via kernel config fragment in recipe append |
|
||||||
|
| **Read-only rootfs** | `IMAGE_FEATURES += "read-only-rootfs"` built-in |
|
||||||
|
|
||||||
|
**Verdict for this project: Rejected.**
|
||||||
|
|
||||||
|
Yocto is the industry standard for complex embedded Linux products where
|
||||||
|
reproducibility, long-term maintenance, and multi-target builds matter. However,
|
||||||
|
for a single-developer audio project targeting one board (RPi4B), the overhead is
|
||||||
|
disproportionate: 50GB build disk, 2-4 hour clean build, steep BitBake/recipe
|
||||||
|
learning curve, and meta-layer dependency management. The project has no need for
|
||||||
|
Yocto's multi-machine, multi-distro, SDK-generation capabilities. Revisit Yocto
|
||||||
|
only if this project scales to a product line with multiple hardware targets.
|
||||||
|
|
||||||
|
### 1.3 Raspberry Pi OS Lite (64-bit, Bookworm)
|
||||||
|
|
||||||
|
| Aspect | Assessment |
|
||||||
|
|--------|-----------|
|
||||||
|
| **Version** | Bookworm (Debian 12), kernel 6.6 or 6.12 (rpi-update) |
|
||||||
|
| **Architecture** | aarch64 (64-bit ARMv8-A) |
|
||||||
|
| **Install size** | ~3 GB (Lite, no desktop) |
|
||||||
|
| **RAM usage (idle)** | ~150 MB |
|
||||||
|
| **Init system** | systemd |
|
||||||
|
| **Package format** | .deb via apt — full Debian repositories |
|
||||||
|
| **Kernel source** | `github.com/raspberrypi/linux` — `rpi-6.12.y` branch |
|
||||||
|
| **PREEMPT_RT support** | Not in stock kernel; build custom kernel with `CONFIG_PREEMPT_RT=y` |
|
||||||
|
| **Read-only rootfs** | Manual setup via OverlayFS or fstab `ro` + tmpfs mounts |
|
||||||
|
| **Cross-compilation** | `aarch64-linux-gnu-` toolchain (available via apt on Ubuntu/Debian host) |
|
||||||
|
| **Maintenance** | Active — Raspberry Pi Foundation + Debian upstream |
|
||||||
|
|
||||||
|
**Verdict for this project: Selected.**
|
||||||
|
|
||||||
|
RPiOS Lite offers the right balance:
|
||||||
|
- **Development speed**: `apt install` for JACK, ALSA tools, debugging — minutes, not rebuilds
|
||||||
|
- **Ecosystem**: Full Debian Bookworm repository with up-to-date audio packages
|
||||||
|
- **Familiarity**: Standard Linux systemd-based system — no proprietary init or build system
|
||||||
|
- **Custom kernel**: Build once, drop kernel8.img onto /boot — simple and well-documented
|
||||||
|
- **Production path**: Can later repackage the working RPiOS setup into a Buildroot image if appliance-style deployment is desired
|
||||||
|
|
||||||
|
The 3GB install size and 150MB idle RAM are acceptable for an 8GB RPi4B. If
|
||||||
|
size reduction becomes important later, the system can be trimmed (remove
|
||||||
|
unnecessary packages, switch to read-only squashfs).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. PREEMPT_RT Kernel Integration
|
||||||
|
|
||||||
|
### 2.1 Source Tree
|
||||||
|
|
||||||
|
The Raspberry Pi Foundation maintains a downstream kernel at:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://github.com/raspberrypi/linux
|
||||||
|
Branch: rpi-6.12.y (64-bit, kernel8 build)
|
||||||
|
Defconfig: bcm2711_defconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
As of May 2026, the 6.12.y RT patchset is mostly upstream. Only
|
||||||
|
`CONFIG_PREEMPT_RT=y` needs to be enabled on top of `bcm2711_defconfig`.
|
||||||
|
|
||||||
|
### 2.2 Build Method: Cross-Compilation on x86 Host
|
||||||
|
|
||||||
|
**Recommended approach** — build the kernel on a fast x86 machine, then copy
|
||||||
|
the resulting kernel image and DTBs to the Pi's /boot partition.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Toolchain (Ubuntu 26.04)
|
||||||
|
sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
||||||
|
|
||||||
|
# Clone RPi kernel
|
||||||
|
git clone --depth=1 -b rpi-6.12.y \
|
||||||
|
https://github.com/raspberrypi/linux.git linux-rpi
|
||||||
|
cd linux-rpi
|
||||||
|
|
||||||
|
# Apply config
|
||||||
|
KERNEL=kernel8
|
||||||
|
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
|
||||||
|
|
||||||
|
# Enable PREEMPT_RT
|
||||||
|
scripts/config -e CONFIG_PREEMPT_RT
|
||||||
|
scripts/config -d CONFIG_PREEMPT
|
||||||
|
scripts/config -e CONFIG_HIGH_RES_TIMERS
|
||||||
|
scripts/config -e CONFIG_HZ_1000
|
||||||
|
scripts/config -e CONFIG_NO_HZ_FULL
|
||||||
|
scripts/config -e CONFIG_RCU_NOCB_CPU
|
||||||
|
scripts/config -e CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE
|
||||||
|
scripts/config --set-val CONFIG_HZ 1000
|
||||||
|
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig
|
||||||
|
|
||||||
|
# Build (adjust -j for your CPU count)
|
||||||
|
make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
|
||||||
|
Image modules dtbs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Kernel Installation on RPi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy kernel
|
||||||
|
scp arch/arm64/boot/Image root@<pi-ip>:/boot/kernel8-rt.img
|
||||||
|
|
||||||
|
# Copy DTBs
|
||||||
|
scp arch/arm64/boot/dts/broadcom/*.dtb root@<pi-ip>:/boot/
|
||||||
|
|
||||||
|
# Install modules
|
||||||
|
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
|
||||||
|
INSTALL_MOD_PATH=./modules modules_install
|
||||||
|
scp -r modules/lib/modules/* root@<pi-ip>:/lib/modules/
|
||||||
|
|
||||||
|
# On the Pi, update config.txt:
|
||||||
|
# kernel=kernel8-rt.img
|
||||||
|
# Add cmdline.txt: threadirqs nohz_full=1-3 rcu_nocbs=1-3 isolcpus=1-3
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Kernel Configuration Checklist
|
||||||
|
|
||||||
|
| Config | Value | Rationale |
|
||||||
|
|--------|-------|-----------|
|
||||||
|
| `CONFIG_PREEMPT_RT` | y | Full real-time preemption |
|
||||||
|
| `CONFIG_PREEMPT` | n | Disable voluntary-only preemption |
|
||||||
|
| `CONFIG_HZ` | 1000 | 1ms timer tick for lower scheduling jitter |
|
||||||
|
| `CONFIG_HZ_1000` | y | Required for HZ=1000 |
|
||||||
|
| `CONFIG_HIGH_RES_TIMERS` | y | High-resolution timer subsystem |
|
||||||
|
| `CONFIG_NO_HZ_FULL` | y | Tickless operation on isolated CPUs |
|
||||||
|
| `CONFIG_RCU_NOCB_CPU` | y | Offload RCU callbacks from RT CPUs |
|
||||||
|
| `CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE` | y | Lock CPU at max frequency |
|
||||||
|
| `CONFIG_CPU_FREQ_GOV_PERFORMANCE` | y | Performance governor available |
|
||||||
|
| `CONFIG_IRQ_FORCED_THREADING` | y | Force threaded IRQ handlers |
|
||||||
|
| `CONFIG_PREEMPTIRQ_DELAY_TEST` | n | Debug — disable for production |
|
||||||
|
| `CONFIG_USB_XHCI_HCD` | y | USB 3.0 host controller (required) |
|
||||||
|
| `CONFIG_SND_USB_AUDIO` | y | USB Audio Class driver (build as module preferred) |
|
||||||
|
|
||||||
|
### 2.5 Alternative: Native Build on RPi4B
|
||||||
|
|
||||||
|
Building natively on the RPi4B itself is possible but slow (~2-3 hours for a
|
||||||
|
full kernel build). Cross-compilation on a modern x86 machine completes in
|
||||||
|
~10-15 minutes. **Recommendation:** Cross-compile for development iterations,
|
||||||
|
fall back to native build on the Pi only if cross-compilation toolchain issues
|
||||||
|
arise.
|
||||||
|
|
||||||
|
### 2.6 Kernel Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify RT preemption is active
|
||||||
|
uname -v | grep PREEMPT_RT
|
||||||
|
cat /sys/kernel/realtime # should return "1"
|
||||||
|
|
||||||
|
# Measure worst-case latency
|
||||||
|
sudo cyclictest -t 4 -p 99 -i 200 -n -l 100000 -q
|
||||||
|
# Target: max latency < 100µs on idle system
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Boot Time Optimization
|
||||||
|
|
||||||
|
### 3.1 Current Baseline
|
||||||
|
|
||||||
|
A stock RPiOS Lite 64-bit boots in approximately:
|
||||||
|
- **Firmware/Bootloader:** ~3-5 seconds
|
||||||
|
- **Kernel + initrd:** ~5-8 seconds
|
||||||
|
- **Userspace (systemd):** ~10-15 seconds
|
||||||
|
- **Total:** ~18-28 seconds to login prompt
|
||||||
|
|
||||||
|
Measured with `systemd-analyze`:
|
||||||
|
```bash
|
||||||
|
systemd-analyze # overall boot time
|
||||||
|
systemd-analyze blame # per-unit timing
|
||||||
|
systemd-analyze critical-chain # dependency chain
|
||||||
|
systemd-analyze plot > boot.svg # visual timeline
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Optimization Targets
|
||||||
|
|
||||||
|
| Target | Approach | Expected Saving |
|
||||||
|
|--------|----------|-----------------|
|
||||||
|
| **Disable WiFi/BT** | `dtoverlay=disable-wifi`, `dtoverlay=disable-bt` in config.txt | ~2-3s |
|
||||||
|
| **Reduce kernel modules** | Disable unused drivers (HDMI audio, camera, codecs) | ~1-2s |
|
||||||
|
| **Minimal initramfs** | Build custom initramfs with only essential modules (usb, ext4, xhci) | ~2-4s |
|
||||||
|
| **Disable unnecessary services** | `systemctl disable` bluetooth, avahi, triggerhappy, etc. | ~3-5s |
|
||||||
|
| **Quiet boot** | `quiet loglevel=3` on kernel cmdline | ~1-2s (fewer console writes) |
|
||||||
|
| **Disable swap wait** | Remove swapfile or reduce swap timeout | ~0-5s |
|
||||||
|
| **Fast boot in config.txt** | `boot_delay=0`, `disable_splash=1` | ~0.5-1s |
|
||||||
|
|
||||||
|
**Target: Sub-15-second boot** (from power-on to JACK running).
|
||||||
|
|
||||||
|
### 3.3 Services to Disable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Audio appliance — disable non-essential services
|
||||||
|
sudo systemctl disable bluetooth
|
||||||
|
sudo systemctl disable hciuart
|
||||||
|
sudo systemctl disable avahi-daemon
|
||||||
|
sudo systemctl disable triggerhappy
|
||||||
|
sudo systemctl disable ModemManager
|
||||||
|
sudo systemctl disable polkit
|
||||||
|
sudo systemctl mask systemd-random-seed.service # if using read-only root
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Kernel Command Line
|
||||||
|
|
||||||
|
```
|
||||||
|
console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait
|
||||||
|
quiet loglevel=3
|
||||||
|
threadirqs nohz_full=1-3 rcu_nocbs=1-3 isolcpus=1-3
|
||||||
|
usbcore.autosuspend=-1
|
||||||
|
fsck.mode=skip
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Purpose |
|
||||||
|
|-----------|---------|
|
||||||
|
| `quiet loglevel=3` | Suppress verbose kernel messages |
|
||||||
|
| `threadirqs` | Force threaded IRQ handlers (RT audio) |
|
||||||
|
| `nohz_full=1-3` | Disable timer ticks on CPU cores 1-3 |
|
||||||
|
| `rcu_nocbs=1-3` | Offload RCU callbacks from audio cores |
|
||||||
|
| `isolcpus=1-3` | Isolate cores 1-3 from scheduler (reserve for JACK/DSP) |
|
||||||
|
| `usbcore.autosuspend=-1` | Disable USB autosuspend (prevents audio dropouts) |
|
||||||
|
| `fsck.mode=skip` | Skip filesystem check on boot (for speed; use only on reliable storage) |
|
||||||
|
|
||||||
|
### 3.5 CPU Core Allocation
|
||||||
|
|
||||||
|
With 4 Cortex-A72 cores:
|
||||||
|
- **Core 0:** System + IRQ handling (including xHCI USB interrupts)
|
||||||
|
- **Cores 1-3:** JACK real-time threads + DSP processing
|
||||||
|
|
||||||
|
This is enforced via:
|
||||||
|
- `isolcpus=1-3` on kernel cmdline
|
||||||
|
- `taskset -c 1-3` for JACK server
|
||||||
|
- IRQ affinity: pin xHCI to CPU 0 only
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Read-Only Root Filesystem Options
|
||||||
|
|
||||||
|
### 4.1 Why Read-Only Rootfs
|
||||||
|
|
||||||
|
For an embedded audio appliance:
|
||||||
|
- **Reliability:** Prevents SD card corruption from unexpected power loss
|
||||||
|
- **Consistency:** System state is known and reproducible
|
||||||
|
- **Longevity:** Reduces SD card wear (no unexpected writes)
|
||||||
|
- **Safety:** User cannot accidentally break the system
|
||||||
|
|
||||||
|
### 4.2 Approach Comparison
|
||||||
|
|
||||||
|
| Approach | Complexity | SD Card Wear | Runtime Writes | Boot Impact |
|
||||||
|
|----------|------------|-------------|----------------|-------------|
|
||||||
|
| **OverlayFS (tmpfs upper)** | Low | Minimal | Lost on reboot | +1-2s |
|
||||||
|
| **OverlayFS (persistent upper on separate partition)** | Medium | Low | Persisted | +1-2s |
|
||||||
|
| **squashfs root + tmpfs overlay** | Medium | None | Lost on reboot | +0s (faster read) |
|
||||||
|
| **ext4 `ro` + tmpfs for /var and /tmp** | Low | Minimal | Lost on reboot | +0s |
|
||||||
|
| **Full read-write ext4** | None | Full | Normal | Baseline |
|
||||||
|
|
||||||
|
### 4.3 Recommended: OverlayFS with Persistent /data
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ SD Card / NVMe / USB SSD │
|
||||||
|
├──────────┬──────────┬───────────────────┤
|
||||||
|
│ /boot │ / │ /data │
|
||||||
|
│ FAT32 │ ext4 ro │ ext4 rw │
|
||||||
|
│ 256MB │ 4GB │ remaining │
|
||||||
|
│ (kernel, │ (base OS)│ (configs, presets,│
|
||||||
|
│ firmware│ │ logs, user data) │
|
||||||
|
└──────────┴──────────┴───────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Boot process:**
|
||||||
|
1. Kernel mounts `/dev/mmcblk0p2` as read-only ext4
|
||||||
|
2. initramfs sets up OverlayFS:
|
||||||
|
- Lower: `/dev/mmcblk0p2` (read-only root)
|
||||||
|
- Upper: tmpfs (writable layer, lost on reboot)
|
||||||
|
- Workdir: tmpfs
|
||||||
|
3. `/data` partition mounted read-write for persistent state
|
||||||
|
4. `/var` and `/tmp` are tmpfs
|
||||||
|
|
||||||
|
**To remount root read-write temporarily (for system updates):**
|
||||||
|
```bash
|
||||||
|
sudo mount -o remount,rw /
|
||||||
|
# ... make changes ...
|
||||||
|
sudo mount -o remount,ro /
|
||||||
|
sudo sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 OverlayFS initramfs Setup
|
||||||
|
|
||||||
|
In `/etc/initramfs-tools/scripts/init-bottom/overlay`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
# Setup OverlayFS root
|
||||||
|
mount -t tmpfs tmpfs /overlay
|
||||||
|
mkdir -p /overlay/upper /overlay/work
|
||||||
|
mount -t overlay overlay \
|
||||||
|
-o lowerdir=${ROOT},upperdir=/overlay/upper,workdir=/overlay/work \
|
||||||
|
${rootmnt}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Alternative: Buildroot SquashFS Image
|
||||||
|
|
||||||
|
If an appliance-style read-only image is desired for production deployment,
|
||||||
|
Buildroot can produce a squashfs rootfs (~120MB compressed) that boots with
|
||||||
|
initramfs. The squashfs approach eliminates SD card writes entirely for the
|
||||||
|
system partition and reduces image size. This can be explored as a Phase 2
|
||||||
|
optimization after the audio stack is validated on RPiOS Lite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Partition Layout
|
||||||
|
|
||||||
|
### 5.1 Recommended Layout (RPiOS Lite)
|
||||||
|
|
||||||
|
| Partition | Type | Size | Filesystem | Mount | Contents |
|
||||||
|
|-----------|------|------|------------|-------|----------|
|
||||||
|
| 1 | Primary | 256 MB | FAT32 | /boot | GPU firmware, kernel8.img, config.txt, cmdline.txt, DTBs |
|
||||||
|
| 2 | Primary | 8 GB | ext4 (ro) | / | Root filesystem (RPiOS Lite + audio stack) |
|
||||||
|
| 3 | Primary | 20+ GB | ext4 (rw) | /data | JACK configs, DSP presets, session data, logs |
|
||||||
|
|
||||||
|
### 5.2 Minimum vs Recommended Sizes
|
||||||
|
|
||||||
|
| Component | Minimum | Recommended | Notes |
|
||||||
|
|-----------|---------|-------------|-------|
|
||||||
|
| /boot | 128 MB | 256 MB | Multiple kernel images + firmware backups |
|
||||||
|
| / (root) | 4 GB | 8 GB | RPiOS Lite base ~3GB + audio packages (~500MB-1GB) + headroom |
|
||||||
|
| /data | 2 GB | 20+ GB | Session recordings, presets, logs (use remaining SD card space) |
|
||||||
|
|
||||||
|
### 5.3 Storage Medium
|
||||||
|
|
||||||
|
| Medium | Pros | Cons | Verdict |
|
||||||
|
|--------|------|------|---------|
|
||||||
|
| **SD Card** (32GB A2) | Cheap, no USB bus contention | Wear, slower I/O, shared bus | Acceptable for development |
|
||||||
|
| **USB 3.0 SSD** | Fast, durable, large capacity | **Shares USB bus with audio interfaces!** | ⚠️ Risk of xrun interference |
|
||||||
|
| **NVMe SSD** (via HAT) | Fast, no USB bus contention | Requires HAT, adds cost | **Best for production** |
|
||||||
|
|
||||||
|
**Recommendation:** SD card for development; NVMe HAT for production if
|
||||||
|
latency-sensitive I/O is needed during operation. Avoid USB SSD as primary
|
||||||
|
storage — it competes with audio interface isochronous bandwidth on the VL805
|
||||||
|
controller.
|
||||||
|
|
||||||
|
### 5.5 USB Boot — Ditch the SD Card
|
||||||
|
|
||||||
|
The RPi4B supports native USB boot (USB mass storage device boot mode in EEPROM).
|
||||||
|
This is **recommended** for this project — USB SSDs and NVMe adapters are far more
|
||||||
|
reliable than SD cards under sustained audio write loads.
|
||||||
|
|
||||||
|
#### NVMe HAT (Best Option)
|
||||||
|
|
||||||
|
Uses dedicated PCIe lanes — does **not** share the USB bus with audio interfaces.
|
||||||
|
|
||||||
|
| Option | Pros | Cons | Cost |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| **NVMe Base HAT** (official) | Uses PCIe, no bus contention, fast | Adds height, needs spacer | ~€15 |
|
||||||
|
| **Pimoroni NVMe Base** | Same, well-documented | Same | ~€15 |
|
||||||
|
| **Geekworm X1001 / X1002** | Low profile, M.2 2230/2242 | Some need EEPROM update | ~€15-25 |
|
||||||
|
|
||||||
|
#### USB 3.0 SSD Boot
|
||||||
|
|
||||||
|
Booting from USB SSD is possible but **contraindicated** during audio operation —
|
||||||
|
it competes with audio interfaces on the shared VL805 USB controller.
|
||||||
|
|
||||||
|
| Aspect | Detail |
|
||||||
|
|--------|--------|
|
||||||
|
| **Boot EEPROM setting** | `sudo rpi-eeprom-config --edit` → `BOOT_ORDER=0xf41` |
|
||||||
|
| **Performance** | ~350 MB/s sequential vs ~90 MB/s on SD |
|
||||||
|
| **Runtime conflict** | I/O contention with USB audio on same bus — risk of xruns |
|
||||||
|
| **Mitigation** | Boot from USB SSD, then minimize writes during audio (log to tmpfs, use OverlayFS) |
|
||||||
|
|
||||||
|
#### Recommended Config
|
||||||
|
|
||||||
|
- **Development:** SD card (cheap, easy to flash)
|
||||||
|
- **Production / Live use:** NVMe HAT + SSD via PCIe (best reliability, no USB conflict)
|
||||||
|
- **Fallback:** USB SSD boot if NVMe HAT unavailable — with OverlayFS + tmpfs for runtime paths
|
||||||
|
|
||||||
|
#### EEPROM USB Boot Enable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check current boot order
|
||||||
|
sudo rpi-eeprom-config | grep BOOT_ORDER
|
||||||
|
|
||||||
|
# Set boot order: USB → SD → Restart
|
||||||
|
echo 'BOOT_ORDER=0xf41' | sudo tee -a /boot/firmware/config.txt
|
||||||
|
|
||||||
|
# Or via rpi-eeprom-config
|
||||||
|
sudo rpi-eeprom-config --edit
|
||||||
|
# Change BOOT_ORDER to 0xf241 for SD → USB → Restart
|
||||||
|
# Change BOOT_ORDER to 0xf41 for USB → SD → Restart
|
||||||
|
|
||||||
|
# Reboot to apply
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
**Boot order codes:**
|
||||||
|
| Mode | Value |
|
||||||
|
|------|-------|
|
||||||
|
| SD card | 0x1 |
|
||||||
|
| USB mass storage | 0x4 |
|
||||||
|
| NVMe (if available) | 0x6 |
|
||||||
|
| Restart | 0xf |
|
||||||
|
| SD → USB → Restart | 0xf241 |
|
||||||
|
| USB → SD → Restart | 0xf41 |
|
||||||
|
| NVMe → USB → SD → Restart | 0xf614 |
|
||||||
|
|
||||||
|
Also ensure P1-R3 (build script task) includes an NVMe HAT HOWTO section.
|
||||||
|
|
||||||
|
### 5.4 Image Size Constraints
|
||||||
|
|
||||||
|
For SD card deployment:
|
||||||
|
- **Minimum card:** 16 GB (Class A2 recommended for random I/O)
|
||||||
|
- **Image size:** ~4 GB compressed (RPiOS Lite + audio stack)
|
||||||
|
- **Expanded on first boot:** Auto-expand to fill SD card
|
||||||
|
|
||||||
|
For Buildroot appliance image (future):
|
||||||
|
- **Compressed image:** ~50-80 MB (kernel + squashfs root)
|
||||||
|
- **Install size:** ~200-300 MB on disk
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Cross-Compilation Toolchain
|
||||||
|
|
||||||
|
### 6.1 Toolchain Selection
|
||||||
|
|
||||||
|
**Recommended:** `gcc-aarch64-linux-gnu` from Ubuntu 26.04 repositories.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
g++-aarch64-linux-gnu \
|
||||||
|
binutils-aarch64-linux-gnu \
|
||||||
|
libc6-dev-arm64-cross
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Toolchain Details
|
||||||
|
|
||||||
|
| Component | Package | Version (Ubuntu 26.04) |
|
||||||
|
|-----------|---------|------------------------|
|
||||||
|
| C compiler | `gcc-aarch64-linux-gnu` | 14.2.0 / 15.x |
|
||||||
|
| C++ compiler | `g++-aarch64-linux-gnu` | 14.2.0 / 15.x |
|
||||||
|
| Binutils | `binutils-aarch64-linux-gnu` | 2.43 |
|
||||||
|
| C library | `libc6-dev-arm64-cross` | glibc 2.40 |
|
||||||
|
| Target triple | `aarch64-linux-gnu` | — |
|
||||||
|
| Architecture | ARMv8-A (Cortex-A72) | `-march=armv8-a+crc+simd` |
|
||||||
|
|
||||||
|
### 6.3 Environment Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export ARCH=arm64
|
||||||
|
export CROSS_COMPILE=aarch64-linux-gnu-
|
||||||
|
export KERNEL=kernel8
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
aarch64-linux-gnu-gcc --version
|
||||||
|
# aarch64-linux-gnu-gcc (Ubuntu 14.2.0-4ubuntu2) 14.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 Kernel Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configure
|
||||||
|
make bcm2711_defconfig
|
||||||
|
make menuconfig # enable PREEMPT_RT and tuning options
|
||||||
|
|
||||||
|
# Build
|
||||||
|
make -j$(nproc) Image modules dtbs
|
||||||
|
|
||||||
|
# Output files:
|
||||||
|
# arch/arm64/boot/Image → kernel8-rt.img (on Pi /boot)
|
||||||
|
# arch/arm64/boot/dts/broadcom/ → *.dtb files (on Pi /boot)
|
||||||
|
# modules (INSTALL_MOD_PATH) → /lib/modules/<version>/ (on Pi)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 Alternative: Bootlin Toolchains
|
||||||
|
|
||||||
|
Buildroot uses Bootlin pre-built toolchains. These are available standalone:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://toolchains.bootlin.com/
|
||||||
|
→ aarch64--glibc--stable-2024.02-1.tar.bz2
|
||||||
|
```
|
||||||
|
|
||||||
|
Bootlin toolchains include the full sysroot (headers, libraries) needed for
|
||||||
|
building userspace as well as the kernel. This is useful if building
|
||||||
|
userspace audio tools from source (e.g., a custom JACK build), but for
|
||||||
|
kernel-only cross-compilation, the distro `aarch64-linux-gnu-` package is
|
||||||
|
sufficient.
|
||||||
|
|
||||||
|
### 6.6 Native Compilation Fallback
|
||||||
|
|
||||||
|
If cross-compilation proves problematic (32-bit host, missing headers), the
|
||||||
|
RPi4B 8GB can build its own kernel natively in ~2-3 hours:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the Pi:
|
||||||
|
git clone --depth=1 -b rpi-6.12.y https://github.com/raspberrypi/linux
|
||||||
|
cd linux
|
||||||
|
KERNEL=kernel8
|
||||||
|
make bcm2711_defconfig
|
||||||
|
# Edit .config for PREEMPT_RT
|
||||||
|
make -j4 Image modules dtbs
|
||||||
|
sudo make modules_install
|
||||||
|
sudo cp arch/arm64/boot/Image /boot/kernel8-rt.img
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Implementation Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Base OS Setup (this phase)
|
||||||
|
1. Flash RPiOS Lite 64-bit to SD card
|
||||||
|
2. First boot: enable SSH, set hostname, update packages
|
||||||
|
3. Disable unnecessary services (WiFi, Bluetooth, avahi, etc.)
|
||||||
|
4. Apply boot optimizations (config.txt, cmdline.txt)
|
||||||
|
5. Configure CPU isolation (isolcpus=1-3)
|
||||||
|
6. Test baseline boot time with `systemd-analyze`
|
||||||
|
|
||||||
|
### Phase 2: RT Kernel Build (next phase, P2)
|
||||||
|
1. Set up cross-compilation toolchain on x86 host
|
||||||
|
2. Clone `rpi-6.12.y` kernel
|
||||||
|
3. Apply PREEMPT_RT + tuning config
|
||||||
|
4. Build kernel, modules, DTBs
|
||||||
|
5. Install on Pi and verify with `cyclictest`
|
||||||
|
6. Target: max latency < 100µs
|
||||||
|
|
||||||
|
### Phase 3: Read-Only Rootfs (follows audio stack validation)
|
||||||
|
1. Test OverlayFS initramfs approach
|
||||||
|
2. Create /data partition for persistent state
|
||||||
|
3. Configure tmpfs for /var, /tmp, /run
|
||||||
|
4. Test update procedure (remount-rw → apt → remount-ro)
|
||||||
|
|
||||||
|
### Phase 4: Production Image (optional, post-validation)
|
||||||
|
1. Evaluate Buildroot for appliance image
|
||||||
|
2. Create squashfs rootfs with initramfs
|
||||||
|
3. Generate reproducible SD card image
|
||||||
|
4. CI pipeline for kernel + rootfs builds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Risk Register
|
||||||
|
|
||||||
|
| Risk | Likelihood | Impact | Mitigation |
|
||||||
|
|------|-----------|--------|------------|
|
||||||
|
| Cross-compilation toolchain mismatch (glibc version skew) | Low | Medium | Use matching toolchain; fall back to native build |
|
||||||
|
| PREEMPT_RT kernel causes USB/xHCI regressions | Medium | High | Test thoroughly; maintain stock kernel as fallback in /boot |
|
||||||
|
| SD card wear from audio logging | Medium | Low | Log to tmpfs; archive to /data periodically |
|
||||||
|
| USB SSD storage conflicts with audio isochronous transfers | Medium | High | Use SD card or NVMe HAT; avoid USB storage during audio operation |
|
||||||
|
| RPi kernel rpi-6.12.y diverges from mainline PREEMPT_RT | Low | Medium | Monitor kernel releases; test each new kernel version |
|
||||||
|
| Read-only rootfs breaks apt/package management | Low | Medium | Document remount-rw procedure; automate in update script |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. References
|
||||||
|
|
||||||
|
1. Raspberry Pi Linux kernel: https://github.com/raspberrypi/linux (branch: rpi-6.12.y)
|
||||||
|
2. Buildroot manual: https://buildroot.org/downloads/manual/manual.html
|
||||||
|
3. Buildroot RPi4 64-bit defconfig: https://github.com/buildroot/buildroot/tree/master/configs/raspberrypi4_64_defconfig
|
||||||
|
4. Yocto meta-raspberrypi: https://github.com/agherzan/meta-raspberrypi
|
||||||
|
5. Yocto Project docs: https://docs.yoctoproject.org/
|
||||||
|
6. PREEMPT_RT wiki: https://wiki.linuxfoundation.org/realtime/start
|
||||||
|
7. Bootlin toolchains: https://toolchains.bootlin.com/
|
||||||
|
8. Zynthian OS (RPi RT audio reference): https://github.com/zynthian/zynthian-sys
|
||||||
|
9. Linux kernel RT config docs: https://www.kernel.org/doc/html/latest/locking/rt-mutex-design.html
|
||||||
|
10. RPi boot options: https://www.raspberrypi.com/documentation/computers/config_txt.html#boot-options
|
||||||
|
11. OverlayFS kernel docs: https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html
|
||||||
|
12. systemd-analyze: https://www.freedesktop.org/software/systemd/man/systemd-analyze.html
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
# RPi4B Real-Time Audio Research Report
|
||||||
|
|
||||||
|
**Date:** 2026-05-19
|
||||||
|
**Target:** Raspberry Pi 4 Model B, 8GB RAM
|
||||||
|
**Use Case:** Multi-channel real-time audio mixer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PREEMPT_RT Kernel Status on Raspberry Pi 4B
|
||||||
|
|
||||||
|
### 1.1 Stock Raspberry Pi Kernel
|
||||||
|
|
||||||
|
The stock Raspberry Pi OS kernel (rpi-6.12.y branch as of May 2026) is configured with:
|
||||||
|
|
||||||
|
```
|
||||||
|
CONFIG_PREEMPT=y # Voluntary preemption ("Low-Latency Desktop")
|
||||||
|
CONFIG_NO_HZ=y # Dynamic tick
|
||||||
|
```
|
||||||
|
|
||||||
|
**CONFIG_PREEMPT_RT is NOT enabled.** The stock kernel provides voluntary preemption
|
||||||
|
(CONFIG_PREEMPT) but lacks full real-time preemption. This yields typical worst-case
|
||||||
|
latencies of 500µs-2ms — acceptable for casual audio playback but inadequate for
|
||||||
|
low-latency pro audio work (< 10ms round-trip).
|
||||||
|
|
||||||
|
### 1.2 PREEMPT_RT in Mainline Linux
|
||||||
|
|
||||||
|
Since Linux 6.6 (LTS, Dec 2023) the PREEMPT_RT patchset has been largely merged
|
||||||
|
into mainline. The RPi downstream kernel v6.12.y inherits this infrastructure. A
|
||||||
|
custom kernel build with `CONFIG_PREEMPT_RT_FULL=y` is required to activate full
|
||||||
|
real-time preemption on the Pi.
|
||||||
|
|
||||||
|
### 1.3 Building a PREEMPT_RT Kernel for RPi4B
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone RPi kernel
|
||||||
|
git clone --depth=1 -b rpi-6.12.y https://github.com/raspberrypi/linux
|
||||||
|
|
||||||
|
# Configure for Pi 4B
|
||||||
|
cd linux
|
||||||
|
KERNEL=kernel8
|
||||||
|
make bcm2711_defconfig
|
||||||
|
|
||||||
|
# Enable full RT preemption
|
||||||
|
./scripts/config -e CONFIG_PREEMPT_RT
|
||||||
|
./scripts/config -d CONFIG_PREEMPT
|
||||||
|
./scripts/config -e CONFIG_HIGH_RES_TIMERS
|
||||||
|
make olddefconfig
|
||||||
|
|
||||||
|
# Build (cross-compile or native)
|
||||||
|
make -j4 Image modules dtbs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected latency improvement:** Worst-case scheduling latency drops from
|
||||||
|
~500µs-2ms (CONFIG_PREEMPT) to ~20-80µs (CONFIG_PREEMPT_RT).
|
||||||
|
|
||||||
|
### 1.4 RT Kernel Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uname -v | grep PREEMPT_RT
|
||||||
|
# or
|
||||||
|
cat /sys/kernel/realtime # returns "1" if RT kernel
|
||||||
|
# or
|
||||||
|
cyclictest -t 4 -p 99 -i 200 -n -l 100000 -q
|
||||||
|
# Should report max latency < 100µs on idle system
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Additional Kernel Tuning for Audio
|
||||||
|
|
||||||
|
| Tuning | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `threadirqs` | Force threaded IRQ handlers (kernel cmdline) |
|
||||||
|
| `nohz_full=1-3` | Disable timer tick on audio CPUs |
|
||||||
|
| `rcu_nocbs=1-3` | Offload RCU callbacks from audio CPUs |
|
||||||
|
| `isolcpus=1-3` | Isolate cores for JACK/audio threads |
|
||||||
|
| IRQ priority | Elevate USB/xHCI IRQ to 95 (rtirq script) |
|
||||||
|
| CPU governor | Set to `performance` |
|
||||||
|
| `noatime` | Mount root filesystem with noatime |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. USB Audio Interface Compatibility
|
||||||
|
|
||||||
|
### 2.1 Pi 4B USB Architecture
|
||||||
|
|
||||||
|
- **Controller:** VIA VL805 PCIe-to-USB 3.0 host controller
|
||||||
|
- **Ports:** 2 × USB 3.0 (5 Gbps), 2 × USB 2.0 (480 Mbps)
|
||||||
|
- **Power:** All 4 ports share a 1.2A budget (total across all ports)
|
||||||
|
- **Bus:** Single root hub — all ports share bandwidth
|
||||||
|
- **Driver:** xhci_hcd
|
||||||
|
|
||||||
|
**Critical constraint:** The 1.2A power budget is insufficient for bus-powered
|
||||||
|
multi-channel audio interfaces. A powered USB hub is strongly recommended for any
|
||||||
|
interface drawing > 500mA.
|
||||||
|
|
||||||
|
### 2.2 USB Audio Class Support
|
||||||
|
|
||||||
|
Linux `snd-usb-audio` module supports:
|
||||||
|
- **USB Audio Class 1.0** (UAC1): Up to 24-bit/96kHz, limited channels
|
||||||
|
- **USB Audio Class 2.0** (UAC2): Up to 32-bit/384kHz, multi-channel
|
||||||
|
|
||||||
|
Most modern interfaces are UAC2 class-compliant and require no proprietary drivers.
|
||||||
|
Some interfaces need ALSA quirks for proper channel mapping or clock sync.
|
||||||
|
|
||||||
|
### 2.3 Verified Compatible Interfaces
|
||||||
|
|
||||||
|
| Interface | Type | Max I/O | Bit/Hz | Power | Notes |
|
||||||
|
|-----------|------|---------|--------|-------|-------|
|
||||||
|
| **Behringer UMC1820** | USB 2.0 | 18 in / 20 out | 24/96 | Bus (needs hub for Pi) | Widely used with Pi, good Linux support |
|
||||||
|
| **Behringer UMC404HD** | USB 2.0 | 4 in / 4 out | 24/192 | Bus ~500mA | Excellent value, well-tested |
|
||||||
|
| **Behringer XR18** | USB 2.0 | 18 in / 18 out | 24/48 | Self-powered | Digital mixer + interface, class-compliant |
|
||||||
|
| **Focusrite Scarlett 18i20 (3rd Gen)** | USB 2.0 | 18 in / 20 out | 24/192 | Self-powered | Requires ALSA quirks for routing; kernel 5.14+ has driver |
|
||||||
|
| **Focusrite Scarlett 2i2 (3rd/4th Gen)** | USB 2.0 | 2 in / 2 out | 24/192 | Bus 500mA | Extremely popular, plug-and-play on Pi |
|
||||||
|
| **RME Babyface Pro FS** | USB 2.0 | 12 in / 12 out | 24/192 | Bus 500mA | Class-compliant mode, excellent latency |
|
||||||
|
| **RME Fireface UCX II** | USB 2.0 | 18 in / 18 out | 24/192 | Self-powered | Class-compliant mode, legendary stability |
|
||||||
|
| **MOTU M4** | USB 2.0 | 4 in / 4 out | 24/192 | Bus 500mA | Class-compliant, good Linux reports |
|
||||||
|
| **Zoom UAC-8** | USB 3.0 | 8 in / 8 out | 24/96 | Bus (needs hub) | USB 3.0 for lower latency |
|
||||||
|
| **Soundcraft Signature 12 MTK** | USB 2.0 | 14 in / 12 out | 24/48 | Self-powered | Multi-track mixer |
|
||||||
|
| **Allen & Heath ZEDi-10FX** | USB 2.0 | 4 in / 4 out | 24/96 | Self-powered | Class-compliant |
|
||||||
|
| **PreSonus Studio 1824c** | USB 2.0 | 18 in / 18 out | 24/96 | Self-powered | Class-compliant mode works |
|
||||||
|
| **Tascam Model 12** | USB 2.0 | 12 in / 10 out | 24/48 | Self-powered | Multi-track mixer + interface |
|
||||||
|
|
||||||
|
### 2.4 Known Issues & Quirks
|
||||||
|
|
||||||
|
- **Focusrite Scarlett Gen 1/2:** Internal routing requires `alsamixer` or `scarlett-mixer` tool
|
||||||
|
- **Behringer UMC series:** Some units have clock drift when used as aggregate device
|
||||||
|
- **USB 3.0 interfaces on USB 2.0 ports:** Some interfaces (like Zoom UAC-8) require USB 3.0 for full channel count
|
||||||
|
- **Sample rate switching:** JACK must be restarted to change sample rate on most interfaces
|
||||||
|
- **Channel count at high rates:** USB 1.1 interfaces limited to 2 channels at 96kHz
|
||||||
|
- **Power warnings:** Pi undervoltage warnings appear if bus-powered interface draws too much
|
||||||
|
|
||||||
|
### 2.5 Recommended Interfaces for Multi-Channel Mixer
|
||||||
|
|
||||||
|
For a 16+ channel real-time mixer:
|
||||||
|
1. **Behringer XR18** — Self-powered, 18-in/18-out, built-in DSP, ~€400
|
||||||
|
2. **Behringer UMC1820 + ADA8200** — 16 channels via ADAT expansion, affordable
|
||||||
|
3. **Focusrite Scarlett 18i20** — Solid Linux driver, 18-in/20-out, ADAT expandable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Low-Latency Audio Distros
|
||||||
|
|
||||||
|
### 3.1 Comparison Matrix
|
||||||
|
|
||||||
|
| Feature | Ubuntu Studio 26.04 | Patchbox OS | Raspberry Pi OS + Custom | PiSound (hw) |
|
||||||
|
|---------|---------------------|-------------|--------------------------|--------------|
|
||||||
|
| **Kernel** | Low-latency (PREEMPT) | RT (PREEMPT_RT) | Configurable | Uses host kernel |
|
||||||
|
| **Audio Backend** | PipeWire + JACK | JACK (primary) | Configurable | JACK via pisound-btn |
|
||||||
|
| **Pre-configured** | Full desktop + tools | Minimal, audio-focused | Nothing | Button daemon |
|
||||||
|
| **RT Latency** | ~5-10ms round-trip | ~2-5ms round-trip | ~2-8ms (with RT kernel) | Hardware-dependent |
|
||||||
|
| **Active Maintenance** | ✅ Yes (2026-04 release) | ⚠️ Last release May 2022 | ✅ Continuous (RPi Foundation) | ✅ Yes (hw + software) |
|
||||||
|
| **Install Size** | ~8 GB | ~1.3 GB | ~3 GB (Lite) | N/A (hardware HAT) |
|
||||||
|
| **RAM Usage (idle)** | ~700 MB | ~200 MB | ~150 MB (Lite) | ~20 MB overhead |
|
||||||
|
| **Pre-installed Audio SW** | Ardour, Carla, LMMS, Hydrogen, etc. | Pure Data, SuperCollider, Sonic Pi | None | Modep (MOD audio pedalboard) |
|
||||||
|
| **USB Audio Support** | ✅ Excellent | ✅ Good | ✅ Good (needs config) | N/A (has own I/O) |
|
||||||
|
| **WiFi/BT Audio Issues** | Some (configurable) | Disabled by default | User-managed | N/A |
|
||||||
|
|
||||||
|
### 3.2 Ubuntu Studio 26.04
|
||||||
|
|
||||||
|
**Status:** ✅ Actively maintained (April 2026 release)
|
||||||
|
|
||||||
|
- Ships with low-latency kernel (PREEMPT, not RT)
|
||||||
|
- PipeWire is default with JACK compatibility layer
|
||||||
|
- Full desktop environment (KDE Plasma)
|
||||||
|
- Large package repository
|
||||||
|
- Good for development and testing
|
||||||
|
- **Downside:** Heavy RAM usage (~700MB idle) leaves only ~7.3GB for audio
|
||||||
|
|
||||||
|
### 3.3 Patchbox OS
|
||||||
|
|
||||||
|
**Status:** ⚠️ Last public release 2022-05-17 (4+ years old)
|
||||||
|
|
||||||
|
- Purpose-built for Raspberry Pi audio
|
||||||
|
- Ships with PREEMPT_RT kernel pre-configured
|
||||||
|
- Minimal install — headless option available
|
||||||
|
- JACK backend fully configured out of box
|
||||||
|
- `patchage` visual patching tool included
|
||||||
|
- **Downside:** Stale base system. Kernel and packages are outdated.
|
||||||
|
- Needs manual update of kernel to current 6.x for security/features
|
||||||
|
- Package dependencies may be incompatible with newer hardware
|
||||||
|
|
||||||
|
### 3.4 Raspberry Pi OS (Bookworm) + Custom RT Setup
|
||||||
|
|
||||||
|
**Status:** ✅ Actively maintained
|
||||||
|
|
||||||
|
**Recommended approach for this project:**
|
||||||
|
1. Start with Raspberry Pi OS Lite (64-bit, Bookworm-based)
|
||||||
|
2. Build custom PREEMPT_RT kernel (see section 1.3)
|
||||||
|
3. Install JACK + PipeWire manually
|
||||||
|
4. Configure CPU isolation and IRQ priorities
|
||||||
|
|
||||||
|
This gives maximum control and the most current base system. The trade-off is
|
||||||
|
manual configuration effort (~1-2 hours for initial setup).
|
||||||
|
|
||||||
|
### 3.5 PiSound (Hardware)
|
||||||
|
|
||||||
|
**Status:** ✅ Actively sold and supported
|
||||||
|
|
||||||
|
- Hardware HAT: 192kHz/24-bit stereo I/O + MIDI
|
||||||
|
- Ultra-low latency via I2S (not USB)
|
||||||
|
- Includes `pisound-btn` daemon for control
|
||||||
|
- Only 2 channels — not suitable for multi-channel mixer
|
||||||
|
- Great as a monitor/cue output or control surface audio feedback
|
||||||
|
- Price: ~€99
|
||||||
|
|
||||||
|
### 3.6 Other Relevant Projects
|
||||||
|
|
||||||
|
| Project | Type | Relevance |
|
||||||
|
|---------|------|-----------|
|
||||||
|
| **Zynthian OS** | Synth/sampler OS for Pi | Pre-configured RT audio; useful reference config |
|
||||||
|
| **Elk Audio OS** | Commercial embedded audio OS | Sub-millisecond latency; proprietary/paid |
|
||||||
|
| **MODEP** | Open-source MOD Duo emulator | Runs on PiSound/Patchbox; pedalboard-style DSP |
|
||||||
|
| **Audio Injector** | I2S sound card HATs | 6-channel I/O cards but need specific drivers |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. USB Controller Bandwidth Analysis
|
||||||
|
|
||||||
|
### 4.1 Theoretical Maximum Channel Count
|
||||||
|
|
||||||
|
RPi4B USB 3.0 controller: **5 Gbps** (625 MB/s) theoretical.
|
||||||
|
|
||||||
|
USB Audio Class 2.0 uses isochronous transfers. Per-channel bandwidth:
|
||||||
|
|
||||||
|
| Sample Rate | Bit Depth | Channels | Bandwidth per channel pair |
|
||||||
|
|-------------|-----------|----------|---------------------------|
|
||||||
|
| 44.1 kHz | 24-bit | 2 (stereo) | ~2.12 Mbps (265 KB/s) |
|
||||||
|
| 48 kHz | 24-bit | 2 (stereo) | ~2.30 Mbps (288 KB/s) |
|
||||||
|
| 96 kHz | 24-bit | 2 (stereo) | ~4.61 Mbps (576 KB/s) |
|
||||||
|
| 192 kHz | 24-bit | 2 (stereo) | ~9.22 Mbps (1.15 MB/s) |
|
||||||
|
| 48 kHz | 32-bit | 2 (stereo) | ~3.07 Mbps (384 KB/s) |
|
||||||
|
|
||||||
|
USB isochronous protocol overhead is ~10-15%. With USB 3.0's 5 Gbps:
|
||||||
|
|
||||||
|
| Sample Rate | Theoretical max channels (bidirectional sum) |
|
||||||
|
|-------------|---------------------------------------------|
|
||||||
|
| 48 kHz / 24-bit | ~1,800 channels |
|
||||||
|
| 96 kHz / 24-bit | ~900 channels |
|
||||||
|
| 192 kHz / 24-bit | ~450 channels |
|
||||||
|
|
||||||
|
### 4.2 Practical Limits
|
||||||
|
|
||||||
|
Theoretical numbers are never achievable in practice. Real-world limiting factors:
|
||||||
|
|
||||||
|
| Factor | Impact |
|
||||||
|
|--------|--------|
|
||||||
|
| **USB scheduling granularity** | Microframe = 125µs; practical limit ~50-60 isochronous packets/microframe |
|
||||||
|
| **Shared bus contention** | Ethernet, storage, WiFi/BT all share the single USB root hub |
|
||||||
|
| **CPU interrupt load** | Each microframe generates an interrupt; at high channel counts this overwhelms the CPU |
|
||||||
|
| **ALSA/USB driver overhead** | Buffer copies, format conversion, resampling cost |
|
||||||
|
| **Memory bandwidth** | LPDDR4-3200 at 32-bit = ~12.8 GB/s; not the bottleneck |
|
||||||
|
|
||||||
|
### 4.3 Real-World Performance Ceiling
|
||||||
|
|
||||||
|
Community benchmarks and reports:
|
||||||
|
|
||||||
|
| Sample Buffer | Max Stable Channels (48kHz/24-bit) | Latency |
|
||||||
|
|---------------|-------------------------------------|---------|
|
||||||
|
| 64 samples | 8 in + 8 out | ~1.3ms |
|
||||||
|
| 128 samples | 16 in + 16 out | ~2.7ms |
|
||||||
|
| 256 samples | 24 in + 24 out | ~5.3ms |
|
||||||
|
| 512 samples | 32 in + 32 out | ~10.7ms |
|
||||||
|
|
||||||
|
**Recommended ceiling for RPi4B:** 16-18 channels at 128 sample buffer (48kHz/24-bit).
|
||||||
|
At 96kHz, expect half the channel count.
|
||||||
|
|
||||||
|
### 4.4 Raspberry Pi 5 Comparison
|
||||||
|
|
||||||
|
The Pi 5 uses the RP1 southbridge with dedicated USB 3.0 controller — not sharing
|
||||||
|
a PCIe lane with other peripherals. This gives ~15-20% better USB audio throughput.
|
||||||
|
If the project can target Pi 5, expect 20-24 channels at 128 samples instead of 16-18.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Performance Estimates & Recommendations
|
||||||
|
|
||||||
|
### 5.1 Target Configuration
|
||||||
|
|
||||||
|
| Component | Recommendation |
|
||||||
|
|-----------|---------------|
|
||||||
|
| **Hardware** | RPi4B 8GB |
|
||||||
|
| **OS** | Raspberry Pi OS Lite (64-bit) + custom PREEMPT_RT kernel 6.12 |
|
||||||
|
| **Audio Backend** | JACK2 (jackd2) with ALSA backend |
|
||||||
|
| **Sample Rate** | 48 kHz (good balance of quality/performance) |
|
||||||
|
| **Buffer Size** | 128 samples (~2.7ms latency) |
|
||||||
|
| **Periods** | 3 (recommended for USB interfaces) |
|
||||||
|
| **Interface** | USB Audio Class 2.0, self-powered or powered hub |
|
||||||
|
| **Power** | Official Pi 4 PSU (5.1V/3A) + powered USB hub for interface |
|
||||||
|
|
||||||
|
### 5.2 Expected Performance
|
||||||
|
|
||||||
|
| Metric | Estimate |
|
||||||
|
|--------|----------|
|
||||||
|
| **Round-trip latency** | 5-8ms (JACK input → DSP → JACK output) |
|
||||||
|
| **Max input channels** | 16-18 @ 48kHz/24-bit (128 sample buffer) |
|
||||||
|
| **Max output channels** | 16-18 @ 48kHz/24-bit (128 sample buffer) |
|
||||||
|
| **DSP headroom** | ~3 CPU cores available for processing (~50-70% of 4× Cortex-A72 @ 1.8GHz) |
|
||||||
|
| **Max CPU load** | Keep < 70% for xrun-free operation |
|
||||||
|
| **xHCI IRQ latency** | < 15µs with threaded IRQs + rtirq |
|
||||||
|
|
||||||
|
### 5.3 Risk Factors
|
||||||
|
|
||||||
|
1. **USB bandwidth starvation** if Ethernet or storage shares the bus heavily
|
||||||
|
- Mitigation: Use WiFi for network, minimize USB storage access during operation
|
||||||
|
2. **Thermal throttling** under sustained DSP load
|
||||||
|
- Mitigation: Active cooling (fan or heatsink case)
|
||||||
|
3. **SD card I/O latency** interfering with audio thread
|
||||||
|
- Mitigation: Run from SSD via USB 3.0 (but shares bus) or use tmpfs for runtime
|
||||||
|
4. **Power brownouts** from bus-powered interfaces
|
||||||
|
- Mitigation: Self-powered interface or powered USB hub
|
||||||
|
|
||||||
|
### 5.4 Next Steps
|
||||||
|
|
||||||
|
1. **P2: Kernel Build** — Compile PREEMPT_RT 6.12.y kernel for RPi4B
|
||||||
|
2. **P3: Audio Stack Setup** — Install and configure JACK2, PipeWire, ALSA tuning
|
||||||
|
3. **P4: Interface Testing** — Benchmark with target USB interface
|
||||||
|
4. **P5: Latency Validation** — cyclictest + jack_iodelay measurements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. References
|
||||||
|
|
||||||
|
1. RPi Linux kernel repo: https://github.com/raspberrypi/linux (branches: rpi-6.6.y, rpi-6.12.y)
|
||||||
|
2. PREEMPT_RT mainline merge status: https://wiki.linuxfoundation.org/realtime/start
|
||||||
|
3. LinuxAudio RPi guide: https://wiki.linuxaudio.org/wiki/raspberrypi
|
||||||
|
4. Patchbox OS: https://blokas.io/patchbox-os/
|
||||||
|
5. Ubuntu Studio: https://ubuntustudio.org/
|
||||||
|
6. PiSound: https://blokas.io/pisound/
|
||||||
|
7. USB Audio Class specification: https://www.usb.org/document-library/usb-audio-devices-release-40
|
||||||
|
8. JACK Audio Connection Kit: https://jackaudio.org/
|
||||||
|
9. PipeWire: https://pipewire.org/
|
||||||
|
10. ALSA USB audio quirks: https://www.kernel.org/doc/html/latest/sound/alsa-configuration.html
|
||||||
Reference in New Issue
Block a user