NeuralAmpModeler.h (10838B)
1 #pragma once 2 3 #include "NeuralAmpModelerCore/NAM/dsp.h" 4 #include "AudioDSPTools/dsp/ImpulseResponse.h" 5 #include "AudioDSPTools/dsp/NoiseGate.h" 6 #include "AudioDSPTools/dsp/dsp.h" 7 #include "AudioDSPTools/dsp/wav.h" 8 #include "AudioDSPTools/dsp/ResamplingContainer/ResamplingContainer.h" 9 10 #include "Colors.h" 11 #include "ToneStack.h" 12 13 #include "IPlug_include_in_plug_hdr.h" 14 #include "ISender.h" 15 16 17 const int kNumPresets = 1; 18 // The plugin is mono inside 19 constexpr size_t kNumChannelsInternal = 1; 20 21 class NAMSender : public iplug::IPeakAvgSender<> 22 { 23 public: 24 NAMSender() 25 : iplug::IPeakAvgSender<>(-90.0, true, 5.0f, 1.0f, 300.0f, 500.0f) 26 { 27 } 28 }; 29 30 enum EParams 31 { 32 // These need to be the first ones because I use their indices to place 33 // their rects in the GUI. 34 kInputLevel = 0, 35 kNoiseGateThreshold, 36 kToneBass, 37 kToneMid, 38 kToneTreble, 39 kOutputLevel, 40 // The rest is fine though. 41 kNoiseGateActive, 42 kEQActive, 43 kIRToggle, 44 // Input calibration 45 kCalibrateInput, 46 kInputCalibrationLevel, 47 kOutputMode, 48 kNumParams 49 }; 50 51 const int numKnobs = 6; 52 53 enum ECtrlTags 54 { 55 kCtrlTagModelFileBrowser = 0, 56 kCtrlTagIRFileBrowser, 57 kCtrlTagInputMeter, 58 kCtrlTagOutputMeter, 59 kCtrlTagSettingsBox, 60 kCtrlTagOutputMode, 61 kCtrlTagCalibrateInput, 62 kCtrlTagInputCalibrationLevel, 63 kNumCtrlTags 64 }; 65 66 enum EMsgTags 67 { 68 // These tags are used from UI -> DSP 69 kMsgTagClearModel = 0, 70 kMsgTagClearIR, 71 kMsgTagHighlightColor, 72 // The following tags are from DSP -> UI 73 kMsgTagLoadFailed, 74 kMsgTagLoadedModel, 75 kMsgTagLoadedIR, 76 kNumMsgTags 77 }; 78 79 // Get the sample rate of a NAM model. 80 // Sometimes, the model doesn't know its own sample rate; this wrapper guesses 48k based on the way that most 81 // people have used NAM in the past. 82 double GetNAMSampleRate(const std::unique_ptr<nam::DSP>& model) 83 { 84 // Some models are from when we didn't have sample rate in the model. 85 // For those, this wraps with the assumption that they're 48k models, which is probably true. 86 const double assumedSampleRate = 48000.0; 87 const double reportedEncapsulatedSampleRate = model->GetExpectedSampleRate(); 88 const double encapsulatedSampleRate = 89 reportedEncapsulatedSampleRate <= 0.0 ? assumedSampleRate : reportedEncapsulatedSampleRate; 90 return encapsulatedSampleRate; 91 }; 92 93 class ResamplingNAM : public nam::DSP 94 { 95 public: 96 // Resampling wrapper around the NAM models 97 ResamplingNAM(std::unique_ptr<nam::DSP> encapsulated, const double expected_sample_rate) 98 : nam::DSP(expected_sample_rate) 99 , mEncapsulated(std::move(encapsulated)) 100 , mResampler(GetNAMSampleRate(mEncapsulated)) 101 { 102 // Assign the encapsulated object's processing function to this object's member so that the resampler can use it: 103 auto ProcessBlockFunc = [&](NAM_SAMPLE** input, NAM_SAMPLE** output, int numFrames) { 104 mEncapsulated->process(input[0], output[0], numFrames); 105 }; 106 mBlockProcessFunc = ProcessBlockFunc; 107 108 // Get the other information from the encapsulated NAM so that we can tell the outside world about what we're 109 // holding. 110 if (mEncapsulated->HasLoudness()) 111 { 112 SetLoudness(mEncapsulated->GetLoudness()); 113 } 114 if (mEncapsulated->HasInputLevel()) 115 { 116 SetInputLevel(mEncapsulated->GetInputLevel()); 117 } 118 if (mEncapsulated->HasOutputLevel()) 119 { 120 SetOutputLevel(mEncapsulated->GetOutputLevel()); 121 } 122 123 // NOTE: prewarm samples doesn't mean anything--we can prewarm the encapsulated model as it likes and be good to 124 // go. 125 // _prewarm_samples = 0; 126 127 // And be ready 128 int maxBlockSize = 2048; // Conservative 129 Reset(expected_sample_rate, maxBlockSize); 130 }; 131 132 ~ResamplingNAM() = default; 133 134 void prewarm() override { mEncapsulated->prewarm(); }; 135 136 void process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames) override 137 { 138 if (num_frames > mMaxExternalBlockSize) 139 // We can afford to be careful 140 throw std::runtime_error("More frames were provided than the max expected!"); 141 142 if (!NeedToResample()) 143 { 144 mEncapsulated->process(input, output, num_frames); 145 } 146 else 147 { 148 mResampler.ProcessBlock(&input, &output, num_frames, mBlockProcessFunc); 149 } 150 }; 151 152 int GetLatency() const { return NeedToResample() ? mResampler.GetLatency() : 0; }; 153 154 void Reset(const double sampleRate, const int maxBlockSize) override 155 { 156 mExpectedSampleRate = sampleRate; 157 mMaxExternalBlockSize = maxBlockSize; 158 mResampler.Reset(sampleRate, maxBlockSize); 159 160 // Allocations in the encapsulated model (HACK) 161 // Stolen some code from the resampler; it'd be nice to have these exposed as methods? :) 162 const double mUpRatio = sampleRate / GetEncapsulatedSampleRate(); 163 const auto maxEncapsulatedBlockSize = static_cast<int>(std::ceil(static_cast<double>(maxBlockSize) / mUpRatio)); 164 mEncapsulated->ResetAndPrewarm(sampleRate, maxEncapsulatedBlockSize); 165 }; 166 167 // So that we can let the world know if we're resampling (useful for debugging) 168 double GetEncapsulatedSampleRate() const { return GetNAMSampleRate(mEncapsulated); }; 169 170 private: 171 bool NeedToResample() const { return GetExpectedSampleRate() != GetEncapsulatedSampleRate(); }; 172 // The encapsulated NAM 173 std::unique_ptr<nam::DSP> mEncapsulated; 174 175 // The resampling wrapper 176 dsp::ResamplingContainer<NAM_SAMPLE, 1, 12> mResampler; 177 178 // Used to check that we don't get too large a block to process. 179 int mMaxExternalBlockSize = 0; 180 181 // This function is defined to conform to the interface expected by the iPlug2 resampler. 182 std::function<void(NAM_SAMPLE**, NAM_SAMPLE**, int)> mBlockProcessFunc; 183 }; 184 185 class NeuralAmpModeler final : public iplug::Plugin 186 { 187 public: 188 NeuralAmpModeler(const iplug::InstanceInfo& info); 189 ~NeuralAmpModeler(); 190 191 void ProcessBlock(iplug::sample** inputs, iplug::sample** outputs, int nFrames) override; 192 void OnReset() override; 193 void OnIdle() override; 194 195 bool SerializeState(iplug::IByteChunk& chunk) const override; 196 int UnserializeState(const iplug::IByteChunk& chunk, int startPos) override; 197 void OnUIOpen() override; 198 bool OnHostRequestingSupportedViewConfiguration(int width, int height) override { return true; } 199 200 void OnParamChange(int paramIdx) override; 201 void OnParamChangeUI(int paramIdx, iplug::EParamSource source) override; 202 bool OnMessage(int msgTag, int ctrlTag, int dataSize, const void* pData) override; 203 204 private: 205 // Allocates mInputPointers and mOutputPointers 206 void _AllocateIOPointers(const size_t nChans); 207 // Moves DSP modules from staging area to the main area. 208 // Also deletes DSP modules that are flagged for removal. 209 // Exists so that we don't try to use a DSP module that's only 210 // partially-instantiated. 211 void _ApplyDSPStaging(); 212 // Deallocates mInputPointers and mOutputPointers 213 void _DeallocateIOPointers(); 214 // Fallback that just copies inputs to outputs if mDSP doesn't hold a model. 215 void _FallbackDSP(iplug::sample** inputs, iplug::sample** outputs, const size_t numChannels, const size_t numFrames); 216 // Sizes based on mInputArray 217 size_t _GetBufferNumChannels() const; 218 size_t _GetBufferNumFrames() const; 219 void _InitToneStack(); 220 // Loads a NAM model and stores it to mStagedNAM 221 // Returns an empty string on success, or an error message on failure. 222 std::string _StageModel(const WDL_String& dspFile); 223 // Loads an IR and stores it to mStagedIR. 224 // Return status code so that error messages can be relayed if 225 // it wasn't successful. 226 dsp::wav::LoadReturnCode _StageIR(const WDL_String& irPath); 227 228 bool _HaveModel() const { return this->mModel != nullptr; }; 229 // Prepare the input & output buffers 230 void _PrepareBuffers(const size_t numChannels, const size_t numFrames); 231 // Manage pointers 232 void _PrepareIOPointers(const size_t nChans); 233 // Copy the input buffer to the object, applying input level. 234 // :param nChansIn: In from external 235 // :param nChansOut: Out to the internal of the DSP routine 236 void _ProcessInput(iplug::sample** inputs, const size_t nFrames, const size_t nChansIn, const size_t nChansOut); 237 // Copy the output to the output buffer, applying output level. 238 // :param nChansIn: In from internal 239 // :param nChansOut: Out to external 240 void _ProcessOutput(iplug::sample** inputs, iplug::sample** outputs, const size_t nFrames, const size_t nChansIn, 241 const size_t nChansOut); 242 // Resetting for models and IRs, called by OnReset 243 void _ResetModelAndIR(const double sampleRate, const int maxBlockSize); 244 245 void _SetInputGain(); 246 void _SetOutputGain(); 247 248 // See: Unserialization.cpp 249 void _UnserializeApplyConfig(nlohmann::json& config); 250 // 0.7.9 and later 251 int _UnserializeStateWithKnownVersion(const iplug::IByteChunk& chunk, int startPos); 252 // Hopefully 0.7.3-0.7.8, but no gurantees 253 int _UnserializeStateWithUnknownVersion(const iplug::IByteChunk& chunk, int startPos); 254 255 // Update all controls that depend on a model 256 void _UpdateControlsFromModel(); 257 258 // Make sure that the latency is reported correctly. 259 void _UpdateLatency(); 260 261 // Update level meters 262 // Called within ProcessBlock(). 263 // Assume _ProcessInput() and _ProcessOutput() were run immediately before. 264 void _UpdateMeters(iplug::sample** inputPointer, iplug::sample** outputPointer, const size_t nFrames, 265 const size_t nChansIn, const size_t nChansOut); 266 267 // Member data 268 269 // Input arrays to NAM 270 std::vector<std::vector<iplug::sample>> mInputArray; 271 // Output from NAM 272 std::vector<std::vector<iplug::sample>> mOutputArray; 273 // Pointer versions 274 iplug::sample** mInputPointers = nullptr; 275 iplug::sample** mOutputPointers = nullptr; 276 277 // Input and output gain 278 double mInputGain = 1.0; 279 double mOutputGain = 1.0; 280 281 // Noise gates 282 dsp::noise_gate::Trigger mNoiseGateTrigger; 283 dsp::noise_gate::Gain mNoiseGateGain; 284 // The model actually being used: 285 std::unique_ptr<ResamplingNAM> mModel; 286 // And the IR 287 std::unique_ptr<dsp::ImpulseResponse> mIR; 288 // Manages switching what DSP is being used. 289 std::unique_ptr<ResamplingNAM> mStagedModel; 290 std::unique_ptr<dsp::ImpulseResponse> mStagedIR; 291 // Flags to take away the modules at a safe time. 292 std::atomic<bool> mShouldRemoveModel = false; 293 std::atomic<bool> mShouldRemoveIR = false; 294 295 std::atomic<bool> mNewModelLoadedInDSP = false; 296 std::atomic<bool> mModelCleared = false; 297 298 // Tone stack modules 299 std::unique_ptr<dsp::tone_stack::AbstractToneStack> mToneStack; 300 301 // Post-IR filters 302 recursive_linear_filter::HighPass mHighPass; 303 // recursive_linear_filter::LowPass mLowPass; 304 305 // Path to model's config.json or model.nam 306 WDL_String mNAMPath; 307 // Path to IR (.wav file) 308 WDL_String mIRPath; 309 310 WDL_String mHighLightColor{PluginColors::NAM_THEMECOLOR.ToColorCode()}; 311 312 std::unordered_map<std::string, double> mNAMParams = {{"Input", 0.0}, {"Output", 0.0}}; 313 314 NAMSender mInputSender, mOutputSender; 315 };