9cd8292acc
Lint & Validate / lint (push) Has been cancelled
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)
225 lines
7.6 KiB
Bash
225 lines
7.6 KiB
Bash
#!/bin/bash
|
|
# lv2lint-check.sh — Validate LV2 plugins for compatibility with Carla/RPi4B
|
|
# Uses lv2lint to check plugin spec compliance, plus runtime checks.
|
|
#
|
|
# Usage:
|
|
# chmod +x lv2lint-check.sh
|
|
# ./lv2lint-check.sh # Check all installed LV2 plugins
|
|
# ./lv2lint-check.sh /path/to/plugin.lv2 # Check a specific plugin
|
|
# ./lv2lint-check.sh --nam-only # Only check NAM plugin
|
|
# ./lv2lint-check.sh --list # List all installed LV2 plugins
|
|
#
|
|
# lv2lint checks (from https://git.open-music-kontrollers.ch/lv2/lv2lint):
|
|
# - URI validity, manifest parsing
|
|
# - Port definitions (in/out, control/audio/CV)
|
|
# - TTL consistency
|
|
# - Required features
|
|
# - Instantiation test
|
|
# - Parameter ranges
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[lv2lint]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[lv2lint]${NC} $*"; }
|
|
fail() { echo -e "${RED}[lv2lint] FAIL:${NC} $*"; }
|
|
|
|
# ── Install lv2lint if not present ────────────────────
|
|
install_lv2lint() {
|
|
if ! command -v lv2lint >/dev/null 2>&1; then
|
|
log "lv2lint not found. Building from source..."
|
|
sudo apt update -qq
|
|
sudo apt install -y build-essential cmake git \
|
|
liblilv-dev libserd-dev libsord-dev libsratom-dev \
|
|
lv2-dev libexempi-dev libsndfile1-dev
|
|
|
|
LV2LINT_DIR="/tmp/lv2lint-build"
|
|
git clone --depth 1 https://git.open-music-kontrollers.ch/lv2/lv2lint "$LV2LINT_DIR" 2>/dev/null || \
|
|
git clone --depth 1 https://github.com/brummer10/lv2lint.git "$LV2LINT_DIR"
|
|
|
|
cd "$LV2LINT_DIR"
|
|
meson setup build 2>/dev/null || \
|
|
(mkdir -p build && cd build && cmake .. && make -j"$(nproc)")
|
|
|
|
if [ -d "build" ]; then
|
|
cd build
|
|
if [ -f "meson.build" ] || [ -f "../meson.build" ]; then
|
|
meson compile 2>/dev/null && sudo meson install 2>/dev/null || true
|
|
elif [ -f "Makefile" ]; then
|
|
make -j"$(nproc)" && sudo make install 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if command -v lv2lint >/dev/null 2>&1; then
|
|
log "lv2lint installed successfully."
|
|
else
|
|
warn "Could not install lv2lint. Skipping spec checks."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── List installed LV2 plugins ────────────────────────
|
|
list_plugins() {
|
|
log "Installed LV2 plugins:"
|
|
echo ""
|
|
for dir in /usr/lib/lv2 /usr/local/lib/lv2 /usr/lib/aarch64-linux-gnu/lv2 ~/.lv2; do
|
|
if [ -d "$dir" ]; then
|
|
find "$dir" -maxdepth 2 -name "*.ttl" | while read -r ttl; do
|
|
uri=$(grep -oP 'a\s+lv2:Plugin' "$(dirname "$ttl")"/*.ttl 2>/dev/null | head -1 || true)
|
|
if [ -n "$uri" ]; then
|
|
echo " $(basename "$(dirname "$ttl")")"
|
|
fi
|
|
done | sort -u
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ── Run lv2lint checks on a plugin ─────────────────────
|
|
check_plugin() {
|
|
local plugin_uri="$1"
|
|
local plugin_path="$2"
|
|
local issues=0
|
|
|
|
log "Checking: $(basename "$plugin_path")"
|
|
echo " URI: $plugin_uri"
|
|
|
|
# Manifest check
|
|
if [ ! -f "$plugin_path/manifest.ttl" ]; then
|
|
fail " Missing manifest.ttl"
|
|
issues=$((issues + 1))
|
|
fi
|
|
|
|
# Binary check
|
|
local so_file=$(find "$plugin_path" -name "*.so" 2>/dev/null | head -1)
|
|
if [ -z "$so_file" ]; then
|
|
warn " No .so file found (might be an extension-only bundle)"
|
|
else
|
|
echo " Binary: $so_file"
|
|
file "$so_file" 2>/dev/null | sed 's/^/ /'
|
|
|
|
# ARM-specific: check architecture
|
|
if file "$so_file" 2>/dev/null | grep -q "x86"; then
|
|
fail " WRONG ARCH: x86 binary on ARM. Rebuild!"
|
|
issues=$((issues + 1))
|
|
fi
|
|
|
|
# Check shared library deps
|
|
echo " Shared libs needed:"
|
|
ldd "$so_file" 2>/dev/null | grep "not found" | while read -r missing; do
|
|
fail " MISSING: $missing"
|
|
issues=$((issues + 1))
|
|
done || true
|
|
fi
|
|
|
|
# Run lv2lint if available
|
|
if command -v lv2lint >/dev/null 2>&1; then
|
|
echo " lv2lint output:"
|
|
local lint_output
|
|
lint_output=$(lv2lint -M pack -q -s skip "$plugin_uri" 2>&1) || true
|
|
if [ -n "$lint_output" ]; then
|
|
echo "$lint_output" | sed 's/^/ /'
|
|
# Count warnings/errors
|
|
local lint_issues=$(echo "$lint_output" | grep -ciE "(FAIL|ERROR|WARN)" || true)
|
|
issues=$((issues + lint_issues))
|
|
else
|
|
echo " ✓ No issues found"
|
|
fi
|
|
fi
|
|
|
|
# Check for Carla-specific features
|
|
if grep -qi "carla" "$plugin_path"/*.ttl 2>/dev/null; then
|
|
echo " ✓ Contains Carla-specific extensions"
|
|
fi
|
|
|
|
echo ""
|
|
return $issues
|
|
}
|
|
|
|
# ── Check NAM specifically ─────────────────────────────
|
|
check_nam() {
|
|
local nam_dir=""
|
|
for dir in /usr/lib/lv2/neural_amp_modeler.lv2 \
|
|
/usr/local/lib/lv2/neural_amp_modeler.lv2 \
|
|
~/.lv2/neural_amp_modeler.lv2 \
|
|
/tmp/nam-lv2-build/build/neural_amp_modeler.lv2; do
|
|
if [ -d "$dir" ]; then
|
|
nam_dir="$dir"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$nam_dir" ]; then
|
|
warn "NAM LV2 plugin not found. Build it first: ./scripts/nam-build.sh --install"
|
|
return 1
|
|
fi
|
|
|
|
check_plugin "http://github.com/mikeoliphant/neural-amp-modeler-lv2" "$nam_dir"
|
|
|
|
# NAM-specific: check if atom:Path is supported (critical for model loading)
|
|
if grep -q "atom:Path" "$nam_dir"/*.ttl 2>/dev/null; then
|
|
log "✓ NAM supports atom:Path (model selection in Carla works)"
|
|
else
|
|
warn "NAM does not declare atom:Path support. Model loading may not work in Carla."
|
|
fi
|
|
|
|
# Performance note for RPi4B
|
|
echo ""
|
|
warn "NAM on RPi4B — performance note:"
|
|
echo " Standard models may cause xruns at 128f buffer."
|
|
echo " Use 'nano' or 'feather' models from https://www.tone3000.com/"
|
|
echo " Increase JACK buffer to 256 samples if needed."
|
|
}
|
|
|
|
# ── Check all installed plugins ────────────────────────
|
|
check_all() {
|
|
local total_issues=0
|
|
for dir in /usr/lib/lv2 /usr/local/lib/lv2 /usr/lib/aarch64-linux-gnu/lv2 ~/.lv2; do
|
|
if [ ! -d "$dir" ]; then continue; fi
|
|
for bundle in "$dir"/*/; do
|
|
if [ -f "$bundle/manifest.ttl" ]; then
|
|
# Extract URI from manifest
|
|
local uri=""
|
|
uri=$(grep -oP 'a\s+lv2:Plugin' "$bundle"/*.ttl 2>/dev/null | head -1 | sed 's/:.*//' || true)
|
|
if [ -z "$uri" ]; then
|
|
# Just use bundle name
|
|
uri="urn:lv2:$(basename "$bundle")"
|
|
fi
|
|
check_plugin "$uri" "${bundle%/}" || total_issues=$((total_issues + $?))
|
|
fi
|
|
done
|
|
done
|
|
echo ""
|
|
log "Total issues: $total_issues"
|
|
}
|
|
|
|
# ── Main ───────────────────────────────────────────────
|
|
case "${1:-}" in
|
|
--list|-l)
|
|
list_plugins
|
|
exit 0
|
|
;;
|
|
--nam-only|-n)
|
|
install_lv2lint
|
|
check_nam
|
|
exit 0
|
|
;;
|
|
"")
|
|
install_lv2lint
|
|
check_all
|
|
check_nam
|
|
;;
|
|
*)
|
|
if [ -d "$1" ]; then
|
|
install_lv2lint
|
|
check_plugin "urn:lv2:$(basename "$1")" "$1"
|
|
else
|
|
echo "Usage: $0 [--list|--nam-only|<plugin-path>]"
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|