Skip to content

Commit

Permalink
Backward-compatible unserialization to v0.7.3 (#532)
Browse files Browse the repository at this point in the history
* Start refactor of legacy unserialization.

* Fix bugs

* Fix bugs, v0.7.3 loads correctly.

* Fix more bugs, current version loads correctly.

* Update pull_request_template.md

* Improve documentation and trim out unneeded includes due to hacky include pattern
  • Loading branch information
sdatkinson authored Nov 23, 2024
1 parent e7dd8b5 commit 1960e35
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 103 deletions.
5 changes: 3 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Include [Closing words](https://docs.github.com/en/issues/tracking-your-work-wit
- [ ] Does the VST3 plugin pass all of the unit tests in the [VST3PluginTestHost](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/Plug-in+Test+Host.html)? (Download it as part of the VST3 SDK [here](https://www.steinberg.net/developers/).)
- [ ] Windows
- [ ] macOS
- [ ] Does your PR add, remove, or rename any plugin parameters?
- [ ] If yes, then have you ensured that older versions of the plug-in load correctly? (Usually, this means writing a new legacy unserialization function like [`_UnserializeStateLegacy_0_7_9`](https://github.com/sdatkinson/NeuralAmpModelerPlugin/blob/f755918e3f325f28658700ca954f8a47ec58d021/NeuralAmpModeler/NeuralAmpModeler.cpp#L823).)
- [ ] Does your PR add, remove, or rename any plugin parameters? If yes...
- [ ] Have you ensured that the plug-in unserializes correctly?
- [ ] Have you ensured that _older_ versions of the plug-in load correctly? (See [`Unserialization.cpp`](https://github.com/sdatkinson/NeuralAmpModelerPlugin/blob/main/NeuralAmpModeler/Unserialization.cpp).)

115 changes: 20 additions & 95 deletions NeuralAmpModeler/NeuralAmpModeler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ EMsgBoxResult _ShowMessageBox(iplug::igraphics::IGraphics* pGraphics, const char
#endif
}

const std::string kCalibrateInputParamName = "CalibrateInput";
const bool kDefaultCalibrateInput = false;
const std::string kInputCalibrationLevelParamName = "InputCalibrationLevel";
const double kDefaultInputCalibrationLevel = 12.0;


NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
: Plugin(info, MakeConfig(kNumParams, kNumPresets))
Expand All @@ -85,8 +90,9 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
GetParam(kEQActive)->InitBool("ToneStack", true);
GetParam(kOutputMode)->InitEnum("OutputMode", 1, {"Raw", "Normalized", "Calibrated"}); // TODO DRY w/ control
GetParam(kIRToggle)->InitBool("IRToggle", true);
GetParam(kCalibrateInput)->InitBool("CalibrateInput", false);
GetParam(kInputCalibrationLevel)->InitDouble("InputCalibrationLevel", 12.5, -30.0, 30.0, 0.1, "dBu");
GetParam(kCalibrateInput)->InitBool(kCalibrateInputParamName.c_str(), kDefaultCalibrateInput);
GetParam(kInputCalibrationLevel)
->InitDouble(kInputCalibrationLevelParamName.c_str(), kDefaultInputCalibrationLevel, -60.0, 60.0, 0.1, "dBu");

mNoiseGateTrigger.AddListener(&mNoiseGateGain);

Expand Down Expand Up @@ -413,29 +419,20 @@ bool NeuralAmpModeler::SerializeState(IByteChunk& chunk) const

int NeuralAmpModeler::UnserializeState(const IByteChunk& chunk, int startPos)
{
// Look for the expected header. If it's there, then we'll know what to do.
WDL_String header;
int pos = startPos;
pos = chunk.GetStr(header, pos);
// Unseralization:

const char* kExpectedHeader = "###NeuralAmpModeler###";
if (strcmp(header.Get(), kExpectedHeader) == 0)
{
// Handle legacy plugin serialized states:
// In v0.7.9, this was the NAM filepath. So, if we dont' get the expected header, then we can attempt to unserialize
// as v0.7.9:
const char* kExpectedHeader = "###NeuralAmpModeler###";
if (strcmp(header.Get(), kExpectedHeader) == 0)
{
pos = _UnserializeStateCurrent(chunk, pos);
}
else
{
pos = _UnserializeStateLegacy_0_7_9(chunk, startPos);
}
return _UnserializeStateWithKnownVersion(chunk, pos);
}
else
{
return _UnserializeStateWithUnknownVersion(chunk, startPos);
}
if (mNAMPath.GetLength())
_StageModel(mNAMPath);
if (mIRPath.GetLength())
_StageIR(mIRPath);
return pos;
}

void NeuralAmpModeler::OnUIOpen()
Expand Down Expand Up @@ -857,81 +854,6 @@ void NeuralAmpModeler::_ProcessOutput(iplug::sample** inputs, iplug::sample** ou
#endif
}

int NeuralAmpModeler::_UnserializeStateCurrent(const IByteChunk& chunk, int pos)
{
WDL_String version;
pos = chunk.GetStr(version, pos);
// Post-v0.7.9 legacy loading here once needed:
// ...

// Current version loading:
pos = chunk.GetStr(mNAMPath, pos);
pos = chunk.GetStr(mIRPath, pos);
pos = UnserializeParams(chunk, pos);
return pos;
}

int NeuralAmpModeler::_UnserializeStateLegacy_0_7_9(const IByteChunk& chunk, int startPos)
{
WDL_String dir;
int pos = startPos;
pos = chunk.GetStr(mNAMPath, pos);
pos = chunk.GetStr(mIRPath, pos);
auto unserialize = [&](const IByteChunk& chunk, int startPos) {
// cf IPluginBase::UnserializeParams(const IByteChunk& chunk, int startPos)

// These are the parameter names, in the order that they were serialized in v0.7.9.
std::vector<std::string> oldParamNames{
"Input", "Gate", "Bass", "Middle", "Treble", "Output", "NoiseGateActive", "ToneStack", "OutNorm", "IRToggle"};
// These are their current names.
// IF YOU CHANGE THE NAMES OF THE PARAMETERS, THEN YOU NEED TO UPDATE THIS!
std::unordered_map<std::string, std::string> newNames{{"Gate", "Threshold"}};
auto getParamByOldName = [&, newNames](std::string& oldName) {
std::string name = newNames.find(oldName) != newNames.end() ? newNames.at(oldName) : oldName;
// Could use a map but eh
for (int i = 0; i < kNumParams; i++)
{
IParam* param = GetParam(i);
if (strcmp(param->GetName(), name.c_str()) == 0)
{
return param;
}
}
return (IParam*)nullptr;
};
TRACE
int pos = startPos;
ENTER_PARAMS_MUTEX
int i = 0;
for (auto it = oldParamNames.begin(); it != oldParamNames.end(); ++it, i++)
{
// Here's the change: instead of assuming that we can iterate through the parameters, we look for the one that now
// holds this info.
// IParam* pParam = mParams.Get(i);
IParam* pParam = getParamByOldName(*it);

double v = 0.0;
pos = chunk.Get(&v, pos);
// It's possible that future versions will not have all of the params of previous versions. If that's the case,
// then this is a null ptr and we skip it.
if (pParam)
{
pParam->Set(v);
Trace(TRACELOC, "%d %s %f", i, pParam->GetName(), pParam->Value());
}
else
{
Trace(TRACELOC, "%d NOT-FOUND", i);
}
}
OnParamReset(kPresetRecall);
LEAVE_PARAMS_MUTEX
return pos;
};
pos = unserialize(chunk, pos);
return pos;
}

void NeuralAmpModeler::_UpdateControlsFromModel()
{
if (mModel == nullptr)
Expand Down Expand Up @@ -985,3 +907,6 @@ void NeuralAmpModeler::_UpdateMeters(sample** inputPointer, sample** outputPoint
mInputSender.ProcessBlock(inputPointer, (int)nFrames, kCtrlTagInputMeter, nChansHack);
mOutputSender.ProcessBlock(outputPointer, (int)nFrames, kCtrlTagOutputMeter, nChansHack);
}

// HACK
#include "Unserialization.cpp"
14 changes: 8 additions & 6 deletions NeuralAmpModeler/NeuralAmpModeler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "AudioDSPTools/dsp/wav.h"
#include "AudioDSPTools/dsp/ResamplingContainer/ResamplingContainer.h"

#include "Colors.h"
#include "ToneStack.h"

#include "IPlug_include_in_plug_hdr.h"
Expand Down Expand Up @@ -150,7 +151,7 @@ class ResamplingNAM : public nam::DSP

int GetLatency() const { return NeedToResample() ? mResampler.GetLatency() : 0; };

void Reset(const double sampleRate, const int maxBlockSize)
void Reset(const double sampleRate, const int maxBlockSize) override
{
mExpectedSampleRate = sampleRate;
mMaxExternalBlockSize = maxBlockSize;
Expand Down Expand Up @@ -244,11 +245,12 @@ class NeuralAmpModeler final : public iplug::Plugin
void _SetInputGain();
void _SetOutputGain();

// Unserialize current-version plug-in data:
int _UnserializeStateCurrent(const iplug::IByteChunk& chunk, int startPos);
// Unserialize v0.7.9 legacy data:
int _UnserializeStateLegacy_0_7_9(const iplug::IByteChunk& chunk, int startPos);
// And other legacy unsrializations if/as needed...
// See: Unserialization.cpp
void _UnserializeApplyConfig(nlohmann::json& config);
// 0.7.9 and later
int _UnserializeStateWithKnownVersion(const iplug::IByteChunk& chunk, int startPos);
// Hopefully 0.7.3-0.7.8, but no gurantees
int _UnserializeStateWithUnknownVersion(const iplug::IByteChunk& chunk, int startPos);

// Update all controls that depend on a model
void _UpdateControlsFromModel();
Expand Down
Loading

0 comments on commit 1960e35

Please sign in to comment.