Skip to content

Commit 4dfc844

Browse files
authored
Bump .nam file supported to 0.6.0 (#203)
* Update model file version for new models to 0.6.0. * Add documentation on version support * Better details on latest supported version, update creator script * 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. * Move version code out to headers * Suppress std::cerr during test
1 parent 925da40 commit 4dfc844

File tree

10 files changed

+184
-20
lines changed

10 files changed

+184
-20
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@
3232
*.app
3333

3434
.vscode/
35+
36+
docs/_build/
37+
*.DS_Store

NAM/get_dsp.cpp

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,8 @@
1414

1515
namespace nam
1616
{
17-
struct Version
18-
{
19-
int major;
20-
int minor;
21-
int patch;
22-
};
23-
2417
Version ParseVersion(const std::string& versionStr)
2518
{
26-
Version version;
27-
2819
// Split the version string into major, minor, and patch components
2920
std::stringstream ss(versionStr);
3021
std::string majorStr, minorStr, patchStr;
@@ -33,11 +24,14 @@ Version ParseVersion(const std::string& versionStr)
3324
std::getline(ss, patchStr);
3425

3526
// Parse the components as integers and assign them to the version struct
27+
int major;
28+
int minor;
29+
int patch;
3630
try
3731
{
38-
version.major = std::stoi(majorStr);
39-
version.minor = std::stoi(minorStr);
40-
version.patch = std::stoi(patchStr);
32+
major = std::stoi(majorStr);
33+
minor = std::stoi(minorStr);
34+
patch = std::stoi(patchStr);
4135
}
4236
catch (const std::invalid_argument&)
4337
{
@@ -49,24 +43,41 @@ Version ParseVersion(const std::string& versionStr)
4943
}
5044

5145
// Validate the semver components
52-
if (version.major < 0 || version.minor < 0 || version.patch < 0)
46+
if (major < 0 || minor < 0 || patch < 0)
5347
{
5448
throw std::invalid_argument("Negative version component: " + versionStr);
5549
}
56-
return version;
50+
return Version(major, minor, patch);
5751
}
5852

5953
void verify_config_version(const std::string versionStr)
6054
{
6155
Version version = ParseVersion(versionStr);
62-
if (version.major != 0 || version.minor != 5)
56+
Version currentVersion = ParseVersion(LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION);
57+
Version earliestSupportedVersion = ParseVersion(EARLIEST_SUPPORTED_NAM_FILE_VERSION);
58+
59+
if (version < earliestSupportedVersion)
6360
{
6461
std::stringstream ss;
65-
ss << "Model config is an unsupported version " << versionStr
62+
ss << "Model config is an unsupported version " << versionStr << ". The earliest supported version is "
63+
<< earliestSupportedVersion.toString()
6664
<< ". Try either converting the model to a more recent version, or "
6765
"update your version of the NAM plugin.";
6866
throw std::runtime_error(ss.str());
6967
}
68+
if (version.major > currentVersion.major || version.minor > currentVersion.minor)
69+
{
70+
std::stringstream ss;
71+
ss << "Model config is an unsupported version " << versionStr << ". The latest fully-supported version is "
72+
<< currentVersion.toString();
73+
throw std::runtime_error(ss.str());
74+
}
75+
else if (version.major == 0 && version.minor == 6 && version.patch > 0)
76+
{
77+
std::cerr << "Model config is a partially-supported version " << versionStr
78+
<< ". The latest fully-supported version is " << currentVersion.toString()
79+
<< ". Continuing with partial support." << std::endl;
80+
}
7081
}
7182

7283
std::vector<float> GetWeights(nlohmann::json const& j)

NAM/get_dsp.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,45 @@
66

77
namespace nam
88
{
9+
class Version
10+
{
11+
public:
12+
Version(int major, int minor, int patch)
13+
: major(major)
14+
, minor(minor)
15+
, patch(patch)
16+
{
17+
}
18+
19+
std::string toString() const
20+
{
21+
return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
22+
}
23+
24+
bool operator>(const Version& other) const
25+
{
26+
return major > other.major
27+
|| (major == other.major && (minor > other.minor || (minor == other.minor && patch > other.patch)));
28+
}
29+
30+
bool operator<(const Version& other) const
31+
{
32+
return major < other.major
33+
|| (major == other.major && (minor < other.minor || (minor == other.minor && patch < other.patch)));
34+
}
35+
36+
int major;
37+
int minor;
38+
int patch;
39+
};
40+
41+
Version ParseVersion(const std::string& versionStr);
42+
43+
void verify_config_version(const std::string versionStr);
44+
45+
const std::string LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION = "0.6.0";
46+
const std::string EARLIEST_SUPPORTED_NAM_FILE_VERSION = "0.5.0";
47+
948
/// \brief Get NAM from a .nam file at the provided location
1049
/// \param config_filename Path to the .nam model file
1150
/// \return Unique pointer to a DSP object

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Welcome to the NeuralAmpModelerCore documentation. This library provides a core
88
:caption: Contents:
99

1010
wavenet_walkthrough
11+
nam_file_version
1112
api/index
1213

1314
Overview

docs/nam_file_version.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
``.nam`` file versions
2+
======================
3+
4+
As more features are added to NAM, the version of the ``.nam`` file format will be incremented.
5+
The general rules try to follow semantic versioning.
6+
That means that any changes to file contents where an older version of
7+
NeuralAmpModelerCore is either not able to understand the contents or might
8+
misunderstand them (e.g. new fields that old code is not looking for) will trigger a
9+
version bump communicating a breaking change (e.g. minor version while pre-v1.0.0;
10+
later, a major version bump).
11+
Improvements where the model will be loaded correctly, but possibly with some incomplete
12+
functionality will trigger a version bump communicating a non-breaking change
13+
(e.g. minor version or patch pre-v1.0.0).
14+
15+
Version history
16+
---------------
17+
18+
The following table shows which versions of NeuralAmpModelerCore support which model file versions:
19+
20+
.. list-table:: Core Version Support Matrix
21+
:header-rows: 1
22+
:widths: 30 70
23+
24+
* - Core Version
25+
- Latest fully-supported ``.nam`` file version
26+
* - 0.0.0
27+
- 0.5.1
28+
* - 0.2.0
29+
- 0.5.2
30+
* - 0.3.0
31+
- 0.5.3
32+
* - 0.4.0
33+
- 0.6.0

example_models/wavenet_a2_max.nam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"This model is meant as a 'test case' to contain all of the new features that are being considered for A2.",
44
"It doesn't have slimmability."
55
],
6-
"version": "0.5.4",
6+
"version": "0.6.0",
77
"metadata": {
88
"date": {
99
"year": 2026,

example_models/wavenet_condition_dsp.nam

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.5.4",
2+
"version": "0.6.0",
33
"metadata": {
44
"date": {
55
"year": 2026,
@@ -17,7 +17,7 @@
1717
"architecture": "WaveNet",
1818
"config": {
1919
"condition_dsp": {
20-
"version": "0.5.4",
20+
"version": "0.6.0",
2121
"metadata": {
2222
"date": {
2323
"year": 2026,

tools/create_wavenet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def generate_weights(weight_count: int, seed: int = None) -> List[float]:
154154

155155
def create_wavenet_nam(config: Dict[str, Any], output_path: Path,
156156
seed: int = None, sample_rate: int = 48000,
157-
version: str = "0.5.4") -> None:
157+
version: str = "0.6.0") -> None:
158158
"""
159159
Create a WaveNet .nam file with the given configuration and random weights.
160160

tools/run_tests.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ int main()
221221
test_get_dsp::test_gets_output_level();
222222
test_get_dsp::test_null_input_level();
223223
test_get_dsp::test_null_output_level();
224+
test_get_dsp::test_version_patch_one_beyond_supported();
225+
test_get_dsp::test_version_minor_one_beyond_supported();
226+
test_get_dsp::test_version_too_early();
224227

225228
// Finally, some end-to-end tests.
226229
test_get_dsp::test_load_and_process_nam_files();

tools/test/test_get_dsp.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include <cassert>
22
#include <cmath>
33
#include <filesystem>
4+
#include <iostream>
45
#include <memory>
6+
#include <sstream>
57
#include <string>
68
#include <vector>
79

@@ -158,4 +160,76 @@ void test_load_and_process_nam_files()
158160
process_buffers(dsp.get(), num_buffers, buffer_size);
159161
}
160162
}
163+
164+
// Helper function to create a config string with a specific version
165+
std::string createConfigWithVersion(const std::string& version)
166+
{
167+
nlohmann::json j = nlohmann::json::parse(basicConfigStr);
168+
j["version"] = version;
169+
return j.dump();
170+
}
171+
172+
void test_version_patch_one_beyond_supported()
173+
{
174+
// Test that a .nam file with version one patch beyond the latest fully supported
175+
// can still be loaded
176+
nam::Version latestVersion = nam::ParseVersion(nam::LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION);
177+
latestVersion.patch++;
178+
const std::string configStr = createConfigWithVersion(latestVersion.toString());
179+
nam::dspData config = _GetConfig(configStr);
180+
181+
// Suppress the warning message that gets printed to std::cerr
182+
std::streambuf* originalCerr = std::cerr.rdbuf();
183+
std::ostringstream nullStream;
184+
std::cerr.rdbuf(nullStream.rdbuf());
185+
186+
// Should succeed (with a warning, but we suppress it)
187+
std::unique_ptr<nam::DSP> dsp = get_dsp(config);
188+
assert(dsp != nullptr);
189+
190+
// Restore original cerr
191+
std::cerr.rdbuf(originalCerr);
192+
}
193+
194+
void test_version_minor_one_beyond_supported()
195+
{
196+
// Test that a .nam file with version one minor beyond the latest fully supported
197+
// cannot be loaded
198+
nam::Version latestVersion = nam::ParseVersion(nam::LATEST_FULLY_SUPPORTED_NAM_FILE_VERSION);
199+
latestVersion.minor++;
200+
latestVersion.patch = 0; // Reset patch when incrementing minor
201+
const std::string configStr = createConfigWithVersion(latestVersion.toString());
202+
nam::dspData config = _GetConfig(configStr);
203+
204+
bool threw = false;
205+
try
206+
{
207+
std::unique_ptr<nam::DSP> dsp = get_dsp(config);
208+
}
209+
catch (const std::runtime_error&)
210+
{
211+
threw = true;
212+
}
213+
assert(threw);
214+
}
215+
216+
void test_version_too_early()
217+
{
218+
// Test that a .nam file with version too early (before earliest supported) cannot be loaded
219+
nam::Version earliestVersion = nam::ParseVersion(nam::EARLIEST_SUPPORTED_NAM_FILE_VERSION);
220+
earliestVersion.minor--; // Decrement minor to get a version before earliest supported
221+
const std::string configStr = createConfigWithVersion(earliestVersion.toString());
222+
nam::dspData config = _GetConfig(configStr);
223+
224+
bool threw = false;
225+
try
226+
{
227+
std::unique_ptr<nam::DSP> dsp = get_dsp(config);
228+
}
229+
catch (const std::runtime_error&)
230+
{
231+
threw = true;
232+
}
233+
assert(threw);
234+
}
161235
}; // namespace test_get_dsp

0 commit comments

Comments
 (0)