<!--
SPDX-License-Identifier: Apache-2.0
Copyright (c) 2026 Fábio Henrique de Lima Silva (fhl.bsb@gmail.com) All rights reserved.
-->
# Fixture Governance & Golden Vector Reference — NeuralAmpModelerCore ↔ NAM-rs
## Source of Truth
All golden vectors in `tests/fixtures/` are generated by **NeuralAmpModelerCore** (Steven Atkinson) — the canonical implementation that trains and generates `.nam` models.
## Repo-Local Third-Party Area
The upstream C++ mirrors used for cross-reference validation are **not
committed** to git. They live under the gitignored `third-party/` directory
inside this repository, populated by `utils/setup-third-party.sh`:
| Directory | Size | Purpose |
| ------------------------------------- | ------- | -------------------------------------------------------------------------------- |
| `third-party/NeuralAmpModelerCore/` | ~143 MB | Upstream C++ reference (`render` tool for ALL goldens) |
| `third-party/NeuralAmpModelerPlugin/` | ~164 MB | Upstream C++ IR reference (`dsp::ImpulseResponse`, cabsim cross-validation only) |
| `third-party/community_models/` | varies | Optional symlink/dir of non-distributable community test models |
| `build/namcore_render/` | ~6 MB | CMake build artifacts from NeuralAmpModelerCore (repo-local, gitignored) |
> [!NOTE]
> The third-party base directory defaults to `third-party/` at this repo root
> and can be overridden via `NAM_THIRD_PARTY_DIR` (or `NAM_CORE_DIR` /
> `NAM_PLUGIN_DIR`) when the layout differs.
>
> **`utils/setup-third-party.sh` syncs both `NeuralAmpModelerCore` and
> `NeuralAmpModelerPlugin`** (including their submodules) into `third-party/`,
> and optionally links `community_models` when `NAM_COMMUNITY_MODELS_SRC` is set.
> Run it after a fresh clone (or when pins change) to match
> [`variables.env`](../variables.env).
### To regenerate all fixtures from scratch
```bash
# Populate / refresh vendor mirrors (pins from variables.env)
./utils/setup-third-party.sh
# Optional: wipe prior C++ build + Core mirror for a clean regenerate
rm -rf third-party/NeuralAmpModelerCore build/namcore_render
./utils/setup-third-party.sh
# Run full regeneration (builds render tool, generates goldens except known gaps)
./tests/fixtures/golden_gen_build.sh
# Full audit suite (includes long-duration C++ cross-validation)
./utils/tests-long.sh
```
> [!NOTE]
> `utils/tests-long.sh` no longer carries bash golden lists or auto-regeneration:
> golden/fixture presence is gated fail-closed by the Rust
> `catalog_preflight` (V1 + V2 golden catalogs in `src/testing/catalog.rs`) and
> `check_freshness` (nam_freshness manifest). To regenerate missing vectors,
> run `./tests/fixtures/golden_gen_build.sh` (C++ toolchain + local
> NeuralAmpModelerCore dependencies required). The former
> `NAM_AUTO_BUILD_GOLDENS` / `NAM_SKIP_GOLDEN_BUILD` knobs were removed.
When the pinned upstream commits are updated (after re-baselining goldens),
update the values in [`variables.env`](../variables.env)
(sourced by both `golden_gen_build.sh` and `setup-third-party.sh`). All goldens
generated from the new version must pass both Layer 1 and Layer 2 validation
before committing.
## Golden Vector Catalog & Fixtures Inventory
| Golden File | `.nam` Model | Nature | Topology |
| -------------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `golden_wavenet_standard.bin` | `BossWN-standard.nam` | Community real (Boss Waza, trained) | CH=16, K=3, HEAD=8, 20 layers |
| `golden_wavenet_lite.bin` | `EVH-5150-Lite.nam` | **Community real** (CH=12 WaveNet Lite, non-distributable) | CH=12, K=3, HEAD=6, 20 layers |
| `golden_wavenet_feather.bin` | `BossWN-feather.nam` | Community real (Boss Waza, trained) | CH=8, K=3, HEAD=4, 20 layers |
| `golden_wavenet_nano.bin` | `BossWN-nano.nam` | Community real (Boss Waza, trained) | CH=4, K=3, HEAD=2, 20 layers |
| `golden_wavenet_a1_standard.bin` | `wavenet_a1_standard.nam` | **Official real** (trained model, 407 KB, md5 `1c540f40…`) | CH=16, K=3, HEAD=8, 20 layers |
| `golden_lstm_1x16.bin` | `BossLSTM-1x16.nam` | Community real (Boss Waza, trained) | 1 layer, H=16 |
| `golden_lstm_2x8.bin` | `BossLSTM-2x8.nam` | Community real (Boss Waza, trained) | 2 layers, H=8 |
| `golden_lstm_official.bin` | `lstm.nam` | **Official real** (NAM example model, 1 layer H=3) | 1 layer, H=3 |
| `golden_wavenet_a2_full.bin` | `wavenet_a2_full.nam` | **Synthetic** (weights calibrated for fast-path parity; not FiLM/official) | CH=8, K=6/15, 23 layers — cross-reference vs C++ v0.5.4 |
| `golden_wavenet_a2_lite.bin` | `wavenet_a2_lite.nam` | **Synthetic** (weights calibrated for fast-path parity; not FiLM/official) | CH=3, K=6/15, 23 layers — cross-reference vs C++ v0.5.4 |
| `golden_wavenet_a2_film_full.bin` | `wavenet_a2_film_full.nam` | **Synthetic** (FiLM — PM-05 conformism, RF1) | CH=8, K=6/15, 23 layers, FiLM post-mod — cross-reference vs C++ generic |
| `golden_wavenet_a2_film_lite.bin` | `wavenet_a2_film_lite.nam` | **Synthetic** (FiLM — PM-05 conformism, RF1) | CH=3, K=6/15, 23 layers, FiLM post-mod — cross-reference vs C++ generic |
| `golden_a2_dynamic_gated_ch8.bin` | `a2_dynamic_gated_ch8.nam` | **Synthetic** (dynamic gating engine parity) | CH=8, 3 gated layers — cross-reference vs C++ generic |
| `golden_a2_dynamic_blended_ch3.bin` | `a2_dynamic_blended_ch3.nam` | **Synthetic** (dynamic blending engine parity) | CH=3, 2 blended layers — cross-reference vs C++ generic |
| `golden_linear_fft_rf320.bin` | `linear_fft_rf320.nam` | **Synthetic** (functional parity, partitioned convolution) | RF=320, 2 channels, block=128 |
| `golden_linear_fft_rf2048.bin` | `linear_fft_rf2048.nam` | **Synthetic** (functional parity, partitioned convolution) | RF=2048, 1 channel, block=1024 |
| `golden_linear_fft_rf4096.bin` | `linear_fft_rf4096.nam` | **Synthetic** (functional parity, partitioned convolution) | RF=4096, 1 channel, block=2048 |
| `golden_linear_fft_rf8192.bin` | `linear_fft_rf8192.nam` | **Synthetic** (functional parity, partitioned convolution) | RF=8192, 1 channel, block=4096 |
| `golden_a2_example.bin` | `a2_example.nam` | **Synthetic** (SlimmableContainer with A2 submodels) | CH=3→6, 23 layers |
| `golden_convnet_test.bin` | `convnet_test.nam` | **Synthetic** (ConvNet parity) | CH=8, 6 blocks |
| `golden_lstm_dyn_test.bin` | `lstm_dyn_test.nam` | **Synthetic** (LSTM dynamic path parity) | 1 layer, H=7 |
| `golden_wavenet_a2_film_chaos_stress.bin` | `wavenet_a2_film_chaos_stress.nam` | **Synthetic** (FiLM, pre-fix chaos stress snapshot) | CH=3, bottleneck=3, FiLM conv/input_mixin/activation/layer1x1 |
| `golden_wavenet_a2_film_input_mixin_pre.bin` | `wavenet_a2_film_input_mixin_pre.nam` | **Synthetic** (FiLM, isolated input_mixin_pre regression — Bug C1) | CH=3, bottleneck=3, FiLM input_mixin_pre only |
| `golden_wavenet_a2_max.bin` | `wavenet_a2_max.nam` | **Official real** — KB-A2-MAX known bug (guard TR1.1; meter ~0.23 dB; §4.4.3) | CH=4, cond=8 FiLM; retained for future reopen diagnostics |
| `golden_wavenet_app_evh.bin` | (non-distributable) | Community real (EVH Stealth 100) | — |
| `golden_wavenet_boss_bd2.bin` | (non-distributable) | Community real (Boss BD-2 H2O Mod) | — |
| `golden_wavenet_condition_dsp.bin` | `wavenet_condition_dsp.nam` | **Official real** (FiLM+condition_dsp, near-bit-exact) | CH=3, cond=3 FiLM, post-FiLM DSP |
| `golden_wavenet_dyn_free.bin` | `wavenet_dyn_free.nam` | **Synthetic** (WaveNet free-shape dynamic path) | CH=7/4, free geometry |
| `golden_wavenet_official.bin` | `wavenet_official.nam` | **Official real** (CH=3 free geom, dynamic path) | CH=3, K=3 |
| `golden_wavenet_slammin_marshall.bin` | (non-distributable) | Community real (Slammin Marshall J45) | — |
| `golden_lstm_1x10.bin` | `lstm_1x10.nam` | **Synthetic** (Uncatalogued LSTM hidden size) | 1 layer, H=10 |
| `golden_lstm_2x24.bin` | `lstm_2x24.nam` | **Synthetic** (Uncatalogued LSTM hidden size) | 2 layers, H=24 |
| `golden_lstm_3x8.bin` | `lstm_3x8.nam` | **Synthetic** (3-layer LSTM topology) | 3 layers, H=8 |
| `golden_convnet_nobn.bin` | `convnet_nobn.nam` | **Synthetic** (ConvNet without BatchNorm) | CH=8, 6 blocks |
| `golden_convnet_relu.bin` | `convnet_relu.nam` | **Synthetic** (ConvNet with ReLU activation) | CH=8, 6 blocks |
| `golden_convnet_silu.bin` | `convnet_silu.nam` | **Synthetic** (ConvNet with SiLU activation) | CH=8, 6 blocks |
| `golden_linear_nobias.bin` | `linear_nobias.nam` | **Synthetic** (Linear without bias) | RF=4, bias=0.0 |
| `golden_wavenet_a1_secondary_act.bin` | `wavenet_a1_secondary_act.nam` | **Synthetic** (Non-null secondary activation rejection fixture) | CH=16, non-null secondary activation |
| `golden_wavenet_condition_lstm.bin` | `wavenet_condition_lstm.nam` | **Synthetic** (WaveNet + LSTM condition_dsp; skip_reason) | CH=3, cond=3, LSTM 1×3 |
| `golden_cabsim_cpp_short.bin` | N/A | C++ reference (synthetic IR) | Cabsim Short IR (64 samples) C++ dsp::ImpulseResponse |
| `golden_cabsim_cpp_medium.bin` | N/A | C++ reference (synthetic IR) | Cabsim Medium IR (512 samples) C++ dsp::ImpulseResponse |
| `golden_cabsim_cpp_long.bin` | N/A | C++ reference (synthetic IR) | Cabsim Long IR (8192 samples) C++ dsp::ImpulseResponse |
> [!WARNING]
> **Automation gaps tracked:**
>
> - `golden_wavenet_a2_film_{full,lite}.bin`, `golden_a2_dynamic_gated_ch8.bin`, and
> `golden_a2_dynamic_blended_ch3.bin` are now **auto-generated** by
> `golden_gen_build.sh` for v1 (48000 Hz). Their `.nam` source models are built by
> `tests/fixtures/generate_a2_fixtures.py`, and v1 goldens are rendered by the
> script's v1 loop. v2 multi-SR goldens are intentionally skipped
> (`v2_scope=none` in `src/testing/catalog.rs::GOLDEN_GEN_CATALOG`) — see the
> rationale comment in `golden_gen_build.sh` for the technical explanation.
> - `golden_cabsim_cpp_stress.bin` has been **removed**. `tests/fixtures/render_ir.cpp`
> only implements 3 scenarios (short, medium, long) because the C++
> `dsp::ImpulseResponse` engine hard-caps IR length at 8192 samples (`mMaxLength`),
> so a genuine C++ cross-reference for a 65536-sample IR is not achievable with the
> vendored engine — see `tests/parity/cabsim_cpp_parity.rs` for the Rust-side rationale.
>
> **Nature classification:**
>
> - **Official real** — modelo `.nam` com pesos treinados, publicado pelo projeto NAM oficial (`sdatkinson/NeuralAmpModelerCore`).
> - **Community real** — modelo `.nam` com pesos treinados, publicado pela comunidade (Boss Waza Tube Amp Expander).
> - **Synthetic** — pesos auto-gerados/calibrados para fins de validação; **não** representam timbres de amplificador reais.
> - **C++ reference** — vetores de referência gerados pelo C++ upstream (`dsp::ImpulseResponse`); não são modelos NAM.
> [!NOTE]
> **Resumo rápido:** 5 goldens são **oficial real** (A1-Standard, LSTM Official, A2-Max-disabled,
> Condition-DSP, Official CH=3).
> 9 são **community real** (WaveNet Standard, Lite CH=12, Feather, Nano,
> LSTM 1×16/2×8 — todos Boss Waza — mais EVH Stealth 100, Boss BD-2, Slammin Marshall J45,
> todos não-distributáveis).
> 24 são **synthetic** (A2-Full, A2-Lite, A2-FiLM-Full, A2-FiLM-Lite,
> A2-Dynamic-Gated-CH8, A2-Dynamic-Blended-CH3, Linear-FFT-RF320/2048/4096/8192,
> A2-Example, ConvNet-Test, ConvNet-NoBN, ConvNet-ReLU, ConvNet-SiLU, Linear-NoBias,
> LSTM-Dyn-Test, LSTM 1×10, LSTM 2×24, LSTM 3×8, A2-FiLM-Chaos-Stress,
> A2-FiLM-InputMixinPre, WaveNet-Dyn-Free, WaveNet A1 Secondary Act, WaveNet Condition LSTM).
> Os 3 goldens C++ cabsim são vetores de referência do upstream.
### Model Files and Trust Levels Registry
All captures and models in `.nam` and `.json` format located under [`tests/fixtures/models/`](../tests/fixtures/models/) are audited to verify quality, legal provenance, and usefulness in integration tests.
> [!NOTE]
> **Registry completeness:** all models in `tests/fixtures/models/` are accounted for in the
> tables above and have dedicated provenance sections below.
#### 1. High-Quality Real Models (Git Versioned)
These models have real trained weights, excellent fidelity, and are certified for authorship/permissive licensing compatible with `nam-rs` distribution:
| Model / Fixture | Nature | Architecture | Quality & Confidence | License & Provenance | Purpose in Tests |
| ------------------------- | -------------- | ------------------------------- | ------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BossLSTM-1x16.nam` | Community Real | LSTM (1 layer, H=16) | **High** (Proven) | Boss Waza TAE Community. Compatible license. | Golden vectors v1, v2 multi-SR and zero-allocation checks. |
| `BossLSTM-2x8.nam` | Community Real | LSTM (2 layers, H=8) | **High** (Proven) | Boss Waza TAE Community. Compatible license. | Golden vectors v1, v2 multi-SR and weight layout roundtrip. |
| `BossWN-standard.nam` | Community Real | WaveNet (CH=16, K=3, 20 layers) | **High** (Proven) | Boss Waza TAE Community. Compatible license. | E2E SPSC pipeline, golden vectors v1/v2, zero-allocation. |
| `BossWN-feather.nam` | Community Real | WaveNet (CH=8, K=3, 20 layers) | **High** (Proven) | Boss Waza TAE Community. Compatible license. | Golden vectors v1/v2, zero-allocation tests. |
| `BossWN-nano.nam` | Community Real | WaveNet (CH=4, K=3, 20 layers) | **High** (Proven) | Boss Waza TAE Community. Compatible license. | Golden vectors v1/v2, multi-SR checks. |
| `wavenet_a1_standard.nam` | Official Real | WaveNet (CH=16, K=3, 20 layers) | **High** (Excellent / Steve Atkinson) | Pinned `sdatkinson/NeuralAmpModelerCore` example. CC0. | Golden vectors v1/v2. |
| `wavenet_official.nam` | Official Real | WaveNet (CH=3, K=3, free geom) | **High** (Excellent / Steve Atkinson) | Pinned `sdatkinson/NeuralAmpModelerCore` example. CC0. | Permanent clone-protection regression fixture. Golden vectors v1/v2, self-consistency (dynamic path). Heterogeneous clone exact tested in `wavenet_clone_exact_test.rs`. |
| `lstm.nam` | Official Real | LSTM (1 layer, H=3) | **High** (Excellent / Steve Atkinson) | Pinned `sdatkinson/NeuralAmpModelerCore` example. CC0. | Golden vectors v1/v2, C++ live parity. |
#### 2. Synthetic Models and Mocks (Targets for Progressive Replacement)
These files contain synthetic weights or partial/invalid structures. They exist exclusively to validate boundary limits, error detection, and specific numerical regressions, with the goal of being progressively replaced by real, licensed models as they become available:
| Model / Fixture | Nature | Architecture | Quality & Confidence | License & Provenance | Purpose in Tests |
| ------------------------------------- | ---------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BossWN-lite.nam` | Synthetic | WaveNet (CH=12, K=3, 20 layers) | **Obsolete** — replaced by `EVH-5150-Lite.nam` | Artificially generated. | Legacy fixture; no longer used in active tests. Superseded by real community model. |
| `linear_test.nam` | Synthetic | Linear (RF=4, bias=0.1) | **High** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Linear parity and linear_golden. |
| `wavenet_a2_full.nam` | Synthetic | WaveNet A2 (CH=8, K=6/15, 23 layers) | **High** (Fast-path parity) | Calibrated weights. Apache-2.0. | Parity A2 fast-path (Full), container submodel. |
| `wavenet_a2_lite.nam` | Synthetic | WaveNet A2 (CH=3, K=6/15, 23 layers) | **High** (Fast-path parity) | Calibrated weights. Apache-2.0. | Parity A2 fast-path (Lite), container submodel. |
| `wavenet_a2_container.nam` | Synthetic | SlimmableContainer (A2 Lite & Full) | **High** (Functional parity) | Container joining the two submodels above. Apache-2.0. | Golden vectors container (A2 Lite and A2 Full submodel swaps). |
| `keras_unsupported.json` | Mock / Synthetic | Keras Legacy format (H5 Mock) | N/A (Negative mock) | Clean structure without weights (legal mitigation). Apache-2.0. | Tests graceful rejection of legacy Keras format (F13). |
| `mock_a2.nam` | Mock / Synthetic | WaveNet (ReLU config, zero weights) | N/A (Negative mock) | Empty model for failure testing. Apache-2.0. | Permanent negative regression fixture. Tests failure transition on the audio thread (`RT_STATUS_MODEL_LOAD_FAILED`). |
| `slimmable_container.nam` | Mock / Synthetic | SlimmableContainer (3 submodels: LSTM 1x3 + WaveNetDyn + Nano) | **High** (Topology routing, ReLU verified) | Container exercising topology dispatch across architectures. Apache-2.0. | Permanent regression fixture. Validates robust submodel routing (LSTM fast-path + WaveNetDyn free-geometry + Nano SKU) with ReLU. |
| `slimmable_wavenet.nam` | Official / Core | WaveNet slimmable (`slice_channels_uniform`, `allowed_channels=[1,2,3]`) | **Disclaimer** — inference-only; sem claim de paridade multi-size NAMCore | Upstream-style slimmable single-net fixture. Apache-2.0 / core distribution. | **Not** a reject mock. `test_loader_gap_slimmable_wavenet` and `test_slimmable_wavenet_inference_and_breakpoints` assert successful build. C++ multi-size parity architecturally infeasible (NAMCore lacks channel-slicing API). |
| `wavenet_a2_max.nam` | Official Real | WaveNet (CH=4, cond=8 FiLM, head1x1) | **Known bug KB-A2-MAX** (fail-closed TR1.1; prod×C++ **SNR ≈ 0.23 dB**; see [cpp_parity_map.md](cpp_parity_map.md) §4.4.3) | Steve Atkinson official example. CC0. | Permanent known bug. `test_wavenet_a2_max_dispatch_rejected` asserts `Err` (KB-A2-MAX). Golden/meter/live ignored — not CI gates. Unlock: `NAM_A2_MAX_UNLOCK=1` (test/testing only). |
| `wavenet_condition_dsp.nam` | Official Real | WaveNet (CH=3, cond=3 FiLM, post-FiLM DSP) | **High** (Near-bit-exact — SNR=139.5 dB) | Steve Atkinson official example. CC0. | Permanent clone-protection regression fixture. Golden vectors v1 (dynamic path with FiLM+condition_dsp, §6 of this doc). Heterogeneous clone exact tested in `wavenet_clone_exact_test.rs`. |
| `wavenet_a2_film_full.nam` | Synthetic | WaveNet A2 (CH=8, FiLM active) | **Medium** (FiLM parity — SNR=36.0 dB, RF1) | Generated by `generate_a2_fixtures.py`. Apache-2.0. | Golden vectors v1 (A2+FiLM dynamic path, §FiLM Fixtures section). |
| `wavenet_a2_film_lite.nam` | Synthetic | WaveNet A2 (CH=3, FiLM active) | **Medium** (FiLM parity — SNR=18.1 dB, RF1) | Generated by `generate_a2_fixtures.py`. Apache-2.0. | Golden vectors v1 (A2+FiLM dynamic path, §FiLM Fixtures section). |
| `linear_fft_rf320.nam` | Synthetic | Linear FFT (RF=320, 2 channels, block=128) | **Medium** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), partitioned convolution cross-validation. Graceful skip if golden absent. |
| `linear_fft_rf2048.nam` | Synthetic | Linear FFT (RF=2048, 1 channel, block=1024) | **Medium** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), partitioned convolution cross-validation. Graceful skip if golden absent. |
| `linear_fft_rf4096.nam` | Synthetic | Linear FFT (RF=4096, 1 channel, block=2048) | **Medium** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), partitioned convolution cross-validation. Graceful skip if golden absent. |
| `linear_fft_rf8192.nam` | Synthetic | Linear FFT (RF=8192, 1 channel, block=4096) | **Medium** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), partitioned convolution cross-validation. Graceful skip if golden absent. |
| `a2_example.nam` | Synthetic | SlimmableContainer (A2 submodels) | **Medium** (Container routing) | Generated by `generate_a2_fixtures.py`. Apache-2.0. | Golden vectors v1 (A2 example container, dynamic path). |
| `convnet_test.nam` | Synthetic | ConvNet (CH=8, 6 blocks) | **High** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), ConvNet topology validation. |
| `lstm_dyn_test.nam` | Synthetic | LSTM-Dyn (1 layer, H=7) | **High** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), LSTM dynamic path validation. |
| `wavenet_a2_film_chaos_stress.nam` | Synthetic | WaveNet A2 (CH=3, FiLM 4 slots, non-identity weight scaling) | **High** (Numerical stress) | Preserved snapshot of pre-fix `wavenet_a2_film_lite.nam` (commit `b96e4c7d`). Apache-2.0. | Golden vectors v1 (FiLM chaos stress, §A2-FiLM Chaos Stress). |
| `wavenet_a2_film_input_mixin_pre.nam` | Synthetic | WaveNet A2 (CH=3, FiLM input_mixin_pre only) | **High** (Bug C1 regression) | Generated by `generate_a2_fixtures.py`. Seed 145. Apache-2.0. | Golden vectors v1 (isolated input_mixin_pre FiLM, §A2-FiLM InputMixinPre). |
| `wavenet_condition_lstm.nam` | Synthetic | WaveNet + LSTM condition_dsp (CH=3, cond=3, LSTM 1×3) | **Medium** (C++ golden blocked — C++ upstream channel mismatch) | Generated by `generate_a2_fixtures.py`. Apache-2.0. | Golden vectors v1 (WaveNet with LSTM condition_dsp; golden C++ IMPOSSÍVEL — skip_reason). |
| `wavenet_dyn_free.nam` | Synthetic | WaveNetDyn (CH=7/4, free geometry) | **High** (Functional parity) | Simple weights defined for testing. Apache-2.0. | Golden vectors v1 (@48k), WaveNet free-shape dynamic path validation. |
| `lstm_1x10.nam` | Synthetic | LSTM (1 layer, H=10) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | Uncatalogued LSTM hidden size validation. |
| `lstm_2x24.nam` | Synthetic | LSTM (2 layers, H=24) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | Uncatalogued LSTM hidden size validation. |
| `lstm_3x8.nam` | Synthetic | LSTM (3 layers, H=8) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | 3-layer LSTM topology validation. |
| `convnet_nobn.nam` | Synthetic | ConvNet (CH=8, 6 blocks, no BatchNorm) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | ConvNet without BatchNorm validation. |
| `convnet_relu.nam` | Synthetic | ConvNet (CH=8, 6 blocks, ReLU) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | ConvNet with ReLU activation validation. |
| `convnet_silu.nam` | Synthetic | ConvNet (CH=8, 6 blocks, SiLU) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | ConvNet with SiLU activation validation. |
| `linear_nobias.nam` | Synthetic | Linear (RF=4, bias=0.0) | **High** (Functional parity) | Generated by `generate_fixtures.py`. Apache-2.0. | Linear model without bias validation. |
| `wavenet_a1_secondary_act.nam` | Mock / Synthetic | WaveNet (non-null secondary_activation) | N/A (Negative mock) | Generated by `generate_fixtures.py`. Apache-2.0. | Rejection test fixture for non-null secondary activation (F1/F5). |
#### 3. Non-Distributable Model Management (`third-party/community_models` / `tests/fixtures/models-nondist`)
Due to legal redistribution restrictions on many community captures, they must **not** be
committed to this repository. Local developers working on NeuralAmpModeler-rs can point tests
at a private archive via either `third-party/community_models/` (recommended symlink) or
`tests/fixtures/models-nondist/`:
##### Model Resolution Order (`golden_gen_build.sh`)
`golden_gen_build.sh` resolves every `.nam` model through a shared `resolve_nam_model()` function
that mirrors the search order of `tests/common/io_helpers.rs::model_path`:
| Step | Location | Environment override |
|:---- |:--------------------------------------------------------------------- |:--------------------- |
| 1 | `$NAM_MODELS_DIR/<file>` | `NAM_MODELS_DIR` |
| 2 | `third-party/community_models/<file>` (or `$NAM_THIRD_PARTY_DIR/...`) | `NAM_THIRD_PARTY_DIR` |
| 3 | `tests/fixtures/models-nondist/<file>` | — |
| 4 | `tests/fixtures/models/<file>` (default, distributed) | — |
> [!IMPORTANT]
> **Skip rule:** a model is **SKIP**ped only if the file is absent in **all** search paths. If the
> file exists at any path, the gen script **must** either render the golden successfully or
> fail hard — never skip silently. If a model exists in `third-party/community_models/` it will
> be found and rendered.
> [!NOTE]
> **Nondist golden policy:** non-distributable models (EVH-5150-Lite, APP-EVH, Boss BD-2,
> SLAMMIN MARSHALL) live under `third-party/community_models/` or `models-nondist/`. When
> present, the gen script renders their goldens into `tests/fixtures/` as usual. The `.nam`
> files themselves must **not** be committed to git (license restriction). The golden `.bin`
> files follow the project policy:
>
> - **Committed to the repo** when the golden alone does not expose the copyrighted model
> weights (binary activation outputs are not derivative works of the training corpus).
> - **Gitignored** or kept local if specific legal review requires it.
> - Goldens for models that could not be found at any path are **not** listed as "generated" in
> the script's final summary — preserving summary honesty.
- Point `third-party/community_models` at your private archive (preferred), or use
`models-nondist`:
```bash
# After clone / setup-third-party:
ln -s /path/to/your/nam_models third-party/community_models
# or:
NAM_COMMUNITY_MODELS_SRC=/path/to/your/nam_models ./utils/setup-third-party.sh
# or:
ln -s /path/to/your/nam_models tests/fixtures/models-nondist
```
- The integration test suite (`tests/models/nondist_validation.rs`) automatically detects
`tests/fixtures/models-nondist` or falls back to `third-party/community_models/`, running a
comprehensive verification battery (parsing, determinism, block size invariance, and denormal
silence stability) on all captures found. It skips validation gracefully without failure if
neither directory is present.
##### Catalog & Model Discovery (`manifest.json`)
`manifest.json` is the machine-readable catalog of all non-distributable models. It enables
`nondist_validation.rs` to discover models and validate expected classification
(e.g., "this CH=32 model should route to WaveNetDyn"). Each entry contains:
```json
{
"filename": "EVH-5150-Lite.nam",
"sha256": "4404e56f...",
"expected_class": "WaveNet A1 Lite (CH=12)",
"is_goal_target": true,
"name": "EVH-5150-Lite.nam",
"author": "Unknown Author"
}
```
Without the manifest, only determinism, block-invariance, and stability are checked (no
classification validation).
##### WaveNet Lite Golden Gate (Non-Distributable)
The cross-parity gate for the WaveNet Lite model in `tests/models/golden_vectors.rs`
(`test_golden_vectors_wavenet_lite`, `test_golden_vectors_v2_wavenet_lite`) is conditioned
on the presence of the local model `EVH-5150-Lite.nam` inside the `models-nondist` directory.
This model is a community-real capture (CH=12, K=3, HEAD=6, 20 layers) and cannot be
redistributed under the project's Apache-2.0 license.
In a clean environment (e.g., third-party CI) where the `models-nondist` directory is absent
and only the `golden_wavenet_lite.bin` golden file is present, the test will **skip**
gracefully with an `eprintln!` message — preserving test honesty (no placebo gate) but
sacrificing coverage of the WaveNet Lite golden cross-reference.
To re-enable full WaveNet Lite golden coverage:
1. Place `EVH-5150-Lite.nam` in your local `tests/fixtures/models-nondist/` directory.
2. Regenerate golden vectors from the C++ reference:
```bash
./tests/fixtures/golden_gen_build.sh
```
3. Run the golden tests:
```bash
cargo test --test models test_golden_vectors_wavenet_lite
cargo test --test models test_golden_vectors_v2_wavenet_lite -- --ignored
```
##### Model Maintenance Tool: `utils/check-model.sh` (`examples/inspect_model.rs`)
`utils/check-model.sh` (backed by the `inspect_model` example) is the canonical tool for inspecting and managing model data (`.nam` and `.namb`). It classifies files by architecture, topology, gain staging, metadata, and engine compatibility status.
**Interactive mode** (colored terminal output):
```bash
utils/check-model.sh tests/fixtures/models-nondist/EVH-5150-Lite.nam
```
Outputs architecture, version, topology classification (`[STANDARD / SUPPORTED]` or `[CUSTOM / TARGET-RESEARCH]`), receptive field, loudness, dBu levels, and file SHA-256.
**(Re)generating the manifest** (machine-readable JSON):
```bash
utils/check-model.sh --manifest tests/fixtures/models-nondist/*.nam \
> tests/fixtures/models-nondist/manifest.json
```
Regenerate whenever non-distributable models are added, removed, or updated. The `--manifest`
flag produces a clean JSON array suitable for automated consumption by the test suite.
**v2 multi-SR files** (`golden_<model_id>_v2_<sr>.bin`): Stress Signal v2 (5s, 5 categories), generated by `golden_gen_build.sh`. See table below for SR coverage per model.
| SR Coverage | Models |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| All 5 SRs (44.1/48/88.2/96/192k) | `wavenet_feather`, `wavenet_nano`, `wavenet_lite`, `wavenet_a1_standard` |
| 4 SRs (44.1/48/88.2/96k, Excl. 192 kHz) | `lstm_1x16`, `lstm_2x8` — recurrent drift > 18 dB SNR at 192 kHz over 960k samples; `skip_srs=192000` |
| 48 kHz only (`48k_only`) | `wavenet_standard`, `lstm_official`, `wavenet_a2_full`, `wavenet_a2_lite`, `wavenet_official`, `wavenet_condition_dsp`, `wavenet_dyn_free`, `lstm_dyn_test`, `convnet_test`, `wavenet_a2_max`, `wavenet_app_evh`, `wavenet_boss_bd2`, `wavenet_slammin_marshall`, `a2_example` |
| Uncatalogued synthetic pending build (`48k_only`) | `lstm_1x10`, `lstm_2x24`, `lstm_3x8`, `convnet_nobn`, `convnet_relu`, `convnet_silu`, `linear_nobias` |
| No v2 golden (`v2_scope=none`) | `a2_dynamic_gated_ch8`, `a2_dynamic_blended_ch3`, `wavenet_a2_film_full`, `wavenet_a2_film_lite`, `wavenet_a2_film_chaos_stress`, `wavenet_a2_film_input_mixin_pre`, `linear_fft_rf{320,2048,4096,8192}`, `wavenet_a1_secondary_act`, `wavenet_condition_lstm` |
> [!NOTE]
> `v2_scope=none` entries have **no committed v2 golden binary at all** — not just 48 kHz. This is intentional: the C++ `a2_fast` path rejects FiLM-conditioned models and falls back to the Eigen-based generic WaveNet engine, which does not consistently support multi-SR FiLM renderings. The dynamic engines' coverage is exercised via live C++ cross-validation (`cpp_parity.rs`) instead. Linear FFT and container models have `v2_scope=none` for analogous reasons. See the rationale comment in `golden_gen_build.sh` and `src/testing/catalog.rs::GOLDEN_GEN_CATALOG`, plus [cpp_parity_map.md](cpp_parity_map.md) §3.3.
**v2 files** (`golden_<model_id>_v2_<sr>.bin`): Multi-sample-rate goldens using Stress Signal v2 (5 seconds, multi-component, covering all 5 stimulus categories GA/FRG/P/BA/PA in a single file). Naming schema:
```text
golden_<model_id>_v2_<sr>.bin
```
Examples:
| Golden File | Model ID | SR |
| -------------------------------------- | ------------------ | ----- |
| `golden_wavenet_standard_v2_48000.bin` | `wavenet_standard` | 48000 |
| `golden_lstm_1x16_v2_88200.bin` | `lstm_1x16` | 88200 |
| `golden_wavenet_feather_v2_44100.bin` | `wavenet_feather` | 44100 |
**SR coverage varies per model:** Models with an explicit `sample_rate` field in their NAM JSON (WaveNet Standard, LSTM Official, A2-Full, A2-Lite) only generate at 48 kHz due to C++ render tool constraints. Models without `sample_rate` (e.g. WaveNet Feather, Nano, Lite, A1 Standard, LSTM 1×16/2×8) generate at all 5 supported rates (44100/48000/88200/96000/192000).
**Layer-2 soak tests:** The corresponding tests in `tests/models/golden_vectors.rs` are `#[ignore]` because the 5-second v2 signals are ~200× longer than v1 (240k–960k vs 2048 samples), making them impractical for debug-mode CI (~2 min per model). Run with `cargo test -- --include-ignored` for comprehensive multi-SR validation. The committed `.bin` files serve as reproducible C++ reference artifacts for offline/CI-scheduled validation.
**v1 files** (`golden_<model>.bin`): Single-stimulus goldens using Stress Signal v1 (2048 samples @ 48 kHz). Maintained for retro compatibility — v1 goldens use model-only naming without tone_id. See table below.
## Binary format (.golden.bin)
```text
[u32 num_samples LE]
[f32×N input samples LE] — stress signal (v1: 2048 samples @ 48 kHz; v2: 5s × SR)
[f32×N expected output LE] — output from NeuralAmpModelerCore (render tool)
```
## Stress Signal v1 (2048 samples @ 48 kHz ≈ 42.7 ms)
Replaces the 440 Hz sine wave with a deterministic multi-component signal:
| Behavior to test | Signal component |
| -------------------------------------- | ----------------------------------- |
| Frequency response (low → high) | Chirp sweep 220 Hz → 3520 Hz |
| Harmonic intermodulation (real guitar) | Low-E harmonics (82/165/330/659 Hz) |
| Transient response (note attack) | Isolated impulse (+0.9) at 25% |
| Amplitude dynamics | Attack–sustain–release envelope |
| Near-silence / denormals behavior | Fade-to-silence (release tail) |
## Stress Signal v2 (5 seconds, multi-SR: 44.1k/48k/96k/192k)
Comprehensive multi-component signal with 6 segments:
| Segment | Time | Component | Category |
| ------- | -------- | ---------------------------------- | --------------- |
| GA-1 | 0.0–1.0s | Single note Low-E + bend + vibrato | Guitar Amp |
| FRG-1 | 1.0–2.0s | Power chord E2+E3+B3 + ADSR | Full Rig Guitar |
| P-1 | 2.0–2.5s | Palm-mute 16 hits @ 120 BPM | Pedal/Transient |
| P-2 | 2.5–3.5s | Pinch harmonic train + saw sweep | Pedal/Transient |
| BA-1 | 3.5–4.5s | Bass amp Low-A 55 Hz + 5 harmonics | Bass Amp |
| PA-1 | 4.5–5.0s | Chord C-E-G ringing decay (exp) | Post-Amp |
## `tone_id` MUSHRA-Aligned Nomenclature
The `tone_id` naming scheme mirrors the category taxonomy of `a2-mushra-data` (the standardized MUSHRA dataset used by the `t3k-mushra` ecosystem). Each tone_id encodes the stimulus category + a variation index `N`, enabling precise cross-project comparison.
### `tone_id` ↔ `a2-mushra-data` Category Mapping
| `tone_id` | Stress v2 Segment | Time Range | Category (`a2-mushra-data`) | Description |
| --------- | ----------------- | ---------- | --------------------------- | ------------------------------------------------- |
| `GA-N` | GA-N | 0.0–1.0s | Guitar Amp | Single-note Low-E + bend + vibrato, variation `N` |
| `FRG-N` | FRG-N | 1.0–2.0s | Full Rig Guitar | Power chord E2+E3+B3 + ADSR, variation `N` |
| `P-N` | P-N | 2.0–2.5s | Pedal / Transient | Palm-mute 16 hits @ 120 BPM, variation `N` |
| `BA-N` | BA-N | 3.5–4.5s | Bass Amp | Bass amp Low-A 55 Hz + 5 harmonics, variation `N` |
| `PA-N` | PA-N | 4.5–5.0s | Post-Amp / Ringing Decay | Chord C-E-G ringing decay (exp), variation `N` |
**Reserved for future expansion:**
| `tone_id` | Category | Description |
| --------- | ------------- | -------------------------- |
| `FRB-N` | Full Rig Bass | Full rig bass (multi-note) |
| `PB-N` | Pedal Bass | Bass pedal/transient |
> **Canonical MUSHRA testing tool:** [`t3k-mushra`](https://github.com/tone-3000/t3k-mushra) (MIT license) — use for publishing MUSHRA ratings derived from these stimuli.
### Mapping Strategy
- **`N` (variation index):** Labeled as `1`, `2`, … to distinguish different signals within the same category (e.g., `P-1` for palm-mute, `P-2` for pinch harmonic train).
- **Backwards compatibility:** v1 goldens (`golden_<model>.bin`) use Stress Signal v1 (full-signal, single-stimulus) and are maintained as-is.
- **Cross-project alignment:** The `GA`/`FRG`/`P`/`BA`/`PA` prefixes match the category codes used in `a2-mushra-data` (`tone-3000/a2-mushra-data`), enabling direct mapping to MUSHRA categories in `t3k-mushra` tests.
## t3k-mushra Primitives (Ported, MIT-licensed)
The following audio primitives are ported from `t3k-mushra` (`github.com/tone-3000/t3k-mushra`, MIT license):
| Primitive | Function | Source (TS) |
| ------------------------ | --------------------------------- | --------------------------- |
| `synth_tone` | Pluck guitar 6-harmonic + vibrato | `generateSampleAudio.ts:18` |
| `low_pass_1pole` | 1-pole IIR low-pass | `generateSampleAudio.ts:37` |
| `soft_clip` | Symmetric tanh saturation | `generateSampleAudio.ts:58` |
| `add_noise` | White noise via Mulberry32 | `generateSampleAudio.ts:50` |
| `apply_gain` | Linear gain scaling | `generateSampleAudio.ts:66` |
| `fnv1a32` + `Mulberry32` | Deterministic PRNG | `internal/prng.ts:9` |
Attribution: see `NOTICE.txt` and `src/testing/mushra.rs` header.
MUSHRA-compliant variants (from `generateSampleAudio.ts:132-139`):
```text
hidden-ref → reference bit-identical
excellent → reference + noise(0.001)
good → lowpass(9 kHz) + noise(0.002)
fair → lowpass(5 kHz) + softclip(drive=1.6)
poor → lowpass(2.5 kHz) + gain(0.9) + noise(0.01)
anchor → lowpass(3.5 kHz) [MUSHRA anchor canônico]
```
## Precision Metrics (7+ metrics, single-pass fusion)
Each golden test reports multiple metrics computed in a single pass:
| Metric | Formula | What it detects |
| ------------ | --------------------------------- | ------------------------------------------ |
| **MSE** | `Σ(rᵢ - tᵢ)² / N` | Mean error (structural regressions) |
| **MAE** | `max \| rᵢ - tᵢ \|` | Maximum peak absolute error |
| **SNR** | `10 · log₁₀(Σrᵢ² / Σ(rᵢ-tᵢ)²)` | Signal-to-noise ratio (DSP interpretation) |
| **PSNR** | `10 · log₁₀(peak² / MSE)` | SNR normalized by peak |
| **Bits eq.** | `-0.5 · log₂(MSE / signal_power)` | Precision — how many correct float32 bits |
| **ESR** | `Σ(rᵢ-tᵢ)² / Σ rᵢ²` | Error-to-Signal Ratio (perceptual) |
| **LUFS** | ITU-R BS.1770-4 simplified | Loudness (diagnostic) |
ESR baselines from published data (`A2Esr.tsx:19-38`, t3k-mushra):
- A1-Standard median: 0.00623 (−22.1 dB)
- A2-Full median: 0.00334 (−24.8 dB)
- A2-Lite median: 0.00500 (−23.0 dB)
Nam-rs vs C++ parity target: ESR < 1e-3 (−30 dB conservative gate); actual expected < 1e-5.
See [perceptual_validation.md](perceptual_validation.md) for methodology.
## Parity Thresholds
Catalog / headline models (audited 2026-07-02 against `tests/common/validation.rs::topology_thresholds()`
— corrected this pass; the previous revision of this table had drifted from the current, much
tighter calibrated gates on every WaveNet catalog row and two of the three LSTM rows):
| Model | SNR threshold | ESR threshold | Measured (comment in `validation.rs`) |
| --------------------------- |:-------------:|:-------------:| ------------------------------------- |
| LSTM 1×3 (Official) | ≥ 105.0 dB | < 9.0e-11 | SNR=120.8 dB, ESR=8.30e-13 |
| LSTM 1×16 | ≥ 93.0 dB | < 1.5e-9 | SNR=108.5 dB, ESR=1.42e-11 |
| LSTM 2×8 | ≥ 93.0 dB | < 1.7e-9 | SNR=107.8 dB, ESR=1.67e-11 |
| WaveNet Feather (CH=8) | ≥ 100.0 dB | < 1.0e-10 | SNR=133.1 dB, ESR=1.74e-12 |
| WaveNet Standard (CH=16) | ≥ 105.0 dB | < 3.0e-11 | SNR=134.6 dB, ESR=4.99e-13 |
| WaveNet Nano (CH=4) | ≥ 95.0 dB | < 3.0e-10 | SNR=132.0 dB, ESR=3.46e-12 |
| WaveNet Lite (CH=12) | ≥ 105.0 dB | < 3.5e-11 | SNR=122.3 dB, ESR=5.84e-13 |
| WaveNet A1 Standard (CH=16) | ≥ 85.0 dB | < 3.0e-9 | SNR=123.4 dB, ESR=6.62e-11 |
| A2-Lite (CH=3) | ≥ 105.0 dB | < 3.5e-11 | SNR=132.2 dB, ESR=6.08e-14 |
| A2-Full (CH=8) | ≥ 105.0 dB | < 3.0e-11 | SNR=129.5 dB, ESR=1.13e-13 |
> [!IMPORTANT]
> This table is an illustrative subset of the catalog SKUs, not the complete list. Every
> calibrated model — including `wavenet_condition_dsp`, `wavenet_a2_film_{full,lite}`,
> `a2_dynamic_{gated_ch8,blended_ch3}`, `wavenet_dyn_free`, `lstm_dyn_test`, `a2_example`, the
> non-distributable production captures, and the KB-A2-MAX fail-closed `wavenet_a2_max` arm
> (§4.4.3 / §7.1) — is calibrated identically via `topology_thresholds()` in `tests/common/validation.rs`,
> which is the single source of truth. Read that file directly for the full, current, enforced
> list rather than trusting a second hand-copied table — that is exactly how this table drifted
> stale in the first place. `tests/models/threshold_calibration.rs`'s meta-tests
> (`test_all_golden_models_have_calibrated_thresholds`,
> `test_all_calibrated_entries_have_measurement_comments`, `test_all_thresholds_anti_placebo`)
> continuously enforce that every entry in `validation.rs` stays measured, documented, and
> non-placebo — they do **not**, however, enforce that this table stays in sync with
> `validation.rs`; that remains a manual audit responsibility.
>
> WaveNet Lite (CH=12) was migrated from the synthetic `BossWN-lite.nam` (0.9 dB SNR)
> to the real community model `EVH-5150-Lite.nam` (≥ 105 dB SNR).
> See §Non-Distributable Model Management and `tests/common/validation.rs`.
### Principle: "Todo Golden Deve Poder Falhar" (Every Golden Must Be Able to Fail)
A golden test is a **gate** — it exists to catch regressions. Three patterns defeat this purpose:
| Placebo pattern | Why it's not a gate |
|:-------------------------- |:---------------------------------------------------------------------------------------------------------------------------------------- |
| **Self-golden** | Output validated against itself. Passes by definition, catches nothing. |
| **Threshold neutralizado** | SNR ≤ 0 dB, ESR ≥ 1.0, or MSE ≥ 1e29 without rigid SNR+ESR compensation. Metrics that can never fire create a false sense of confidence. |
| **Fallback heurístico** | Silent fallback to `topology_thresholds()` when no calibrated entry exists. |
**Meta-tests enforce this principle** in `tests/models/threshold_calibration.rs`:
| Meta-tests | What it catches |
|:------------------------------------------------------- |:--------------------------------------------------------------------------------------- |
| `test_all_golden_models_have_calibrated_thresholds` | Models without explicit calibrated entry |
| `test_all_calibrated_entries_have_measurement_comments` | Entries without `// Measured: SNR=..., ESR=...` provenance |
| `test_all_thresholds_anti_placebo` | Any single neutralized component: SNR ≤ 0, ESR ≥ 1, or MSE ≥ 1e29 without rigid SNR+ESR |
> [!IMPORTANT]
> **A2 Exception:** The A2 Full/Lite models intentionally use `mse_limit = 1e30` (MSE effectively disabled)
> because their ESR gates are ultra-strict (≤ 8e-8) and SNR gates are ≥ 70 dB. The anti-placebo test
> accepts `mse_limit ≥ 1e29` only when SNR ≥ 40 dB and ESR < 0.1 — ensuring the bypass is compensated
> by rigid remaining gates. A total neutralization (SNR ≤ 0 or ESR ≥ 1) would still fail.
## To regenerate (golden vectors)
See Repo-Local Third-Party Area for the pinned upstream commits and
a complete regeneration walkthrough. Quick commands:
```bash
# v1 goldens only (fast):
cargo run --release --bin gen_stress -- --version v1 --output tests/fixtures/stress_signal.wav
# Full regeneration (v1 + v2, multi-SR):
./tests/fixtures/golden_gen_build.sh
```
## Resampler Reference Fixtures
Resampler correctness test vectors are generated by `tests/fixtures/generate_resampler_reference.py`.
### Files
| File | Description |
| ---------------------------------- | ------------------------------------------------------------------------------------ |
| `resampler_input_{rate}.f32` | Multitone test signal (10 tones, log-spaced 100 Hz to 0.45×Nyquist), raw f32 LE mono |
| `resampler_ref_{from}_to_{to}.f32` | Reference output via ffmpeg libsoxr (precision=33 bits, Chebyshev passband) |
### Rate Pairs
- 44100 → 48000
- 48000 → 44100
- 48000 → 96000
- 96000 → 48000
### Reference Engine
[libsoxr](https://sourceforge.net/projects/soxr/) 0.1.x (SoX Resampler library) via ffmpeg:
- Resampler: soxr
- Precision: 33 bits (~200 dB SNR theoretical)
- Chebyshev passband
### Validation Test
`src/dsp/resampler_test.rs:test_resampler_snr_against_reference` — Measures magnitude at each tone frequency via Goertzel algorithm and computes SNR against the reference. Currently guards at ≥20 dB; the threshold should be tightened toward the aspirational 120 dB as polyphase filter quality improves.
### To regenerate
```bash
python3 tests/fixtures/generate_resampler_reference.py
```
Prerequisites: `ffmpeg` with libsoxr support (`--enable-libsoxr`).
## f64 Reference Anchors
These anchors validate the Rust f64 oracle (`tests/parity/reference_oracle_f64.rs`) against an independent NumPy f64 reference, ensuring the oracle itself is correct (not circular). Generated by `tests/fixtures/scripts/validate_oracle_f64.py`.
### Files f64
| File | Model Family | Description |
| --------------------------------------------------------- | ---------------------- | ------------------------------------------------ |
| `f64_anchors/a2_lite_256_f64.bin` | A2 (CH=3) | 256-sample f64 anchor, lite variant |
| `f64_anchors/a2_max_256_f64.bin` | A2 (CH=4, cond=8) | 256-sample f64 anchor, max variant |
| `f64_anchors/convnet_256_f64.bin` | ConvNet (CH=8) | 256-sample f64 anchor, 6 blocks |
| `f64_anchors/lstm_256_f64.bin` | LSTM (1 layer, H=3) | 256-sample f64 anchor |
| `f64_anchors/sweep_256_48k.bin` | Sweep signal | 256-sample @ 48 kHz chirp input |
| `f64_anchors/sweep_1024_48k.bin` | Sweep signal | 1024-sample @ 48 kHz chirp input |
| `f64_anchors/wavenet_a2_film_full_256_f64.bin` | A2+FiLM (CH=8) | 256-sample f64 anchor, FiLM full variant |
| `f64_anchors/wavenet_a2_film_input_mixin_pre_256_f64.bin` | A2+FiLM (CH=3, slot 2) | 256-sample f64 anchor, input_mixin_pre isolation |
| `f64_anchors/wavenet_a2_film_lite_256_f64.bin` | A2+FiLM (CH=3) | 256-sample f64 anchor, FiLM lite variant |
| `f64_anchors/wavenet_official_256_f64.bin` | WaveNet (CH=3, K=3) | 256-sample f64 anchor, official free-geom |
### Binary Format
```text
[f64×N input samples LE]
[f64×N output samples LE]
```
Input is a deterministic 256-sample chirp sweep signal at 48 kHz (extracted from stress signal v1). Output is the NumPy f64 reference engine forward pass.
### Validation Gate
`tests/parity/reference_oracle_f64.rs` loads each anchor, computes the Rust f64 oracle output, and asserts ESR(anchor vs Rust oracle) < 1e-12 for all model families. This proves the Rust oracle is a correct ground-truth reference before it is used to decompose the f32 production error floor (weight quantization, activation approximation, accumulation precision).
### Regenerate
```bash
python3 tests/fixtures/scripts/validate_oracle_f64.py \
tests/fixtures/models/lstm.nam \
tests/fixtures/f64_anchors/sweep_256_48k.bin \
--architecture LSTM --output tests/fixtures/f64_anchors/lstm_256_f64.bin
```
Repeat for each architecture (WaveNet, A2, A2+FiLM, ConvNet). After any change to the Rust oracle or validation script, regenerate all anchors and confirm ESR < 1e-12 for all families.
Prerequisites: `cmake`, `g++` (or `clang++`, C++20), `cargo` (Rust), `git`.
> Python is no longer required — `gen_stress` and `wav_to_golden` replace the inline Python blocks.
These files are committed to the repository so that the Rust golden vector tests can run without needing to recompile C++. If the golden vectors do not exist, the tests **panic** with instructions to run `golden_gen_build.sh`.
## Generator Scripts
The following scripts produce fixture files tracked by the freshness manifest (`golden_gen_build.sh`). All are deterministic and reproducible.
| Script | Outputs | Prerequisites |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `tests/fixtures/generate_a2_fixtures.py` | `wavenet_a2_{full,lite,film_full,film_lite,film_chaos_stress,film_input_mixin_pre}.nam`, `a2_{example,dynamic_*}.nam`, `wavenet_condition_lstm.nam` | `python3` |
| `tests/fixtures/generate_b1_2_fixtures.py` | `convnet_test.nam`, `wavenet_dyn_free.nam`, `lstm_dyn_test.nam` | `python3` |
| `tests/fixtures/generate_ebu_sequences.py` | `ebu_3341_1_sine_m23.wav`, `ebu_3341_7_sine_m33.wav`, `ebu_3341_sine_m18.wav`, `ebu_3341_dyn_alternating.wav` | `python3` |
| `tests/fixtures/generate_resampler_reference.py` | `resampler_input_{rate}.f32`, `resampler_ref_{from}_to_{to}.f32` | `python3`, `ffmpeg` with libsoxr |
| `tests/fixtures/scripts/gen_mrstft_golden.py` | `mrstft_golden.bin` | `python3`, `numpy` |
| `tests/fixtures/scripts/validate_oracle_f64.py` | `f64_anchors/*.bin` | `python3`, `numpy`, `cmake`, `g++`/`clang++` |
| `src/bin/gen_stress.rs` | `stress_signal.wav`, `stress_signal_v2_{sr}.wav` (Rust binary `gen_stress`) | `cargo` |
| `src/bin/wav_to_golden.rs` | `golden_*.bin` (conversion helper; called by `golden_gen_build.sh`) | `cargo` |
> [!IMPORTANT]
> `generate_b1_2_fixtures.py` produces the ConvNet, WaveNetDyn, and LstmDyn `.nam` model files. It must be re-run if those model architectures change (new weight layouts, new activation functions, etc.). The corresponding golden `.bin` files are re-generated by `golden_gen_build.sh` using the updated models.
## Model Provenance
### `BossWN-lite.nam` — Obsolete Synthetic WaveNet Lite (CH=12)
- **Nature:** Synthetic fixture (weights auto-generated, not trained on real data).
- **Metadata:** Round values, no `sample_rate` field in the JSON.
- **Status:** **Obsolete.** Was an active golden test gate with
a 0 dB SNR threshold (near-noise output). Replaced by the real community model
`EVH-5150-Lite.nam` (non-distributable, ≥ 105 dB SNR).
See `tests/common/validation.rs` and §Non-Distributable Model Management.
### `BossWN-standard.nam`, `BossWN-feather.nam`, `BossWN-nano.nam` — Community real (Boss Waza, trained)
Real NAM models trained by the Boss Waza Tube Amp Expander community. See
[cpp_parity_map.md](cpp_parity_map.md) for per-variant channel/layer counts.
### `BossLSTM-1x16.nam` & `BossLSTM-2x8.nam` — Community real (Boss Waza, trained)
- **Nature:** Real NAM models trained by the Boss Waza Tube Amp Expander community. Validam a arquitetura LSTM com pesos de amplificador reais (1 layer × H=16 e 2 layers × H=8).
- **Golden Fixtures:** `golden_lstm_1x16.bin` and `golden_lstm_2x8.bin`.
- **Provenance:** Generated from C++ `NeuralAmpModelerCore` render tool at pinned commit `9c7b185de346fe0725dea537bcee4bc38b5bb6d6` (v0.5.3, canonical).
- **Command used:**
```bash
render tests/fixtures/models/BossLSTM-1x16.nam tests/fixtures/stress_signal.wav output.wav
wav_to_golden --input output.wav --reference tests/fixtures/stress_signal.wav --output golden_lstm_1x16.bin
```
- **Validation Verdict:** The current committed fixtures are byte-identical to those newly rendered with commit `e49c93e` (SHA-256 match). They differ from `nam-rs_v2.0.0` only at floating-point precision levels (SNR > 120 dB) due to dynamic C++ compiler/build configurations in the original unpinned `v2.0.0` build.
### `lstm.nam`
- **Nature:** Official sample model from `sdatkinson/NeuralAmpModelerCore` (example models).
- **Golden Fixtures:** `golden_lstm_official.bin`.
- **Provenance:** Official model representing 1 layer, H=3. Rendered using the canonical C++ pinned commit `9c7b185` (v0.5.3).
- **Command used:**
```bash
render tests/fixtures/models/lstm.nam tests/fixtures/stress_signal.wav output.wav
wav_to_golden --input output.wav --reference tests/fixtures/stress_signal.wav --output golden_lstm_official.bin
```
### `wavenet_a2_full.nam` & `wavenet_a2_lite.nam` — **Synthetic, NOT official FiLM models**
> [!IMPORTANT]
> **Estes goldens A2 usam pesos sintéticos, NÃO pesos de modelo oficial.** O modelo A2 oficial
> `wavenet_a2_max.nam` é **known bug KB-A2-MAX** (fail-closed TR1.1; prod×C++ **SNR ≈ 0.23 dB**;
> [cpp_parity_map.md](cpp_parity_map.md) §4.4.3). Fixture + golden permanecem para investigação futura
> (unlock só `cfg(test)` / feature `testing` + `NAM_A2_MAX_UNLOCK=1`). O **fast-path** A2
> não suporta FiLM — FiLM é exercitado por `WaveNetA2Dyn` com fixtures sintéticas
> `wavenet_a2_film_full/lite.nam`. `slimmable_wavenet.nam` **carrega** — disclaimer:
> inference-only; sem claim de paridade multi-size NAMCore (NAMCore carece de API de slicing).
>
> **O que estes goldens validam:** paridade numérica Rust↔C++ do **fast-path** da arquitetura A2
> (23 camadas, K=6/15, LeakyReLU, head_scale=0.02). **O que NÃO validam:** timbres de amplificador
> reais ou fidelidade perceptual de modelos A2 treinados.
>
> Para detalhes do conformismo PM-05 com capturas reais de FiLM, ver §FiLM Fixtures abaixo.
- **Nature:** Synthetic fixtures generated by `tests/fixtures/generate_a2_fixtures.py` using the canonical A2 skeleton (23 layers, K=6/15, LeakyReLU, head_scale=0.02).
- **Amplitude regime:** Weights scaled per-channel (Full CH=8: weight=0.28, bias=0.065; Lite CH=3: weight=0.45, bias=0.09) to produce C++ output in the realistic audio regime (Full: peak≈0.15, LUFS≈−22.6; Lite: peak≈0.19, LUFS≈−20.0), replacing the previous near-silence regime (peak≈2e−3, LUFS≈−68). This ensures denormals/FTZ, saturation, and accumulation paths are exercised at meaningful levels.
- **Golden Fixtures:** `golden_wavenet_a2_full.bin`, `golden_wavenet_a2_lite.bin` — cross-reference Rust↔C++ rendered using the same canonical commit `9c7b185de346fe0725dea537bcee4bc38b5bb6d6` (v0.5.3).
- **SNR/ESR:** Full = 79.2 dB / 1.21e−8; Lite = 90.7 dB / 8.58e−10. Thresholds calibrated with ≥8 dB SNR margin and ~6–7× ESR multiplier.
- **Command used:**
```bash
# Generate .nam models
python3 tests/fixtures/generate_a2_fixtures.py
# Render with C++ v0.5.3
render tests/fixtures/models/wavenet_a2_full.nam tests/fixtures/stress_signal.wav full.wav
render tests/fixtures/models/wavenet_a2_lite.nam tests/fixtures/stress_signal.wav lite.wav
# Convert to golden
wav_to_golden --input full.wav --reference tests/fixtures/stress_signal.wav --output tests/fixtures/golden_wavenet_a2_full.bin
wav_to_golden --input lite.wav --reference tests/fixtures/stress_signal.wav --output tests/fixtures/golden_wavenet_a2_lite.bin
```
### `wavenet_a2_film_full.nam` & `wavenet_a2_film_lite.nam` — **FiLM Fixtures (PM-05 conformism)**
> [!NOTE]
> **Conformismo PM-05:** O motor `WaveNetA2Dyn` suporta FiLM nativamente. O modelo
> real `wavenet_a2_max.nam` é **KB-A2-MAX** (fail-closed; §4.4.3) — fixture/golden
> permanecem no repo para reopen futuro, não como gate de release. Fixtures sintéticas
> validam o motor FiLM. Ver [cpp_parity_map.md](cpp_parity_map.md) §4.4.3.
- **Nature:** Synthetic FiLM fixtures generated by `tests/fixtures/generate_a2_fixtures.py` using the canonical A2 skeleton (23 layers, K=6/15, LeakyReLU, head_scale=0.02) with FiLM post-modulation on `conv`, `input_mixin`, `activation`, and `layer1x1` keys (`condition_size=1`).
- **Engine:** Routed to `WaveNetA2Dyn` (dynamic engine with native FiLM support). The C++ `a2_fast.cpp` rejects FiLM and falls back to Eigen-based generic WaveNet — the golden cross-reference validates *cross-engine equivalence*, not bit-exact parity.
- **SNR/ESR:** FiLM-Full (CH=8) = 36.0 dB / 2.50e-4; FiLM-Lite (CH=3) = 18.1 dB / 1.54e-2. Thresholds calibrated per `tests/common/validation.rs` with RF1 flag (FiLM vs generic WaveNet divergence capped and tracked).
- **Golden Fixtures:** `golden_wavenet_a2_film_full.bin`, `golden_wavenet_a2_film_lite.bin` — cross-reference Rust↔C++ rendered at canonical commit `9c7b185de346fe0725dea537bcee4bc38b5bb6d6` (v0.5.3).
- **Command used:**
```bash
# Generate .nam models
python3 tests/fixtures/generate_a2_fixtures.py
# Render FiLM-Full with C++ v0.5.3 (C++ a2_fast.cpp rejects FiLM → falls back to generic)
render tests/fixtures/models/wavenet_a2_film_full.nam tests/fixtures/stress_signal.wav film_full.wav
render tests/fixtures/models/wavenet_a2_film_lite.nam tests/fixtures/stress_signal.wav film_lite.wav
# Convert to golden
wav_to_golden --input film_full.wav --reference tests/fixtures/stress_signal.wav --output tests/fixtures/golden_wavenet_a2_film_full.bin
wav_to_golden --input film_lite.wav --reference tests/fixtures/stress_signal.wav --output tests/fixtures/golden_wavenet_a2_film_lite.bin
```
### `keras_unsupported.json` — Unsupported Legacy Keras/H5 Format Mock
- **Nature:** Synthetic/Mock JSON representation of a legacy Keras-format model.
- **Provenance:** Replaces the original third-party model `tw40_blues_deluxe_deerinkstudios.json` (Fender Blues Deluxe capture by Deer Ink Studios) which was distributed under the **Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)** license.
- **Legal Risk Mitigation:** To avoid licensing conflicts with the main Apache-2.0 codebase (specifically regarding the Non-Commercial restriction, which prevents packaging or commercial use of the repository), the 160 KB real weights have been deleted. The mock file preserves only the key-value dictionary structure (`"in_shape"`, `"layers"`) to test that the model loader/dispatcher gracefully rejects the legacy format (by returning an error due to the missing `"architecture"` field) without hosting any copyrighted or restricted weights.
### `wavenet_a2_film_chaos_stress.nam` — **Numerical Stress Fixture (Pre-Fix Snapshot)**
- **Nature:** Synthetic — preserved snapshot of the **original** `wavenet_a2_film_lite.nam` from commit `b96e4c7d` (2026-06-21, blob `e107e48f`), before `generate_a2_fixtures.py` was updated to fix FiLM scale bias (commit `3faa9345`) and FiLM slot activation keys (commits `445b5cb1`, `ca8c22ee`). Preserved as `wavenet_a2_film_chaos_stress.nam` in commit `743710da`.
- **Architecture:** WaveNet A2 (CH=3, bottleneck=3, condition_size=1, 23 layers), FiLM post-modulation on all 4 slots: `conv`, `input_mixin`, `activation`, `layer1x1`. Head_scale=0.02.
- **Why "chaos stress":** The pre-fix generator used FiLM scale biases that were pure noise (NOT `1.0 + noise`), producing more chaotic output than post-fix models. This makes it ideal for detecting numerical regressions in FiLM routing, reordering, or dimension dispatch.
- **Golden Fixture:** `golden_wavenet_a2_film_chaos_stress.bin` — v1 only (48 kHz), `v2_scope=none`. C++ `a2_fast.cpp` rejects FiLM unconditionally; falls back to Eigen-based generic WaveNet.
- **Tests:** `test_golden_vectors_wavenet_a2_film_chaos_stress` (golden_vectors.rs), `live_cross_validation_wavenet_a2_film_chaos_stress` (cpp_parity.rs), f64 oracle family "A2FiLMChaos" (reference_oracle_f64.rs).
- **SNR/ESR:** Calibrated thresholds: SNR≥120 dB, ESR<1.0e-12 (validation.rs).
### `wavenet_a2_film_input_mixin_pre.nam` — **Bug C1 Regression Fixture (Isolated FiLM Slot)**
- **Nature:** Synthetic — isolated FiLM regression fixture generated by `tests/fixtures/generate_a2_fixtures.py` (lines 509–523), introduced in commit `3d921af4`. Seed 145 (42+3+100).
- **Architecture:** WaveNet A2 (CH=3, bottleneck=3, condition_size=1, 23 layers), **single FiLM slot active**: `input_mixin_pre_film` (slot 2) only.
- **Why isolated:** Bug C1 discovered that `input_mixin_pre_film` requires special channel dimensions: `FiLM(cond_size→cond_size)` instead of `FiLM(cond_size→ch)`. The per-slot generator injects `film_ch=1` for slots 2 and 7. This fixture validates the fix in isolation.
- **Golden Fixture:** `golden_wavenet_a2_film_input_mixin_pre.bin` — v1 only, `v2_scope=none`.
- **Tests:** `test_golden_vectors_wavenet_a2_film_input_mixin_pre` (golden_vectors.rs), `live_cross_validation_wavenet_a2_film_input_mixin_pre` (cpp_parity.rs), f64 oracle anchor `wavenet_a2_film_input_mixin_pre_256_f64.bin`.
- **SNR/ESR:** Calibrated thresholds: SNR≥120 dB, ESR<1.0e-11 (validation.rs).
### `wavenet_condition_lstm.nam` — **WaveNet + LSTM Condition DSP (C++ Golden Blocked)**
- **Nature:** Synthetic — hybrid fixture generated by `tests/fixtures/generate_a2_fixtures.py` (lines 526–648), introduced in commit `030f1cb6`.
- **Architecture:** Outer WaveNet (2 arrays, CH=3→2, K=3) with embedded LSTM `condition_dsp` sub-model (1 layer, hidden_size=3, input_size=1). 217 total weights (147 WaveNet + 70 LSTM).
- **C++ golden status:** **BLOCKED.** The NeuralAmpModelerCore C++ render tool has a known limitation where the LSTM `condition_dsp` sub-model mismatches input channels (`input_size=1` vs `hidden_size=3`). The registry entry in `src/testing/catalog.rs::GOLDEN_GEN_CATALOG` has `skip_reason` and the golden binary cannot be generated.
- **Golden Fixture:** `golden_wavenet_condition_lstm.bin` — **not generated** (skip_reason). v1 golden test skipped gracefully when the golden file is absent.
- **Tests:** `test_wavenet_condition_lstm_loads_and_runs` (smoke test), `test_policy_reject_condition_lstm` (SKIP when golden absent), `live_cross_validation_wavenet_condition_lstm` (cpp_parity.rs).
- **SNR/ESR:** Calibrated thresholds: SNR≥70 dB, ESR<1.0e-8 (validation.rs — conservative floor).
### `a2_example.nam` — **SlimmableContainer A2 Example**
- **Nature:** Synthetic — SlimmableContainer bundling A2 submodels (CH=3→6), generated by `tests/fixtures/generate_a2_fixtures.py`.
- **Golden Fixture:** `golden_a2_example.bin` (v1) and `golden_a2_example_v2_48000.bin` (v2@48k, `v2_scope=48k_only` in `src/testing/catalog.rs`).
- **Purpose:** Validates SlimmableContainer routing and dispatch for A2 dynamic paths.
- **Tests:** Golden vectors v1, live cross-validation (cpp_parity.rs).
### `convnet_test.nam` — **ConvNet Topology Parity**
- **Nature:** Synthetic — ConvNet (CH=8, 6 blocks), simple weights defined for testing.
- **Golden Fixture:** `golden_convnet_test.bin` and `golden_convnet_test_v2_48000.bin`.
- **Purpose:** Validates ConvNet architecture parity with C++ reference. v2_scope=48k_only.
- **Tests:** Golden vectors v1/v2.
### `lstm_dyn_test.nam` — **LSTM Dynamic Path**
- **Nature:** Synthetic — LSTM-Dyn (1 layer, H=7), simple weights defined for testing.
- **Golden Fixture:** `golden_lstm_dyn_test.bin` and `golden_lstm_dyn_test_v2_48000.bin`.
- **Purpose:** Validates LSTM dynamic path with free geometry and variable hidden dimensions.
- **Tests:** Golden vectors v1/v2, live cross-validation (cpp_parity.rs).
### `wavenet_dyn_free.nam` — **WaveNet Free-Shape Dynamic Path**
- **Nature:** Synthetic — WaveNetDyn (CH=7/4, free geometry), simple weights defined for testing.
- **Golden Fixture:** `golden_wavenet_dyn_free.bin` and `golden_wavenet_dyn_free_v2_48000.bin`.
- **Purpose:** Validates WaveNet dynamic path with free-shape geometry dispatch. v2_scope=48k_only.
- **Tests:** Golden vectors v1/v2, live cross-validation (cpp_parity.rs).
## Two Layers of Validation
### Layer 1 — Pre-committed goldens (fast, `cargo test`)
Tests in `tests/models/nam_infer_test.rs` load the `.golden.bin` files and compare against Rust inference. Runs on every `cargo test` without C++.
### Layer 2 — Live cross-validation (slow, `utils/tests-long.sh`)
`#[ignore]` tests in `tests/parity/cpp_parity.rs` compile the `render` tool from NeuralAmpModelerCore on-demand and compare C++ vs Rust live. Detects drift if NAMCore is updated and the pre-committed goldens become stale.
### Layer 0 — The Generation Pipeline Itself (audited, gaps tracked)
Both layers above assume `golden_gen_build.sh` faithfully reproduces every golden a test
needs, on a rarely-executed, mostly-unsupervised run. A dedicated audit of that script
(triggered by the `revisor-auditor` skill, Compliance and Parity Auditor role) found and
tracked several concrete gaps:
- **Catalog coverage** — the non-distributable models (`APP-EVH`, `Boss BD-2`, `MARSHALL J45`)
are silently skipped when the `models-nondist` symlink is absent; coverage is partial in clean-clone environments.
- **`pipefail`/`errexit` interaction** — **resolved** in the current build script via
`|| render_status=$?` capture + `continue` (lines 396–403). Individual model render failures
skip that model and proceed, not abort the whole run.
- **`NeuralAmpModelerPlugin` vendor sync** is handled by `utils/setup-third-party.sh` (alongside Core).
None of these gaps invalidate the *already-committed* golden `.bin` files — they affect only the ability to safely and fully *regenerate* the catalog from scratch in constrained environments.
## Technical Decision: Cross-Reference is NOT Bit-Identical (ADR-002)
> **Decision:** The golden vectors validate *functional* parity (MSE + SNR + PSNR + bits within calibrated thresholds) against NeuralAmpModelerCore C++, **not** *bit-for-bit* parity.
>
> **Consequence:** NAM-rs produces audio perceptually equivalent to C++, but with measurable numerical differences. These differences are inaudible in any 16-bit or higher audio pipeline.
>
> **LSTM divergence:** The LSTM goldens show relatively low SNR
> (1×16 ≈ 19.8 dB, 2×8 ≈ 25.7 dB, official ≈ 29.7 dB) vs WaveNet's ≥ 100 dB (see the corrected
> WaveNet divergence note below — the previous revision of this note said "vs WaveNet 52–68 dB",
> which was stale by the same ~2 orders of magnitude). The hypothesis that
> FastMath Padé [5,4] tanh is the cause was **refuted**: using exact `f32::tanh` (libm) yields
> identical SNR (Δ ≈ 0.0 dB). The actual bottleneck is likely BF16 weight quantization or GEMV
> rounding — **not** the activation approximations. FastMath is adequate for LSTM.
>
> **WaveNet divergence — corrected 2026-07-02, the previous revision of this note was
> stale by ~2 orders of magnitude:** WaveNet's `Standard`-precision `tanh`/`sigmoid`
> approximations are a small, bounded, and *intentional* divergence from C++'s exact math (see
> ADR-001, [architecture.md](architecture.md) §2, and [cpp_parity_map.md](cpp_parity_map.md) §2.5/§5) — they do **not**
> degrade SNR anywhere near what an earlier draft of this note claimed. Current measured SNR
> against the C++ golden is **≥ 100 dB across every WaveNet catalog SKU** (Standard 134.6 dB,
> Feather 133.1 dB, Nano 132.0 dB, Lite 122.3 dB, A1 Standard 123.4 dB — see the Parity Thresholds
> table above, sourced from `tests/common/validation.rs`), not the "~10 dB" figure this note used
> to state. Do not resurrect that figure without a fresh, reproducible measurement.
## EBU Tech 3341 / R 128 Compliance Sequences
Four WAV files generated by `tests/fixtures/generate_ebu_sequences.py` for validating the ITU-R BS.1770-4 integrated loudness (LUFS) implementation in `src/testing/perceptual/lufs.rs`. Used exclusively by `tests/models/ebu_lufs_compliance.rs`.
| File | Signal | Target (LUFS) | Tolerance |
| ------------------------------ | ----------------------------------- |:-------------:|:---------:|
| `ebu_3341_1_sine_m23.wav` | 1 kHz mono sine, 5 s | −23.000 | ± 0.1 LU |
| `ebu_3341_7_sine_m33.wav` | 1 kHz mono sine, 5 s | −33.000 | ± 0.1 LU |
| `ebu_3341_sine_m18.wav` | 1 kHz mono sine, 5 s | −18.000 | ± 0.1 LU |
| `ebu_3341_dyn_alternating.wav` | Alternating −20/−46 dBFS 1 kHz, 5 s | −23.272 | ± 0.2 LU |
All files: 48 kHz, mono, IEEE float32 WAV. Generated deterministically — amplitudes are binary-searched to produce the exact K-weighted integrated LUFS targets. The dynamic alternating signal exercises the 2-pass BS.1770-4 gate (loud→quiet section). These match EBU Tech 3341 signals 1 and 7 and add two supplemental levels.
### Regenerate EBU Tech 3341 / R 128 Compliance Sequences
```bash
python3 tests/fixtures/generate_ebu_sequences.py
```
## MR-STFT Golden (`mrstft_golden.bin`)
Validates the Multi-Resolution STFT loss computation in `src/testing/perceptual/mod.rs` (`compute_mr_stft`). Generated by `tests/fixtures/scripts/gen_mrstft_golden.py` using a deterministic NumPy reference.
### Binary format
```text
[f64 MR-STFT loss] (8 bytes, little-endian)
[u32 num_samples] (4 bytes)
[f32 × N ref] (N × 4 bytes)
[f32 × N test] (N × 4 bytes)
```
- **Seed:** 42 (NumPy `RandomState`); **N** = 4800 samples.
- **Windows:** [256, 1024, 4096]; **Hop:** window/4; **Weights:** [0.1, 0.3, 0.5].
- **Floor:** per-frame relative −80 dB below spectral peak (fallback `eps_abs = 1e-8`).
- **Test consumer:** `tests/parity/parity_primitives.rs` (L1+L2 loss, single tolerance gate).
### Regenerate MR-STFT Golden
```bash
python3 tests/fixtures/scripts/gen_mrstft_golden.py
```
Prerequisites: `numpy`.
## Spectral Fidelity Baseline (`spectral_fidelity_baseline.json`)
Machine-readable baseline for `tests/models/spectral_fidelity.rs`. Stores per-model, per-metric spectral fidelity thresholds (multi-resolution STFT analysis).
- **Format:** JSON array of objects, one per catalog model.
- **Generation:** `cargo test --test spectral_fidelity generate_spectral_fidelity_baseline -- --ignored --nocapture`
- **Consumer:** `tests/models/spectral_fidelity.rs` — loads the committed baseline and asserts every current inference run stays within tolerance. Regenerate and re-commit whenever model weights, the DSP engine, or the MR-STFT implementation change.
- **Tracked by:** `golden_gen_build.sh` freshness manifest (SHA-256 gate).
## Cabsim Golden Fixtures (Synthetic IRs)
The IR Cabsim convolution engine (`src/dsp/cabsim/conv.rs`) is validated against **direct convolution** (naive O(N²) reference), which serves as the mathematically-rigorous golden baseline. No C++ golden files are required — the reference is computed inline from the same synthetic IR and input signal processed by the UPOLS engine.
### Test Scenarios
| Test | Seed | IR len | Signal len | Block | ESR threshold | Status |
| --------------------------------- | ------ | ------ | ---------- | ----- |:-------------:| ----------- |
| `test_cabsim_golden_short` | 42 | 64 | 256 | 64 | < 1e-5 | Active |
| `test_cabsim_golden_medium` | 137 | 512 | 1024 | 64 | < 1e-5 | Active |
| `test_cabsim_golden_long` | 31337 | 8192 | 16384 | 64 | < 1e-5 | `#[ignore]` |
| `test_cabsim_golden_stress` | 999983 | 32768 | 65536 | 256 | < 1e-5 | `#[ignore]` |
| `test_cabsim_bitwise_determinism` | 777 | 128 | 512 | 64 | < 1e-10 | Active |
| `test_cabsim_passthrough_golden` | 42 | empty | 256 | 64 | < 1e-10 | Active |
### IR Generation
All IRs are synthesized deterministically using a **PCG PRNG** (`SimplePcg`) with fixed seeds. The generation formula is:
```text
IR[n] = sin(2π · freq · t) · exp(−decay · t) + 0.02 · rng.next_f32_signed()
```
| Scenario | Seed | freq (Hz) | decay | IR length |
| ----------- | ------ | --------- | ----- | ------------ |
| Short | 42 | 600 | 12.0 | 64 |
| Medium | 137 | 350 | 6.0 | 512 |
| Long | 31337 | 200 | 2.0 | 8192 |
| Stress | 999983 | 150 | 1.5 | 32768 |
| Bitwise | 777 | 440 | 8.0 | 128 |
| Passthrough | 42 | — | — | 0 (empty IR) |
### Input Signal
The test signal is a deterministic mixed-sine + noise signal at 48 kHz:
```text
s[n] = 0.7·sin(2π·220·t) + 0.35·sin(2π·554.37·t) + 0.18·sin(2π·880·t) + 0.05·rng.next_f32_signed()
```
### Validation Approach
Each test:
1. Creates a `ConvEngine` with the synthetic IR and configured block size.
2. Processes the input signal through the UPOLS (Uniform Partitioned Overlap-Save) engine.
3. Computes the direct convolution reference (O(N²)) from the same IR and input.
4. Compares UPOLS output against the reference using **ESR < 1e-5** (Error-to-Signal Ratio) and a per-scenario **max sample diff** threshold.
The direct convolution reference eliminates the need for external golden vectors — it is a mathematically-rigorous, self-contained oracle.
### Test File
Source: `tests/models/cabsim_golden.rs` — 6 golden parity tests covering short, medium, long, stress, bitwise determinism, and empty-IR passthrough scenarios.
> **C++ Cross-Validation:** C++ cross-validation golden files (`golden_cabsim_cpp_*.bin`) are generated from `AudioDSPTools/dsp/ImpulseResponse.h` (NeuralAmpModelerPlugin submodule) using a dedicated render tool (`tests/fixtures/render_ir.cpp`), enabling external cross-reference verification against the C++ `dsp::ImpulseResponse` implementation. Parity is validated in `tests/parity/cabsim_cpp_parity.rs` (ESR < 1e-13).
## `.temp_live/` (Auto-Cleaned Per Run)
The `tests/fixtures/.temp_live/` directory holds live-generated WAV artifacts from `tests/parity/cpp_parity.rs` during the long-duration audit suite. It is **automatically cleaned** by `utils/tests-long.sh` before each run and is **never committed** (listed in `.gitignore`). If you run `cargo test --ignored --test parity` outside the script, stale WAVs may accumulate — delete them with `rm -rf tests/fixtures/.temp_live/`.