Skip to content

[FEATURE] Instantiate (linear) models from .wav files - #337

Merged
sdatkinson merged 4 commits into
mainfrom
157-nam-from-wav
Sep 18, 2026
Merged

sdatkinson merged 4 commits into
mainfrom
157-nam-from-wav

Conversation

@sdatkinson

Copy link
Copy Markdown
Owner

A .wav file is often used to hold the impulse response of a system. This can be used to define a linear model (as is super-common in digital audio).

This implements a loading routine that does a (lossless) conversion from a .wav IR to a linear model.

Resolves #157

@sdatkinson
sdatkinson merged commit 0b3d3c9 into main Sep 18, 2026
4 checks passed
@sdatkinson
sdatkinson deleted the 157-nam-from-wav branch September 18, 2026 23:59
dfernandes83 added a commit to dfernandes83/NeuralAmpModelerCore that referenced this pull request Sep 21, 2026
Brings in NAM Core 0.6.0 from sdatkinson/NeuralAmpModelerCore:
- models with variable sample rate (sdatkinson#333): Linear reports
  SupportsArbitrarySampleRate() and resamples in Reset()
- 1-to-n and n-to-1 channel configurations in Linear models (sdatkinson#335, breaking:
  a Linear model now needs one kernel per output/input path)
- Linear models instantiated from .wav files through get_dsp() (sdatkinson#337)
- version bump to 0.6.0 (sdatkinson#336)

Conflicts:
- NAM/get_dsp.cpp: kept all three includes (<algorithm> and <cctype> from
  upstream, <cmath> from our validation code).
- NAM/linear.cpp: took upstream's rewritten direct-convolution loop. Our side
  only had a static_cast to silence an MSVC conversion warning in the code
  that upstream replaced.

Consumers now have to compile NAM/wav.cpp too: get_dsp.cpp calls
nam::detail::load_wav_ir().
squatchlr added a commit to Squatch-Stack/NeuralAmpModelerCore that referenced this pull request Oct 15, 2026
Closes sdatkinson#327

## Summary

Each architecture registered itself from a static object in its own
translation unit (`static ConfigParserHelper _register_X(...)`). When
Core is
linked from a static archive, nothing references those units. The linker
drops
them, their initializers never run, and every load fails with
`No config parser registered for architecture: …`. Even in a normal
link,
the registry was empty until those units had been initialized.

`ConfigParserRegistry::instance()` now registers the six built-ins
itself, from
one list in `get_dsp.cpp`, inside its function-local static. Naming each
`create_config` there means any program that uses the registry links
every
built-in, however Core is linked. The initialization is thread-safe, so
concurrent first calls register once. The per-file registrars are
removed, since
they would now register duplicates.

This is option 2 from the issue. Calls to `registerParser` are real
references,
so no `volatile` array is needed, and callers don't have to call an init
function. `get_dsp.cpp` was chosen over a new file so that projects
listing
Core's sources by hand need no change. External registration
(`factory::Helper`, `ConfigParserHelper`) works as before, and
`test_extensible` still passes. sdatkinson#228 takes the same approach as part of
a
larger library PR. If `get_dsp` moves into its own library (sdatkinson#294), the
list
moves with it.

## Tests

`tools/test/test_builtin_registration.cpp`:

- all six built-ins are registered;
- `WaveNet` is already registered when the registry is first read during
static
initialization. That fails on `main`, because `run_tests.cpp` is
initialized
  before the architecture files:
  `Assertion failed: (kWaveNetRegisteredDuringStaticInit)`;
- registering a built-in name again still throws.

The issue's own reproduction links the same objects directly, then
through `ar`.
We extended it to more model types:

| AppleClang 21 | `main`, via `ar` | this PR, via `ar` |
|---|---|---|
| `A2.nam`, `slimmable_container.nam` | SlimmableContainer not
registered | loads |
| `wavenet.nam`, `wavenet_condition_dsp.nam` | WaveNet not registered |
loads |
| `lstm.nam` | LSTM not registered | loads |
| a 64-tap `.wav` IR (sdatkinson#337) | Linear not registered | loads |

The PR also loads with `-O3 -flto -Wl,-dead_strip`. On GCC 14.2 (Pi 5),
`main`
fails and the PR loads the same six cases, with
`-ffunction-sections -fdata-sections -Wl,--gc-sections` and with
`-flto -Wl,--gc-sections`.

## Verification

- **AppleClang 21 and Linux clang 18.1.3 (libstdc++ and libc++):** CI's
steps
  pass, in Debug and Debug with `NAM_USE_INLINE_GEMM`.
- **GCC 14.2 (Raspberry Pi 5):** `run_tests` passes, and the new test
fails
  first there too.
- **Concurrency:** ThreadSanitizer, with 8 threads making their first
  `get_dsp` call through a static archive at once: clean in 5 of 5 runs,
  and all 8 threads load every time.
- **Output:** 36 renders are byte-identical to `main`. Nothing on the
audio path
  changes.

## Not covered

MSVC and Windows. Programs that never touch the registry are unaffected
either
way.

Co-authored-by: Squatch Stack <321440025+squatch-stack@users.noreply.github.com>
silvercobraa added a commit to Insapiable-Inc/NeuralAmpModelerCore that referenced this pull request Oct 18, 2026
Brings the FiLM fix for non-blended gating (sdatkinson#321, sdatkinson#322), .nam file validation (sdatkinson#315),
the hot-path allocation removal (sdatkinson#312), cached A2 prewarm (sdatkinson#319), faster long linear
convolutions (sdatkinson#324), variable-rate and 1-to-n / n-to-1 Linear models (sdatkinson#333, sdatkinson#335),
Sequential models (sdatkinson#323) and Linear models from .wav files (sdatkinson#337).

Conflict: test_wavenet_configurable_gating — upstream fixed the same short weight
list (sdatkinson#316); their fix kept. Upstream's new tests are moved to the checked
weights_iterator (assert(it.at_end()) instead of comparing with weights.end()).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Instantiate linear NAMs from impulse response .wav files

1 participant