Files
shawn 0e77adb4c3 Build IR convolution engine
- Full FFT overlap-add IR convolution in IRLoader (process(), set_mix(), toggle)
- Lazy FFT computation — IR FFT padded to correct block+ir size on first process()
- Wet/dry mix control, enabled/disabled toggle with tail clearing
- Fixed pipeline._apply_ir_cab() to delegate to IRLoader.process() instead of
  poking internals (old code had array-size mismatch bug: IR FFT at ir_len vs
  block FFT at conv_size)
- 46 tests: loading, convolution correctness, overlap-add state, mix, toggle,
  directory listing, performance budget (all <5ms even at 8192 taps), edge cases
- scripts/download_irs.sh: free IR pack downloader (God's Cab, Seacow)
2026-06-07 23:46:02 -04:00

207 lines
7.8 KiB
Markdown

# NAM A2 Inference on RPi 4B — Research & Recommendations
**Status:** Complete — June 2026
**Context:** pi-multifx-pedal, BLOCK_SIZE=256, SAMPLE_RATE=48000 (5.33ms per block)
**See also:** [NAM_RESEARCH.md](NAM_RESEARCH.md) for detailed source citations
---
## Executive Summary
NAM A2 inference is **feasible in real-time** on RPi 4B with Feather-class models using any approach. Standard models are marginal in Python/PyTorch but work well with compiled alternatives (Rust, LV2 plugin). The RPi 4B Cortex-A72 at 1.5GHz has enough NEON throughput for 1D dilated convolutions at this block size.
**Bottom line:** Current Python skeleton code works for Feather models. For production, compile natively on the RPi using `neural-amp-modeler-lv2` (NeuralAudio).
---
## 1. NAM Model Architecture
NAM supports 4 architectures, exported as `.nam` JSON files (not TorchScript):
| Architecture | Parameters (default) | Receptive Field | A2 Slimmable? | Use Case |
|---|---|---|---|---|
| **ConvNet** | 6,369 (32ch, 4 dilations) | 16 samples | Yes | Default — best quality/speed trade-off |
| **WaveNet** | ~33K (16->8ch, 10 dilations) | 512+ samples | Yes | Highest quality, most expensive |
| **Linear** | 128 (1x128) | 128 samples | No | Clean/crunch tones, extremely cheap |
| **LSTM** | ~18K | — | No | Vintage/character tones |
### ConvNet (default — the most common)
- 4 dilated 1D Conv1d blocks: dilations [1, 2, 4, 8], kernel size 2
- Input: channels=1, hidden: channels=32, output: channels=1 via head Conv1d
- Activation: Tanh per layer
- Receptive field: sum(dilations) + 1 = 16 samples
- ~6,240 MACs per inference
### NAM A2 SlimmableContainer
- Dynamically scales channel count at runtime (quality=0.0→narrow, 1.0→full)
- RT-safe — can switch quality between blocks
- **Recommended:** quality=0.3-0.5 for RPi 4B at 256-block
---
## 2. Latency Benchmarks (x86_64, PyTorch CPU)
Measured on x86_64. **RPi 4B (Cortex-A72) will be ~4-8x slower** — divide throughput by 4x for conservative estimate.
| Model | Params | 128-block (2.67ms) | 256-block (5.33ms) | 512-block (10.67ms) |
|---|---|---|---|---|
| ConvNet Tiny (16ch) | 1,649 | **1.14ms** (43%) | **0.60ms** (11%) | **0.64ms** (6%) |
| ConvNet Default (32ch) | 6,369 | **1.64ms** (62%) | **0.74ms** (14%) | **4.78ms** (45%) |
| ConvNet Medium (48ch, 5 dil) | 18,817 | 8.63ms (324%) | **2.64ms** (50%) | 21.23ms (199%) |
| ConvNet Large (64ch, 6 dil) | 41,537 | 2.60ms (98%) | 11.84ms (222%) | 13.18ms (124%) |
| Linear 1x64 | 64 | **0.10ms** (4%) | **0.11ms** (2%) | **0.11ms** (1%) |
| Linear 1x128 | 128 | **0.10ms** (4%) | **0.11ms** (2%) | **0.11ms** (1%) |
**Key finding:** Default ConvNet at 256-block takes **0.74ms on x86** — ~3-6ms estimated on RPi 4B. This is within budget but leaves little headroom with Python overhead.
---
## 3. Approach Comparison
| Approach | Feather | Standard | Plugin | aarch64? | Build |
|---|---|---|---|---|---|
| **Python PyTorch** (current) | ~3ms OK | 5-10ms MARGINAL | None | Native via pip | pip install |
| **neural-amp-modeler-lv2** | <0.5ms OK | 1-2ms OK | LV2 | **Compiles native** | cmake on RPi |
| **OpenSauce/nam-rs** | 0.5-1ms OK | 1.9-3.8ms OK | Library crate | cargo build | Pure Rust |
| **PiPedal C++** | <0.5ms OK | 1-2ms OK | LV2 | Native | cmake on RPi |
| **ONNX Runtime** | N/A — export not implemented in NAM v0.13.0 |
### 3a. Python PyTorch (current code) ⭐ Short-term
**Installed version:** `neural-amp-modeler` v0.13.0 via pip.
**Pros:** Already works. Easy model loading via JSON. Can use `torch.jit.script` or `torch.compile` for ~2x speedup.
**Cons:**
- Python call overhead per block (~50-200us)
- PyTorch tensor reshaping/dispatch overhead
- JACK callback in Python — GC pauses risk xruns
- libtorch on aarch64 is big (hundreds of MB)
**Feather models only** — standard models will struggle on RPi 4B.
### 3b. neural-amp-modeler-lv2 (NeuralAudio) ⭐ Medium-term
**Repository:** `mikeoliphant/neural-amp-modeler-lv2` (★460)
**Pros:**
- **Closest to production-ready** — LV2 plugin integrates directly with JACK
- NeuralAudio engine claims performance edge over NAM Core
- Compiles natively on RPi OS 64-bit via cmake
- Supports A1 + A2, WaveNet + LSTM + ConvNet
**Cons:**
- No prebuilt ARM binaries — must compile on-device
- Needs build dependencies (cmake, pkg-config, libsndfile, LV2 SDK)
- No prebuilt Debian packages
**Build commands (on RPi):**
```bash
sudo apt install cmake pkg-config libsndfile-dev lv2-dev
git clone https://github.com/mikeoliphant/neural-amp-modeler-lv2
cd neural-amp-modeler-lv2
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DUSE_NATIVE_ARCH=ON
cmake --build build -j4
sudo cmake --install build
```
### 3c. OpenSauce/nam-rs (Pure Rust) ⭐ Long-term
**Repository:** `github.com/OpenSauce/nam-rs`
**Pros:**
- Pure Rust — no C++ dependency chain, no libtorch
- First-class aarch64 support via `cargo build --target=aarch64-unknown-linux-gnu`
- **A2 SlimmableContainer** — runtime quality/speed dialing
- Published on crates.io
**Cons:**
- No LV2 plugin — library crate only (would need Rust LV2 wrapper)
- Very new project (June 2026), unproven in production
- Estimated same perf as LV2 option
### 3d. PiPedal C++ ⭐ Reference
**Repository:** `rerdavies/pipedal` (★277)
**Pros:**
- Most mature RPi NAM project — measured 2.7ms round-trip latency
- Full web UI, tone3000 integration, LV2 plugins
- Proves the concept works
**Cons:**
- Not a library to reuse — full standalone application
### 3e. ONNX Runtime ❌ Dead end
- `Exportable.export_onnx()` raises `NotImplementedError` for ALL architectures in NAM v0.13.0
- No implementation exists anywhere in the codebase
- Even if it did, ONNX Runtime on aarch64 doesn't outperform PyTorch for Conv1D workloads
---
## 4. Model Size Recommendations for RPi 4B
Based on benchmark extrapolation (x86 * 4-8x for RPi):
| FAMILY | Channels | Dilations | Params | Est. RPi Latency (256-block) | Recommendation |
|---|---|---|---|---|---|
| **Nano** | 4/2ch | [1,2,4] | ~500 | **0.1-0.2ms** | Always safe |
| **Feather** | 8/4ch | [1,2,4,8,16] | ~2,500 | **0.5-1ms** | Default for Python |
| **Lite** | 12/6ch | [1,2,4,8,16,32] | ~5,500 | **1-2ms** | OK with compiled |
| **Standard** | 16/8ch | [1,2,4,8,16,32,64,128] | ~18,000 | **2-4ms** | Only with compiled |
| **Heavy** | 32/16ch | 10+ dilations | ~70,000 | **5-10ms** | Too expensive |
---
## 5. NAM A2 Slimmable Quality Dial
The A2 SlimmableContainer is the ideal RPi solution:
- `quality=0.0` → Nano-class (narrow, fast)
- `quality=0.3` → Feather-class (good tone, 0.5ms on RPi)
- `quality=0.5` → Lite-class (very good tone, 1ms on RPi)
- `quality=1.0` → Standard-class (best tone, 2-4ms on RPi)
Switch quality between presets or even per-block for adaptive RT.
---
## 6. NAM Model Loading (.nam format)
The `.nam` file is a JSON dictionary (NOT TorchScript):
```json
{
"version": "0.13.0",
"architecture": "ConvNet", // or "WaveNet", "Linear", "LSTM"
"metadata": { "date": "...", "loudness": X, "gain": Y },
"config": { "channels": 32, "dilations": [1,2,4,8] },
"weights": [0.001, -0.023, ...] // flat 1D array
}
```
The Python NAMHost should:
1. Load JSON
2. Reconstruct the model from `architecture` + `config`
3. Call `import_weights(weights)` to set parameters
4. Run `model(x_tensor)` for inference
---
## 7. Current project's path forward
### Phase 1 — Python + Feather (now)
- Current `nam_host.py` and `pipeline.py` are adequate
- Limit to Feather/Nano NAM models
- Add `torch.compile()` for ~2x speedup
- Keep BLOCK_SIZE=256, SAMPLE_RATE=48000
### Phase 2 — Compiled inference (after RPi hardware arrives)
- On first boot, compile `neural-amp-modeler-lv2` natively
- Switch to LV2 plugin chain in JACK
- This gives 2-4x more CPU headroom for other FX
### Phase 3 — Rust native (future)
- Port to `nam-rs` for pure-Rust implementation
- A2 Slimmable runtime quality dial
- No libtorch dependency — saves ~300MB on the Pi