# 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`