Files
shawn 9cd8292acc
Lint & Validate / lint (push) Has been cancelled
Phase 1-4: Audio stack, mixer engine, MIDI, and network API
P2-R1: ALSA + JACK2 low-latency config (scripts, quirks, tuning)
P2-R2: Carla integration (build scripts, 8ch rack config, NAM LV2 support)
P2-R3: Plugin manager, categories, blacklist, NAM model support
P3-R1: Mixer DSP engine (channel strip, routing matrix, bus mgr, automation)
P4-R1: MIDI engine (learn mode, clock sync, HID discovery, mapping store)
P4-R2: Network API (OSC server, FastAPI REST, WebSocket, auth, rate limiter)
P5-R1: Touchscreen UI evaluation + main entry point
docs: Audio stack, Carla integration, MIDI support, UI evaluation
tests: Full test suite (292 passing)
2026-05-19 20:39:17 -04:00

23 KiB

Carla Integration — Plugin Host for RPi4B Audio Mixer

Date: 2026-05-19 Task: P2-R2 Parent: P2-R1 (ALSA + JACK2 Low-Latency Config) Target Platform: Raspberry Pi 4B, RPi OS Lite 64-bit (Bookworm), PREEMPT_RT 6.12.y


Table of Contents

  1. Architecture Overview
  2. Build Carla from Source
  3. Build Neural Amp Modeler (NAM) LV2
  4. Carla Configuration
  5. Default Mixer Rack & Patchbay
  6. JACK Auto-Connection
  7. Plugin Compatibility Testing
  8. Preset & Program Management
  9. Performance Tuning for RPi4B
  10. Operational Procedures
  11. Troubleshooting
  12. File Inventory

1. Architecture Overview

Carla sits as the plugin host layer between JACK and the audio applications:

┌──────────────────────────────────────────────────────────────┐
│  User Space                                                    │
│                                                                │
│  ┌────────────────────────────────────────────────────┐       │
│  │  Carla Plugin Host (carla)                          │       │
│  │  ┌──────────────────────────────────────────────┐   │       │
│  │  │  Rack Mode — 8-channel mixer                  │   │       │
│  │  │  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐    │   │       │
│  │  │  │Ch1   │ │Ch2   │ │Ch3   │ │AUX 1/2   │    │   │       │
│  │  │  │Gate→ │ │Gate→ │ │NAM→  │ │Reverb    │    │   │       │
│  │  │  │EQ→   │ │EQ→   │ │IR    │ │Delay     │    │   │       │
│  │  │  │Comp  │ │Comp  │ │      │ │          │    │   │       │
│  │  │  └──┬───┘ └──┬───┘ └──┬───┘ └────┬─────┘    │   │       │
│  │  │     │        │        │           │          │   │       │
│  │  │     └────────┼────────┼───────────┘          │   │       │
│  │  │              │        │                      │   │       │
│  │  │         ┌────▼────────▼────┐                 │   │       │
│  │  │         │  Master Limiter  │                 │   │       │
│  │  │         └────────┬────────┘                 │   │       │
│  │  └──────────────────┼──────────────────────────┘   │       │
│  │                     │                              │       │
│  │  ┌──────────────────▼──────────────────────────┐   │       │
│  │  │  Patchbay — dynamic signal routing           │   │       │
│  │  │  (system capture → plugin inputs)            │   │       │
│  │  │  (plugin outputs → system playback)          │   │       │
│  │  └──────────────────┬──────────────────────────┘   │       │
│  └─────────────────────┼──────────────────────────────┘       │
│                        │                                      │
│  ┌─────────────────────▼──────────────────────────────┐       │
│  │  JACK2 (jackd) — SCHED_FIFO prio 70-80             │       │
│  │  ALSA backend (hw:USB), 48kHz, 128f/3p, 8ms        │       │
│  └─────────────────────┬──────────────────────────────┘       │
├─────────────────────────┼──────────────────────────────────────┤
│  Kernel                 │                                      │
│  snd-usb-audio (nrpacks=1) → xHCI (IRQ prio 95)              │
│  PREEMPT_RT 6.12.y, cores 1-3 isolated for audio              │
└─────────────────────────┼──────────────────────────────────────┘
                          │
                 ┌────────▼────────┐
                 │  USB Audio I/F  │
                 │  (class UAC2)   │
                 └─────────────────┘

Plugin Format Support

Format Carla Support Build Requirement
LV2 ✓ Native lv2-dev, lilv
LADSPA ✓ Native Built-in
DSSI ✓ Native Built-in
VST2 ✓ Via bridge VST2 SDK (optional)
VST3 ✓ Via bridge VST3 SDK (/opt/vst3sdk)
SF2/SFZ ✓ Native libfluidsynth-dev
NAM ✓ LV2 host See §3 below

Key Design Decisions

Parameter Value Rationale
Carla process mode MultipleClients Each plugin = separate JACK client for independent patching
Default channels 8 (expandable) 2 mic/line, 1 guitar, 2 aux, master
Plugin format priority LV2 Native Linux, no bridge overhead, best ARM support
GUI toolkit PyQt5 (headless supported) Carla can run without GUI on RPi4B
ARM optimization Cortex-A72 + NEON + LTO -march=armv8-a+crc+crypto -mtune=cortex-a72
Transport sync Disabled Mixer doesn't need DAW transport

2. Build Carla from Source

Prerequisites

  • Working JACK2 setup (see parent task P2-R1: docs/audio-stack-config.md)
  • ~500MB free disk space for build
  • Internet connection for dependency download
  • Run directly on the Raspberry Pi 4B (not cross-compiled)

Build

cd /home/pi/raspberry-pi-mixer

# One-command build
chmod +x scripts/carla-build.sh
./scripts/carla-build.sh --install

Estimated build time: 30-60 minutes on RPi4B (4 cores).

What gets built

The script:

  1. Installs all build dependencies (Qt5, JACK, ALSA, FFmpeg, FluidSynth, OSC, etc.)
  2. Clones Carla from https://github.com/falkTX/Carla.git
  3. Patches the Makefile to add ARM Cortex-A72 optimization flags (-march=armv8-a+crc+crypto -mtune=cortex-a72)
  4. Builds with WITH_LTO=true for smaller, faster binary
  5. Optionally installs to /usr/local

Cortex-A72 Optimization

The Makefile patch adds ARM64-specific flags to source/Makefile.mk:

ifeq ($(CPU_ARM64_OR_AARCH64),true)
BASE_OPTS += -march=armv8-a+crc+crypto -mtune=cortex-a72
endif

The original Makefile only has NEON flags for ARM32 (Cortex-A7). ARM64/aarch64 uses the compiler's default NEON support (always available on ARMv8-A).

VST3 Support

VST3 plugin hosting requires the Steinberg VST3 SDK:

sudo git clone --recursive https://github.com/steinbergmedia/vst3sdk.git /opt/vst3sdk

Then rebuild Carla. Without the SDK, LV2, LADSPA, and DSSI still work.

Verification

# Check binary
file /usr/local/bin/carla
# Expected: ELF 64-bit LSB executable, ARM aarch64

# Check features
carla --help 2>&1 | head -20

# Test with JACK
sudo audio-stack-start
carla &

3. Build Neural Amp Modeler (NAM) LV2

NAM uses the RTNeural library (real-time neural inference, header-only C++) wrapped in an LV2 plugin. It does NOT require libtorch/PyTorch.

Build

cd /home/pi/raspberry-pi-mixer
chmod +x scripts/nam-build.sh
./scripts/nam-build.sh --install

Critical: Model Sizes Matter

Model Size Parameters RPi4B Feasibility xrun Risk at 128f
Standard ~500K+ ✗ No Guaranteed xruns
Feather ~100-200K ⚠ Marginal Occasional
Nano ~30-50K ✓ Yes Low
Micro ~10-20K ✓ Yes Very low

For RPi4B, use nano or feather models from Tone3000.

# Download models
mkdir -p ~/nam-models
# Then download .nam files from Tone3000 and place them in ~/nam-models/

NAM + IR Chain in Carla

The default rack preset places NAM in Channel 3 followed by an IR loader for cabinet simulation:

system:capture_3 → [Ch3 NAM] → [Ch3 IR Loader] → Master → system:playback_1/2

4. Carla Configuration

Configuration File

Carla's settings are stored at ~/.config/falkTX/Carla.conf (JSON-format).

Key settings for our mixer setup:

{
  "Main": {
    "AudioDriver": "JACK",
    "ProcessMode": "MultipleClients",
    "TransportMode": "Disabled"
  },
  "JACK": {
    "AutoConnectPorts": false,
    "RackExportPorts": true
  },
  "LV2": {
    "UIbridges": false,
    "BridgedWine": false
  }
}

The scripts/carla-jack-autoconnect script handles port wiring instead of Carla's built-in auto-connect, giving us precise control.

Environment Variables

# For headless operation (RPi4B without display)
export CARLA_BRIDGE_DUMMY=1       # Disable plugin UIs
export QT_QPA_PLATFORM=offscreen  # Qt offscreen rendering

# Performance
export JACK_NO_AUDIO_OUT=0        # Don't mute JACK when jackd starts

Carla Startup with the Rack

# Start the full audio stack
sudo audio-stack-start

# Launch Carla with the default mixer rack
carla config/carla-8ch-default.carxp &

# Auto-wire JACK ports
./scripts/carla-jack-autoconnect --watch

5. Default Mixer Rack & Patchbay

Rack Layout (config/carla-8ch-default.carxp)

Plugin Chain (signal flow top-to-bottom):
─────────────────────────────────────────
Channel 1:  Gate → 12-Band EQ → Compressor
Channel 2:  Gate → 8-Band EQ  → Compressor
Channel 3:  NAM  → IR Loader (guitar)
AUX 1:      Hall Reverb
AUX 2:      Vintage Delay
Master:     Limiter (ceiling -1.0 dBFS)
─────────────────────────────────────────

All plugins use Calf Studio Gear LV2 suite for CPU-efficient DSP on ARM.

Patchbay Rules (config/carla-patchbay.xml)

system:capture_1 → Ch1 Gate input
system:capture_2 → Ch2 Gate input
system:capture_3 → Ch3 NAM input
Master Limiter out 1 → system:playback_1
Master Limiter out 2 → system:playback_2

Generating Custom Racks

# 4-channel minimal rack
python3 scripts/carla-preset-gen.py --channels 4

# Patchbay only
python3 scripts/carla-preset-gen.py --patchbay

Adding/Removing Plugins

Edit the rack in Carla's GUI, or modify scripts/carla-preset-gen.py and regenerate. The preset uses standard LV2 URIs:

  • http://calf.sourceforge.net/plugins/Gate
  • http://calf.sourceforge.net/plugins/Equalizer12Band
  • http://calf.sourceforge.net/plugins/Equalizer8Band
  • http://calf.sourceforge.net/plugins/Compressor
  • http://calf.sourceforge.net/plugins/Reverb
  • http://calf.sourceforge.net/plugins/VintageDelay
  • http://calf.sourceforge.net/plugins/Limiter
  • http://github.com/mikeoliphant/neural-amp-modeler-lv2

6. JACK Auto-Connection

One-shot Connection

./scripts/carla-jack-autoconnect

Connects all currently available Carla ports according to routing rules.

Persistent Auto-Connect Daemon

./scripts/carla-jack-autoconnect --watch

Monitors JACK for new Carla plugin ports and wires them automatically. Run this in a tmux session or as a systemd user service:

# ~/.config/systemd/user/carla-autoconnect.service
[Unit]
Description=Carla JACK Auto-Connect
After=jackd.service

[Service]
ExecStart=/home/pi/raspberry-pi-mixer/scripts/carla-jack-autoconnect --watch
Restart=on-failure

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now carla-autoconnect.service

Dry Run

./scripts/carla-jack-autoconnect --dry-run

Shows what would be connected without making changes.

Disconnect All Carla Ports

./scripts/carla-jack-autoconnect --disconnect-all

7. Plugin Compatibility Testing

LV2 Lint Validation

The lv2lint tool checks LV2 plugin bundles for spec compliance:

chmod +x scripts/lv2lint-check.sh

# Check all installed LV2 plugins
./scripts/lv2lint-check.sh

# Check only NAM
./scripts/lv2lint-check.sh --nam-only

# Check a specific plugin
./scripts/lv2lint-check.sh /usr/lib/lv2/calf.lv2

# List all installed plugins
./scripts/lv2lint-check.sh --list

What gets checked

Check Description
Manifest parsing Valid manifest.ttl exists
Binary architecture Must be ARM aarch64 (not x86)
Shared library deps All .so dependencies resolvable
URI validity Unique, well-formed plugin URI
Port definitions Valid audio/CV/control port specs
Parameter ranges min/max/default values defined
atom:Path support Critical for NAM model selection
Carla extensions Carla-specific features if present

ARM-Specific Checks

  • Architecture verification: ensures no x86 binaries are present
  • NEON SIMD detection: checks if plugins use NEON optimization
  • CPU feature requirements: verifies no SSSE3/AVX instructions

Runtime Testing Procedure

# 1. Start JACK
sudo audio-stack-start

# 2. Run Carla with test rack
carla config/carla-8ch-default.carxp &

# 3. Auto-connect
sleep 2 && ./scripts/carla-jack-autoconnect

# 4. Check for xruns
watch -n 1 "jack_cpu_load; echo '---'; tail -5 /tmp/jackd.log | grep -i xrun || echo 'no xruns'"

# 5. Verify audio path
jack_lsp -c | grep -E "(Carla|system)"

# 6. Test each plugin bypass
# Send MIDI CC or use OSC to toggle plugin bypass

8. Preset & Program Management

CLI Interface

python3 scripts/carla-preset-manager.py [command]
Command Description
list List all saved presets
save <name> Save current Carla state as preset
load <name> Load a preset (overwrites current state)
export <name> <file> Export preset + models as .tar.gz bundle
import <bundle> Import a preset bundle
set-model <plugin> <path> Set NAM model path via OSC

Preset Storage

  • User presets: ~/carla-presets/*.carxp
  • System presets: config/*.carxp (in repo)
  • Metadata: ~/carla-presets/*.meta.json
  • Models: ~/nam-models/*.nam

Workflow: Save and Restore

# Tweak your rack in Carla GUI, then:
python3 scripts/carla-preset-manager.py save "my-live-set"

# Later, restore:
python3 scripts/carla-preset-manager.py load "my-live-set"
# Restart Carla to apply

Workflow: Export a Full Bundle

# Export preset with model references
python3 scripts/carla-preset-manager.py export "my-live-set" /tmp/my-set.tar.gz

# Transfer to another RPi4B
scp /tmp/my-set.tar.gz pi@other-rpi:/tmp/
ssh pi@other-rpi "python3 ~/raspberry-pi-mixer/scripts/carla-preset-manager.py import /tmp/my-set.tar.gz"

9. Performance Tuning for RPi4B

CPU Governor

# Must be 'performance' for audio
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# Expected: performance

CPU Isolation

From parent task — cores 1-3 isolated for audio via isolcpus=1-3 in cmdline.txt. Carla plugins run on isolated cores. Check with:

taskset -cp $(pgrep carla)
# Expected: affinity list includes 1-3

Carla Process Priority

After Carla starts:

# Set Carla to SCHED_FIFO priority (JACK client)
sudo chrt -f -p 80 $(pgrep carla)

NAM Performance Tuning

For the NAM plugin specifically:

  1. Use nano/feather models — standard models cause xruns at any buffer
  2. Increase buffer if needed — edit /etc/jackdrc and change -p 128 to -p 256
  3. Disable GUI updates — Carla GUI redraws consume CPU
  4. Bypass unused plugins — Carla's bypass is true DSP bypass (zero CPU)

Monitoring

# Real-time CPU load
jack_cpu_load

# Per-process DSP load
top -H -p $(pgrep carla)

# Xrun count
grep -c xrun /tmp/jackd.log

Expected Performance

Configuration CPU Load (idle) CPU Load (8ch active) Headroom
No NAM, all Calf ~8% ~22% 78%
1x Nano NAM ~12% ~35% 65%
1x Feather NAM ~18% ~52% 48%
1x Standard NAM ~45% ~95% xruns

10. Operational Procedures

Startup Sequence

#!/bin/bash
# Full audio stack startup with Carla

# 1. Start JACK + ALSA
sudo audio-stack-start

# 2. Wait for JACK to stabilize
sleep 3

# 3. Set CPU governor
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# 4. Launch Carla with default rack
carla config/carla-8ch-default.carxp &
CARLA_PID=$!

# 5. Set realtime priority
sleep 2
sudo chrt -f -p 80 $CARLA_PID 2>/dev/null || true

# 6. Auto-connect JACK ports
./scripts/carla-jack-autoconnect &
AUTOCONNECT_PID=$!

echo "Audio stack running. Carla PID: $CARLA_PID"

Shutdown Sequence

#!/bin/bash
# Graceful shutdown

# 1. Disconnect all Carla ports
./scripts/carla-jack-autoconnect --disconnect-all

# 2. Stop Carla
pkill -f carla || true

# 3. Stop JACK
sudo audio-stack-stop

Headless Operation

For RPi4B without a display, Carla can operate headlessly:

# Run Carla without GUI
QT_QPA_PLATFORM=offscreen carla config/carla-8ch-default.carxp &

# Control via OSC
carla-control --osc-port 22752 set_parameter "Ch1 EQ" 0 0.5

OSC Remote Control

Carla exposes all parameters via OSC on port 22752 (configurable):

# List plugins
oscsend localhost 22752 /Carla/refresh

# Set parameter (plugin_id, param_index, value)
oscsend localhost 22752 /Carla/set_parameter i 0 i 0 f 0.707

# Bypass plugin
oscsend localhost 22752 /Carla/set_active i 0 i 0

11. Troubleshooting

Carla won't start

# Check JACK is running
pgrep jackd && echo "JACK OK" || echo "Start JACK first: sudo audio-stack-start"

# Check Carla binary
ldd $(which carla) | grep "not found"
# If any libraries missing, rebuild with: ./scripts/carla-build.sh --clean --install

# Run with debug output
carla --debug 2>&1 | tail -20

No audio (silent, no xruns)

# Check JACK connections
jack_lsp -c | grep system

# Run auto-connect
./scripts/carla-jack-autoconnect

# Check plugin bypass state — all bypassed = silence
# In Carla GUI: check each plugin's bypass button

Xruns with NAM

# 1. Switch to nano model
# 2. Increase JACK buffer: edit /etc/jackdrc, change -p 128 to -p 256
sudo systemctl restart jackd

# 3. Try feather models with buffer 256
# 4. If still xrunning, skip NAM for live use — use it only for re-amping

Plugin not loading in Carla

# Refresh LV2 plugin cache
rm -f ~/.cache/carla/carla-plugin-cache
carla --refresh &

# Check lv2ls
lv2ls 2>&1 | grep -i <plugin-name>

# Validate the plugin
./scripts/lv2lint-check.sh /usr/lib/lv2/<plugin-name>.lv2

VST3 plugins not showing

# VST3 SDK was not installed during build
ls /opt/vst3sdk && echo "SDK found" || echo "Install SDK and rebuild"
# Rebuild: ./scripts/carla-build.sh --clean --install

High CPU / thermal throttling

# Check CPU temp
vcgencmd measure_temp

# Check if throttled
vcgencmd get_throttled

# If throttling:
# - Reduce plugin count
# - Bypass unused plugins
# - Add active cooling
# - Use nano NAM models only

12. File Inventory

raspberry-pi-mixer/
├── scripts/
│   ├── carla-build.sh              # Build Carla from source (ARM Cortex-A72 optimized)
│   ├── nam-build.sh                # Build NAM LV2 plugin for ARM
│   ├── carla-preset-gen.py         # Generate default rack + patchbay presets
│   ├── carla-jack-autoconnect      # Auto-wire JACK ports to Carla plugins
│   ├── carla-preset-manager.py     # Save/load/export presets
│   ├── lv2lint-check.sh            # LV2 plugin validation suite
│   ├── audio-stack-start           # (from P2-R1) Full audio stack startup
│   ├── audio-stack-stop            # (from P2-R1) Full audio stack shutdown
│   ├── jack-latency-test           # (from P2-R1) Round-trip latency measurement
│   └── jack-route-default          # (from P2-R1) Default JACK routing
├── config/
│   ├── carla-8ch-default.carxp     # Default 8-channel mixer rack preset
│   ├── carla-patchbay.xml          # Patchbay connection rules
│   ├── jackdrc                     # (from P2-R1) JACK server config
│   ├── alsa-usb.conf               # (from P2-R1) ALSA USB config
│   └── ...                         # (other P2-R1 configs)
└── docs/
    ├── audio-stack-config.md        # (from P2-R1) Full audio stack docs
    └── carla-integration.md         # THIS FILE — Carla integration docs

Lines of Code (new in this task)

File Lines Purpose
scripts/carla-build.sh 196 Carla build automation
scripts/nam-build.sh 184 NAM LV2 build automation
scripts/carla-preset-gen.py 196 Preset generation
scripts/carla-jack-autoconnect 208 JACK port auto-wiring
scripts/carla-preset-manager.py 246 Preset save/load/export
scripts/lv2lint-check.sh 217 Plugin validation
config/carla-8ch-default.carxp auto-generated Default rack
config/carla-patchbay.xml auto-generated Patchbay rules
docs/carla-integration.md this file Documentation
Total ~1,247 lines 7 scripts + 2 configs + 1 doc

Verification Checklist

  • Carla builds successfully on RPi4B (./scripts/carla-build.sh --install)
  • carla --help works and shows JACK audio driver
  • NAM LV2 builds successfully (./scripts/nam-build.sh --install)
  • lv2ls | grep neural shows NAM plugin
  • ./scripts/lv2lint-check.sh passes with 0 errors
  • JACK starts: sudo audio-stack-start
  • Carla launches with rack: carla config/carla-8ch-default.carxp
  • Auto-connect wires ports: ./scripts/carla-jack-autoconnect
  • Audio passes through: jack_lsp -c shows Carla ↔ system connections
  • No xruns at idle: jack_cpu_load < 10%
  • NAM loads a nano model without xruns (128f buffer)
  • Preset save/load works: ./scripts/carla-preset-manager.py save test && load test
  • CPU governor is 'performance': cat /sys/.../scaling_governor
  • Carla runs at SCHED_FIFO prio 80: chrt -p $(pgrep carla)