From cb28e4821a6aff20124c514f06ea7f6bf7bb6e3c Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 16:06:26 -0800 Subject: [PATCH 1/6] Update model file version for new models to 0.6.0. --- NAM/get_dsp.cpp | 2 +- example_models/wavenet_a2_max.nam | 2 +- example_models/wavenet_condition_dsp.nam | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NAM/get_dsp.cpp b/NAM/get_dsp.cpp index 8163f3a..a724d7d 100644 --- a/NAM/get_dsp.cpp +++ b/NAM/get_dsp.cpp @@ -59,7 +59,7 @@ Version ParseVersion(const std::string& versionStr) void verify_config_version(const std::string versionStr) { Version version = ParseVersion(versionStr); - if (version.major != 0 || version.minor != 5) + if (version.major > 0 || version.minor > 6) { std::stringstream ss; ss << "Model config is an unsupported version " << versionStr diff --git a/example_models/wavenet_a2_max.nam b/example_models/wavenet_a2_max.nam index cea7420..1a48c95 100644 --- a/example_models/wavenet_a2_max.nam +++ b/example_models/wavenet_a2_max.nam @@ -3,7 +3,7 @@ "This model is meant as a 'test case' to contain all of the new features that are being considered for A2.", "It doesn't have slimmability." ], - "version": "0.5.4", + "version": "0.6.0", "metadata": { "date": { "year": 2026, diff --git a/example_models/wavenet_condition_dsp.nam b/example_models/wavenet_condition_dsp.nam index 81487f0..b9e3a9a 100644 --- a/example_models/wavenet_condition_dsp.nam +++ b/example_models/wavenet_condition_dsp.nam @@ -1,5 +1,5 @@ { - "version": "0.5.4", + "version": "0.6.0", "metadata": { "date": { "year": 2026, @@ -17,7 +17,7 @@ "architecture": "WaveNet", "config": { "condition_dsp": { - "version": "0.5.4", + "version": "0.6.0", "metadata": { "date": { "year": 2026, From 430de754e0a318a8dde4f0bdd8a2756a69f5551d Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 16:33:03 -0800 Subject: [PATCH 2/6] Add documentation on version support --- .gitignore | 3 +++ docs/index.rst | 1 + docs/nam_file_version.rst | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 docs/nam_file_version.rst diff --git a/.gitignore b/.gitignore index 8604b38..b7ee58e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ *.app .vscode/ + +docs/_build/ +*.DS_Store diff --git a/docs/index.rst b/docs/index.rst index 7f9bdda..68c7795 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,6 +8,7 @@ Welcome to the NeuralAmpModelerCore documentation. This library provides a core :caption: Contents: wavenet_walkthrough + nam_file_version api/index Overview diff --git a/docs/nam_file_version.rst b/docs/nam_file_version.rst new file mode 100644 index 0000000..c102d08 --- /dev/null +++ b/docs/nam_file_version.rst @@ -0,0 +1,33 @@ +``.nam`` file versions +====================== + +As more features are added to NAM, the version of the ``.nam`` file format will be incremented. +The general rules try to follow semantic versioning. +That means that any changes to file contents where an older version of +NeuralAmpModelerCore is either not able to understand the contents or might +misunderstand them (e.g. new fields that old code is not looking for) will trigger a +version bump communicating a breaking change (e.g. minor version while pre-v1.0.0; +later, a major version bump). +Improvements where the model will be loaded correctly, but possibly with some incomplete +functionality will trigger a version bump communicating a non-breaking change +(e.g. minor version or patch pre-v1.0.0). + +Version history +--------------- + +The following table shows which versions of NeuralAmpModelerCore support which model file versions: + +.. list-table:: Core Version Support Matrix + :header-rows: 1 + :widths: 30 70 + + * - Core Version + - Latest fully-supported ``.nam`` file version + * - 0.0.0 + - 0.5.1 + * - 0.2.0 + - 0.5.2 + * - 0.3.0 + - 0.5.3 + * - 0.4.0 + - 0.6.0 From b0a8a77850de6a69e74b0a5464bfd3b9babd89e7 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 16:47:58 -0800 Subject: [PATCH 3/6] Better details on latest supported version, update creator script --- NAM/get_dsp.cpp | 63 ++++++++++++++++++++++++++++++++++------- tools/create_wavenet.py | 2 +- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/NAM/get_dsp.cpp b/NAM/get_dsp.cpp index a724d7d..b40d169 100644 --- a/NAM/get_dsp.cpp +++ b/NAM/get_dsp.cpp @@ -14,8 +14,33 @@ namespace nam { -struct Version +class Version { +public: + Version(int major, int minor, int patch) + : major(major) + , minor(minor) + , patch(patch) + { + } + + std::string toString() const + { + return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); + } + + bool operator>(const Version& other) const + { + return major > other.major + || (major == other.major && (minor > other.minor || (minor == other.minor && patch > other.patch))); + } + + bool operator<(const Version& other) const + { + return major < other.major + || (major == other.major && (minor < other.minor || (minor == other.minor && patch < other.patch))); + } + int major; int minor; int patch; @@ -23,8 +48,6 @@ struct Version Version ParseVersion(const std::string& versionStr) { - Version version; - // Split the version string into major, minor, and patch components std::stringstream ss(versionStr); std::string majorStr, minorStr, patchStr; @@ -33,11 +56,14 @@ Version ParseVersion(const std::string& versionStr) std::getline(ss, patchStr); // Parse the components as integers and assign them to the version struct + int major; + int minor; + int patch; try { - version.major = std::stoi(majorStr); - version.minor = std::stoi(minorStr); - version.patch = std::stoi(patchStr); + major = std::stoi(majorStr); + minor = std::stoi(minorStr); + patch = std::stoi(patchStr); } catch (const std::invalid_argument&) { @@ -49,24 +75,41 @@ Version ParseVersion(const std::string& versionStr) } // Validate the semver components - if (version.major < 0 || version.minor < 0 || version.patch < 0) + if (major < 0 || minor < 0 || patch < 0) { throw std::invalid_argument("Negative version component: " + versionStr); } - return version; + return Version(major, minor, patch); } void verify_config_version(const std::string versionStr) { Version version = ParseVersion(versionStr); - if (version.major > 0 || version.minor > 6) + Version currentVersion = Version(0, 6, 0); + Version earliestSupportedVersion = Version(0, 5, 0); + + if (version < earliestSupportedVersion) { std::stringstream ss; - ss << "Model config is an unsupported version " << versionStr + ss << "Model config is an unsupported version " << versionStr << ". The earliest supported version is " + << earliestSupportedVersion.toString() << ". Try either converting the model to a more recent version, or " "update your version of the NAM plugin."; throw std::runtime_error(ss.str()); } + if (version.major > currentVersion.major || version.minor > currentVersion.minor) + { + std::stringstream ss; + ss << "Model config is an unsupported version " << versionStr << ". The latest fully-supported version is " + << currentVersion.toString(); + throw std::runtime_error(ss.str()); + } + else if (version.major == 0 && version.minor == 6 && version.patch > 0) + { + std::cerr << "Model config is a partially-supported version " << versionStr + << ". The latest fully-supported version is " << currentVersion.toString() + << ". Continuing with partial support." << std::endl; + } } std::vector GetWeights(nlohmann::json const& j) diff --git a/tools/create_wavenet.py b/tools/create_wavenet.py index 76cec7c..aaa3d83 100755 --- a/tools/create_wavenet.py +++ b/tools/create_wavenet.py @@ -154,7 +154,7 @@ def generate_weights(weight_count: int, seed: int = None) -> List[float]: def create_wavenet_nam(config: Dict[str, Any], output_path: Path, seed: int = None, sample_rate: int = 48000, - version: str = "0.5.4") -> None: + version: str = "0.6.0") -> None: """ Create a WaveNet .nam file with the given configuration and random weights. From e075bc40d43e9778cb23a956710a2daddc9db747 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 17:07:38 -0800 Subject: [PATCH 4/6] Add version validation tests for .nam file loading - Introduced helper function to create configuration strings with specific versions. - Added tests to verify behavior when loading .nam files with versions one patch and one minor beyond the latest supported versions. - Implemented a test for versions that are too early, ensuring proper error handling for unsupported configurations. - Updated `run_tests.cpp` to include the new version validation tests. --- tools/run_tests.cpp | 3 ++ tools/test/test_get_dsp.cpp | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/tools/run_tests.cpp b/tools/run_tests.cpp index c86acec..218881e 100644 --- a/tools/run_tests.cpp +++ b/tools/run_tests.cpp @@ -221,6 +221,9 @@ int main() test_get_dsp::test_gets_output_level(); test_get_dsp::test_null_input_level(); test_get_dsp::test_null_output_level(); + test_get_dsp::test_version_patch_one_beyond_supported(); + test_get_dsp::test_version_minor_one_beyond_supported(); + test_get_dsp::test_version_too_early(); // Finally, some end-to-end tests. test_get_dsp::test_load_and_process_nam_files(); diff --git a/tools/test/test_get_dsp.cpp b/tools/test/test_get_dsp.cpp index 7540b17..deca13e 100644 --- a/tools/test/test_get_dsp.cpp +++ b/tools/test/test_get_dsp.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -158,4 +159,68 @@ void test_load_and_process_nam_files() process_buffers(dsp.get(), num_buffers, buffer_size); } } + +// Helper function to create a config string with a specific version +std::string createConfigWithVersion(const std::string& version) +{ + nlohmann::json j = nlohmann::json::parse(basicConfigStr); + j["version"] = version; + return j.dump(); +} + +void test_version_patch_one_beyond_supported() +{ + // Test that a .nam file with version one patch beyond the latest fully supported + // can still be loaded + nam::Version latestVersion = nam::ParseVersion(nam::LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION); + latestVersion.patch++; + const std::string configStr = createConfigWithVersion(latestVersion.toString()); + nam::dspData config = _GetConfig(configStr); + + // Should succeed (with a warning, but we can't easily test for that) + std::unique_ptr dsp = get_dsp(config); + assert(dsp != nullptr); +} + +void test_version_minor_one_beyond_supported() +{ + // Test that a .nam file with version one minor beyond the latest fully supported + // cannot be loaded + nam::Version latestVersion = nam::ParseVersion(nam::LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION); + latestVersion.minor++; + latestVersion.patch = 0; // Reset patch when incrementing minor + const std::string configStr = createConfigWithVersion(latestVersion.toString()); + nam::dspData config = _GetConfig(configStr); + + bool threw = false; + try + { + std::unique_ptr dsp = get_dsp(config); + } + catch (const std::runtime_error&) + { + threw = true; + } + assert(threw); +} + +void test_version_too_early() +{ + // Test that a .nam file with version too early (before earliest supported) cannot be loaded + nam::Version earliestVersion = nam::ParseVersion(nam::EARLIEST_SUPPORTED_NAM_FILE_VERSION); + earliestVersion.minor--; // Decrement minor to get a version before earliest supported + const std::string configStr = createConfigWithVersion(earliestVersion.toString()); + nam::dspData config = _GetConfig(configStr); + + bool threw = false; + try + { + std::unique_ptr dsp = get_dsp(config); + } + catch (const std::runtime_error&) + { + threw = true; + } + assert(threw); +} }; // namespace test_get_dsp \ No newline at end of file From 8607881a41aee66b679b9ea39745520678402fee Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 17:08:12 -0800 Subject: [PATCH 5/6] Move version code out to headers --- NAM/get_dsp.cpp | 36 ++---------------------------------- NAM/get_dsp.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/NAM/get_dsp.cpp b/NAM/get_dsp.cpp index b40d169..5505cdc 100644 --- a/NAM/get_dsp.cpp +++ b/NAM/get_dsp.cpp @@ -14,38 +14,6 @@ namespace nam { -class Version -{ -public: - Version(int major, int minor, int patch) - : major(major) - , minor(minor) - , patch(patch) - { - } - - std::string toString() const - { - return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); - } - - bool operator>(const Version& other) const - { - return major > other.major - || (major == other.major && (minor > other.minor || (minor == other.minor && patch > other.patch))); - } - - bool operator<(const Version& other) const - { - return major < other.major - || (major == other.major && (minor < other.minor || (minor == other.minor && patch < other.patch))); - } - - int major; - int minor; - int patch; -}; - Version ParseVersion(const std::string& versionStr) { // Split the version string into major, minor, and patch components @@ -85,8 +53,8 @@ Version ParseVersion(const std::string& versionStr) void verify_config_version(const std::string versionStr) { Version version = ParseVersion(versionStr); - Version currentVersion = Version(0, 6, 0); - Version earliestSupportedVersion = Version(0, 5, 0); + Version currentVersion = ParseVersion(LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION); + Version earliestSupportedVersion = ParseVersion(EARLIEST_SUPPORTED_NAM_FILE_VERSION); if (version < earliestSupportedVersion) { diff --git a/NAM/get_dsp.h b/NAM/get_dsp.h index 6353053..e629b30 100644 --- a/NAM/get_dsp.h +++ b/NAM/get_dsp.h @@ -6,6 +6,45 @@ namespace nam { +class Version +{ +public: + Version(int major, int minor, int patch) + : major(major) + , minor(minor) + , patch(patch) + { + } + + std::string toString() const + { + return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); + } + + bool operator>(const Version& other) const + { + return major > other.major + || (major == other.major && (minor > other.minor || (minor == other.minor && patch > other.patch))); + } + + bool operator<(const Version& other) const + { + return major < other.major + || (major == other.major && (minor < other.minor || (minor == other.minor && patch < other.patch))); + } + + int major; + int minor; + int patch; +}; + +Version ParseVersion(const std::string& versionStr); + +void verify_config_version(const std::string versionStr); + +const std::string LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION = "0.6.0"; +const std::string EARLIEST_SUPPORTED_NAM_FILE_VERSION = "0.5.0"; + /// \brief Get NAM from a .nam file at the provided location /// \param config_filename Path to the .nam model file /// \return Unique pointer to a DSP object From d57d76bbc284542d6a7bb3b97636fe597a7618a4 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 26 Jan 2026 17:11:20 -0800 Subject: [PATCH 6/6] Suppress std::cerr during test --- tools/test/test_get_dsp.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/test/test_get_dsp.cpp b/tools/test/test_get_dsp.cpp index deca13e..7d3be4a 100644 --- a/tools/test/test_get_dsp.cpp +++ b/tools/test/test_get_dsp.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -177,9 +178,17 @@ void test_version_patch_one_beyond_supported() const std::string configStr = createConfigWithVersion(latestVersion.toString()); nam::dspData config = _GetConfig(configStr); - // Should succeed (with a warning, but we can't easily test for that) + // Suppress the warning message that gets printed to std::cerr + std::streambuf* originalCerr = std::cerr.rdbuf(); + std::ostringstream nullStream; + std::cerr.rdbuf(nullStream.rdbuf()); + + // Should succeed (with a warning, but we suppress it) std::unique_ptr dsp = get_dsp(config); assert(dsp != nullptr); + + // Restore original cerr + std::cerr.rdbuf(originalCerr); } void test_version_minor_one_beyond_supported()