commit 9f34041d9f12fdf606745a2678fcd5c2684db0b8
parent 11a9dd97ef9c888e5d5bac17708ed1bcfef779bf
Author: Steven Atkinson <[email protected]>
Date: Sun, 5 Feb 2023 22:00:35 -0800
Noise gate (#65)
* Implement trigger
* Add trigger to plugin, fix some bugs
* Gain module implemented. Seems to work
* Knob to modify threshold
* Quadratic gain reduction curve
* Hook gate output to tone stack input
Diffstat:
10 files changed, 426 insertions(+), 40 deletions(-)
diff --git a/NeuralAmpModeler/NeuralAmpModeler.cpp b/NeuralAmpModeler/NeuralAmpModeler.cpp
@@ -98,26 +98,22 @@ const IVStyle styleInactive = style.WithColors(inactiveColorSpec);
NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo &info)
: Plugin(info, MakeConfig(kNumParams, kNumPresets)),
- mInputPointers(nullptr), mOutputPointers(nullptr), mNAM(nullptr),
- mIR(nullptr), mStagedNAM(nullptr), mStagedIR(nullptr),
+ mInputPointers(nullptr), mOutputPointers(nullptr), mNoiseGateTrigger(),
+ mNAM(nullptr), mIR(nullptr), mStagedNAM(nullptr), mStagedIR(nullptr),
mFlagRemoveNAM(false), mFlagRemoveIR(false),
mDefaultNAMString("Select model..."), mDefaultIRString("Select IR..."),
mToneBass(), mToneMid(), mToneTreble(), mNAMPath(), mIRPath(),
mInputSender(), mOutputSender() {
- GetParam(kInputLevel)->InitGain("Input", 0.0, -20.0, 20.0, 0.1);
- GetParam(kToneBass)->InitDouble("Bass", 5.0, 0.0, 10.0, 0.1);
- GetParam(kToneMid)->InitDouble("Middle", 5.0, 0.0, 10.0, 0.1);
- GetParam(kToneTreble)->InitDouble("Treble", 5.0, 0.0, 10.0, 0.1);
- GetParam(kOutputLevel)->InitGain("Output", 0.0, -40.0, 40.0, 0.1);
- GetParam(kEQActive)->InitBool("ToneStack", false);
-
- // try {
- // this->mDSP = get_hard_dsp();
- // }
- // catch (std::exception& e) {
- // std::cerr << "Failed to read hard coded DSP" << std::endl;
- // std::cerr << e.what() << std::endl;
- // }
+ this->GetParam(kInputLevel)->InitGain("Input", 0.0, -20.0, 20.0, 0.1);
+ this->GetParam(kToneBass)->InitDouble("Bass", 5.0, 0.0, 10.0, 0.1);
+ this->GetParam(kToneMid)->InitDouble("Middle", 5.0, 0.0, 10.0, 0.1);
+ this->GetParam(kToneTreble)->InitDouble("Treble", 5.0, 0.0, 10.0, 0.1);
+ this->GetParam(kOutputLevel)->InitGain("Output", 0.0, -40.0, 40.0, 0.1);
+ this->GetParam(kNoiseGateThreshold)
+ ->InitGain("Noise Gate", -80.0, -100.0, 0.0, 0.1);
+ this->GetParam(kEQActive)->InitBool("ToneStack", false);
+
+ this->mNoiseGateTrigger.AddListener(&this->mNoiseGateGain);
mMakeGraphicsFunc = [&]() {
return MakeGraphics(*this, PLUG_WIDTH, PLUG_HEIGHT, PLUG_FPS,
@@ -138,15 +134,23 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo &info)
const float titleHeight = 50.0f;
const auto titleLabel = content.GetFromTop(titleHeight);
- // Areas for knobs
+ // Area for the Noise gate knob
const float knobHalfPad = 10.0f;
const float knobPad = 2.0f * knobHalfPad;
+ const float noiseGateKnobHeight = 80.0f;
+ const float noiseGateKnobWidth = 100.0f;
+ const IRECT noiseGateArea =
+ content.GetFromTop(noiseGateKnobHeight).GetFromLeft(noiseGateKnobWidth);
+
+ // Areas for knobs
+ const float knobsExtraSpaceBelowTitle = 25.0f;
const float knobHalfHeight = 70.0f;
const float knobHeight = 2.0f * knobHalfHeight;
- const auto knobs = content.GetFromTop(knobHeight)
- .GetReducedFromLeft(knobPad)
- .GetReducedFromRight(knobPad)
- .GetTranslated(0.0f, titleHeight);
+ const auto knobs =
+ content.GetFromTop(knobHeight)
+ .GetReducedFromLeft(knobPad)
+ .GetReducedFromRight(knobPad)
+ .GetTranslated(0.0f, titleHeight + knobsExtraSpaceBelowTitle);
const IRECT inputKnobArea =
knobs.GetGridCell(0, kInputLevel, 1, numKnobs).GetPadded(-10);
const IRECT bassKnobArea =
@@ -326,6 +330,9 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo &info)
EDirection::Horizontal);
pGraphics->AttachControl(toneStackSlider);
+ // Noise gate
+ pGraphics->AttachControl(
+ new IVKnobControl(noiseGateArea, kNoiseGateThreshold, "", style));
// The knobs
pGraphics->AttachControl(
new IVKnobControl(inputKnobArea, kInputLevel, "", style));
@@ -366,7 +373,7 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo &info)
toneStackSlider->SetActionFunction(toneStackAction);
// The meters
- const float meterMin = -60.0f;
+ const float meterMin = -90.0f;
const float meterMax = -0.01f;
pGraphics
->AttachControl(
@@ -470,6 +477,7 @@ void NeuralAmpModeler::ProcessBlock(iplug::sample **inputs,
const size_t numChannelsExternalOut = (size_t)this->NOutChansConnected();
const size_t numChannelsInternal = this->mNUM_INTERNAL_CHANNELS;
const size_t numFrames = (size_t)nFrames;
+ const double sampleRate = this->GetSampleRate();
this->_PrepareBuffers(numChannelsInternal, numFrames);
// Input is collapsed to mono in preparation for the NAM.
@@ -478,22 +486,42 @@ void NeuralAmpModeler::ProcessBlock(iplug::sample **inputs,
this->_ApplyDSPStaging();
const bool toneStackActive = this->GetParam(kEQActive)->Value();
+ // Noise gate trigger
+ sample **triggerOutput;
+ {
+ const double time = 0.01;
+ const double threshold =
+ this->GetParam(kNoiseGateThreshold)->Value(); // GetParam...
+ const double ratio = 0.1; // Quadratic...
+ const double openTime = 0.005;
+ const double holdTime = 0.01;
+ const double closeTime = 0.05;
+ const dsp::noise_gate::TriggerParams triggerParams(
+ time, threshold, ratio, openTime, holdTime, closeTime);
+ this->mNoiseGateTrigger.SetParams(triggerParams);
+ this->mNoiseGateTrigger.SetSampleRate(sampleRate);
+ triggerOutput = this->mNoiseGateTrigger.Process(
+ mInputPointers, numChannelsInternal, numFrames);
+ }
+
if (mNAM != nullptr) {
// TODO remove input / output gains from here.
const double inputGain = 1.0;
const double outputGain = 1.0;
const int nChans = (int)numChannelsInternal;
- mNAM->process(this->mInputPointers, this->mOutputPointers, nChans, nFrames,
+ mNAM->process(triggerOutput, this->mOutputPointers, nChans, nFrames,
inputGain, outputGain, mNAMParams);
mNAM->finalize_(nFrames);
} else {
- this->_FallbackDSP(nFrames);
+ this->_FallbackDSP(triggerOutput, this->mOutputPointers,
+ numChannelsInternal, numFrames);
}
+ // Apply the noise gate
+ sample **gateGainOutput = this->mNoiseGateGain.Process(
+ this->mOutputPointers, numChannelsInternal, numFrames);
- sample **toneStackOutPointers = this->mOutputPointers;
+ sample **toneStackOutPointers = gateGainOutput;
if (toneStackActive) {
- // Tone stack
- const double sampleRate = this->GetSampleRate();
// Translate params from knob 0-10 to dB.
// Tuned ranges based on my ear. E.g. seems treble doesn't need nearly as
// much swing as bass can use.
@@ -524,8 +552,8 @@ void NeuralAmpModeler::ProcessBlock(iplug::sample **inputs,
this->mToneBass.SetParams(bassParams);
this->mToneMid.SetParams(midParams);
this->mToneTreble.SetParams(trebleParams);
- sample **bassPointers = this->mToneBass.Process(
- this->mOutputPointers, numChannelsInternal, numFrames);
+ sample **bassPointers =
+ this->mToneBass.Process(gateGainOutput, numChannelsInternal, numFrames);
sample **midPointers =
this->mToneMid.Process(bassPointers, numChannelsInternal, numFrames);
sample **treblePointers =
@@ -637,10 +665,12 @@ void NeuralAmpModeler::_DeallocateIOPointers() {
"Failed to deallocate pointer to output buffer!\n");
}
-void NeuralAmpModeler::_FallbackDSP(const int nFrames) {
- const size_t nChans = this->mNUM_INTERNAL_CHANNELS;
- for (auto c = 0; c < nChans; c++)
- for (auto s = 0; s < nFrames; s++)
+void NeuralAmpModeler::_FallbackDSP(iplug::sample **inputs,
+ iplug::sample **outputs,
+ const size_t numChannels,
+ const size_t numFrames) {
+ for (auto c = 0; c < numChannels; c++)
+ for (auto s = 0; s < numFrames; s++)
this->mOutputArray[c][s] = this->mInputArray[c][s];
}
diff --git a/NeuralAmpModeler/NeuralAmpModeler.h b/NeuralAmpModeler/NeuralAmpModeler.h
@@ -2,6 +2,7 @@
#include "dsp.h"
#include "dsp/ImpulseResponse.h"
+#include "dsp/NoiseGate.h"
#include "dsp/RecursiveLinearFilter.h"
#include "IPlug_include_in_plug_hdr.h"
@@ -13,11 +14,15 @@
const int kNumPresets = 1;
enum EParams {
+ // These need to be the first ones because I use their indices to place
+ // their rects in the GUI.
kInputLevel = 0,
kToneBass,
kToneMid,
kToneTreble,
kOutputLevel,
+ // The rest is fine though.
+ kNoiseGateThreshold,
kEQActive,
kNumParams
};
@@ -65,7 +70,8 @@ private:
// Deallocates mInputPointers and mOutputPointers
void _DeallocateIOPointers();
// Fallback that just copies inputs to outputs if mDSP doesn't hold a model.
- void _FallbackDSP(const int nFrames);
+ void _FallbackDSP(iplug::sample **inputs, iplug::sample **outputs,
+ const size_t numChannels, const size_t numFrames);
// Sizes based on mInputArray
size_t _GetBufferNumChannels() const;
size_t _GetBufferNumFrames() const;
@@ -119,6 +125,9 @@ private:
iplug::sample **mInputPointers;
iplug::sample **mOutputPointers;
+ // Noise gates
+ dsp::noise_gate::Trigger mNoiseGateTrigger;
+ dsp::noise_gate::Gain mNoiseGateGain;
// The Neural Amp Model (NAM) actually being used:
std::unique_ptr<DSP> mNAM;
// And the IR
diff --git a/NeuralAmpModeler/dsp/NoiseGate.cpp b/NeuralAmpModeler/dsp/NoiseGate.cpp
@@ -0,0 +1,165 @@
+//
+// NoiseGate.cpp
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 2/5/23.
+//
+
+#include <cmath> // pow
+
+#include "NoiseGate.h"
+
+double _LevelToDB(const double db) { return 10.0 * log10(db); }
+
+double _DBToLevel(const double level) { return pow(10.0, level / 10.0); }
+
+dsp::noise_gate::Trigger::Trigger()
+ : mParams(0.05, -60.0, 1.5, 0.002, 0.050, 0.050), mSampleRate(0) {}
+
+double signum(const double val) { return (0.0 < val) - (val < 0.0); }
+
+iplug::sample **dsp::noise_gate::Trigger::Process(iplug::sample **inputs,
+ const size_t numChannels,
+ const size_t numFrames) {
+ this->_PrepareBuffers(numChannels, numFrames);
+
+ // A bunch of numbers we'll use a few times.
+ const double alpha =
+ pow(0.5, 1.0 / (this->mParams.GetTime() * this->mSampleRate));
+ const double beta = 1.0 - alpha;
+ const double threshold = this->mParams.GetThreshold();
+ const double dt = 1.0 / this->mSampleRate;
+ const double maxHold = this->mParams.GetHoldTime();
+ const double maxGainReduction = this->_GetMaxGainReduction();
+ // Amount of open or close in a sample: rate times time
+ const double dOpen =
+ -this->_GetMaxGainReduction() / this->mParams.GetOpenTime() * dt; // >0
+ const double dClose =
+ this->_GetMaxGainReduction() / this->mParams.GetCloseTime() * dt; // <0
+
+ // The main algorithm: compute the gain reduction
+ for (auto c = 0; c < numChannels; c++) {
+ for (auto s = 0; s < numFrames; s++) {
+ this->mLevel[c] = std::clamp(alpha * this->mLevel[c] +
+ beta * (inputs[c][s] * inputs[c][s]),
+ MINIMUM_LOUDNESS_POWER, 1000.0);
+ const double levelDB = _LevelToDB(this->mLevel[c]);
+ if (this->mState[c] == dsp::noise_gate::Trigger::State::HOLDING) {
+ this->mGainReductionDB[c][s] = 0.0;
+ this->mLastGainReductionDB[c] = 0.0;
+ if (levelDB < threshold) {
+ this->mTimeHeld[c] += dt;
+ if (this->mTimeHeld[c] >= maxHold)
+ this->mState[c] = dsp::noise_gate::Trigger::State::MOVING;
+ } else {
+ this->mTimeHeld[c] = 0.0;
+ }
+ } else { // Moving
+ const double targetGainReduction = this->_GetGainReduction(levelDB);
+ if (targetGainReduction > this->mLastGainReductionDB[c]) {
+ const double dGain = std::clamp(
+ 0.5 * (targetGainReduction - this->mLastGainReductionDB[c]), 0.0,
+ dOpen);
+ this->mLastGainReductionDB[c] += dGain;
+ if (this->mLastGainReductionDB[c] >= 0.0) {
+ this->mLastGainReductionDB[c] = 0.0;
+ this->mState[c] = dsp::noise_gate::Trigger::State::HOLDING;
+ this->mTimeHeld[c] = 0.0;
+ }
+ } else if (targetGainReduction < this->mLastGainReductionDB[c]) {
+ const double dGain = std::clamp(
+ 0.5 * (targetGainReduction - this->mLastGainReductionDB[c]),
+ dClose, 0.0);
+ this->mLastGainReductionDB[c] += dGain;
+ if (this->mLastGainReductionDB[c] < maxGainReduction) {
+ this->mLastGainReductionDB[c] = maxGainReduction;
+ }
+ }
+ this->mGainReductionDB[c][s] = this->mLastGainReductionDB[c];
+ }
+ }
+ }
+
+ // Share the results with gain objects that are listening to this trigger:
+ for (auto gain = this->mGainListeners.begin();
+ gain != this->mGainListeners.end(); ++gain)
+ (*gain)->SetGainReductionDB(this->mGainReductionDB);
+
+ // Copy input to output
+ for (auto c = 0; c < numChannels; c++)
+ memcpy(this->mOutputs[c].data(), inputs[c],
+ numFrames * sizeof(iplug::sample));
+ return this->_GetPointers();
+}
+
+void dsp::noise_gate::Trigger::_PrepareBuffers(const size_t numChannels,
+ const size_t numFrames) {
+ const size_t oldChannels = this->_GetNumChannels();
+ const size_t oldFrames = this->_GetNumFrames();
+ this->DSP::_PrepareBuffers(numChannels, numFrames);
+
+ const bool updateChannels = numChannels != oldChannels;
+ const bool updateFrames = updateChannels || numFrames != oldFrames;
+
+ if (updateChannels || updateFrames) {
+ const double maxGainReduction = this->_GetMaxGainReduction();
+ if (updateChannels) {
+ this->mGainReductionDB.resize(numChannels);
+ this->mLastGainReductionDB.resize(numChannels);
+ std::fill(this->mLastGainReductionDB.begin(),
+ this->mLastGainReductionDB.end(), maxGainReduction);
+ this->mState.resize(numChannels);
+ std::fill(this->mState.begin(), this->mState.end(),
+ dsp::noise_gate::Trigger::State::MOVING);
+ this->mLevel.resize(numChannels);
+ std::fill(this->mLevel.begin(), this->mLevel.end(),
+ MINIMUM_LOUDNESS_POWER);
+ this->mTimeHeld.resize(numChannels);
+ std::fill(this->mTimeHeld.begin(), this->mTimeHeld.end(), 0.0);
+ }
+ if (updateFrames) {
+ for (auto i = 0; i < this->mGainReductionDB.size(); i++) {
+ this->mGainReductionDB[i].resize(numFrames);
+ std::fill(this->mGainReductionDB[i].begin(),
+ this->mGainReductionDB[i].end(), maxGainReduction);
+ }
+ }
+ }
+}
+
+// Gain========================================================================
+
+iplug::sample **dsp::noise_gate::Gain::Process(iplug::sample **inputs,
+ const size_t numChannels,
+ const size_t numFrames) {
+ // Assume that SetGainReductionDB() was just called to get data from a
+ // trigger. Could use listeners...
+ this->_PrepareBuffers(numChannels, numFrames);
+
+ if (this->mGainReductionDB.size() != numChannels) {
+ std::stringstream ss;
+ ss << "Gain module expected to operate on " << this->mGainReductionDB.size()
+ << "channels, but " << numChannels << " were provided.";
+ throw std::runtime_error(ss.str());
+ }
+ if ((this->mGainReductionDB.size() == 0) && (numFrames > 0)) {
+ std::stringstream ss;
+ ss << "No channels expected by gain module, yet " << numFrames
+ << " were provided?";
+ throw std::runtime_error(ss.str());
+ } else if (this->mGainReductionDB[0].size() != numFrames) {
+ std::stringstream ss;
+ ss << "Gain module expected to operate on "
+ << this->mGainReductionDB[0].size() << "frames, but " << numFrames
+ << " were provided.";
+ throw std::runtime_error(ss.str());
+ }
+
+ // Apply gain!
+ for (auto c = 0; c < numChannels; c++)
+ for (auto s = 0; s < numFrames; s++)
+ this->mOutputs[c][s] =
+ _DBToLevel(this->mGainReductionDB[c][s]) * inputs[c][s];
+
+ return this->_GetPointers();
+}
diff --git a/NeuralAmpModeler/dsp/NoiseGate.h b/NeuralAmpModeler/dsp/NoiseGate.h
@@ -0,0 +1,140 @@
+//
+// NoiseGate.h
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 2/5/23.
+//
+
+#ifndef NoiseGate_h
+#define NoiseGate_h
+
+#include <cmath>
+#include <unordered_set>
+#include <vector>
+
+#include "dsp.h"
+
+namespace dsp {
+namespace noise_gate {
+// Disclaimer: No one told me how noise gates work. I'm just going to try
+// and have fun with it and see if I like what I get! :D
+
+// "The noise floor." The loudness of anything quieter than this is bumped
+// up to as if it were this loud for gating purposes (i.e. computing gain
+// reduction).
+const double MINIMUM_LOUDNESS_DB = -120.0;
+const double MINIMUM_LOUDNESS_POWER = pow(10.0, MINIMUM_LOUDNESS_DB / 10.0);
+
+// Parts 2: The gain module.
+// This applies the gain reduction taht was determined by the trigger.
+// It's declared first so that the trigger can define listeners without a
+// forward declaration.
+
+// The class that applies the gain reductions calculated by a trigger instance.
+class Gain : public DSP {
+public:
+ iplug::sample **Process(iplug::sample **inputs, const size_t numChannels,
+ const size_t numFrames) override;
+
+ void SetGainReductionDB(std::vector<std::vector<double>> &gainReductionDB) {
+ this->mGainReductionDB = gainReductionDB;
+ }
+
+private:
+ std::vector<std::vector<double>> mGainReductionDB;
+};
+
+// Part 1 of the noise gate: the trigger.
+// This listens to a stream of incoming audio and determines how much gain
+// to apply based on the loudness of the signal.
+
+class TriggerParams {
+public:
+ TriggerParams(const double time, const double threshold, const double ratio,
+ const double openTime, const double holdTime,
+ const double closeTime)
+ : mTime(time), mThreshold(threshold), mRatio(ratio), mOpenTime(openTime),
+ mHoldTime(holdTime), mCloseTime(closeTime){};
+
+ double GetTime() const { return this->mTime; };
+ double GetThreshold() const { return this->mThreshold; };
+ double GetRatio() const { return this->mRatio; };
+ double GetOpenTime() const { return this->mOpenTime; };
+ double GetHoldTime() const { return this->mHoldTime; };
+ double GetCloseTime() const { return this->mCloseTime; };
+
+private:
+ // The time constant for quantifying the loudness of the signal.
+ double mTime;
+ // The threshold at which expanssion starts
+ double mThreshold;
+ // The compression ratio.
+ double mRatio;
+ // How long it takes to go from maximum gain reduction to zero.
+ double mOpenTime;
+ // How long to stay open before starting to close.
+ double mHoldTime;
+ // How long it takes to go from open to maximum gain reduction.
+ double mCloseTime;
+};
+
+class Trigger : public DSP {
+public:
+ Trigger();
+
+ iplug::sample **Process(iplug::sample **inputs, const size_t numChannels,
+ const size_t numFrames) override;
+ std::vector<std::vector<double>> GetGainReduction() const {
+ return this->mGainReductionDB;
+ };
+ void SetParams(const TriggerParams ¶ms) { this->mParams = params; };
+ void SetSampleRate(const double sampleRate) {
+ this->mSampleRate = sampleRate;
+ }
+ std::vector<std::vector<double>> GetGainReductionDB() const {
+ return this->mGainReductionDB;
+ };
+
+ void AddListener(Gain *gain) {
+ // This might be risky dropping a raw pointer, but I don't think that the
+ // gain would be destructed, so probably ok.
+ this->mGainListeners.insert(gain);
+ }
+
+private:
+ enum class State { MOVING = 0, HOLDING };
+
+ double _GetGainReduction(const double levelDB) const {
+ const double threshold = this->mParams.GetThreshold();
+ // Quadratic gain reduction? :)
+ return levelDB < threshold
+ ? -(this->mParams.GetRatio()) * (levelDB - threshold) *
+ (levelDB - threshold)
+ : 0.0;
+ }
+ double _GetMaxGainReduction() const {
+ return this->_GetGainReduction(MINIMUM_LOUDNESS_DB);
+ }
+ virtual void _PrepareBuffers(const size_t numChannels,
+ const size_t numFrames) override;
+
+ TriggerParams mParams;
+ std::vector<State> mState; // One per channel
+ std::vector<double> mLevel;
+
+ // Hold the vectors of gain reduction for the block, in dB.
+ // These can be given to the Gain object.
+ std::vector<std::vector<double>> mGainReductionDB;
+ std::vector<double> mLastGainReductionDB;
+
+ double mSampleRate;
+ // How long we've been holding
+ std::vector<double> mTimeHeld;
+
+ std::unordered_set<Gain *> mGainListeners;
+};
+
+}; // namespace noise_gate
+}; // namespace dsp
+
+#endif /* NoiseGate_h */
diff --git a/NeuralAmpModeler/dsp/dsp.cpp b/NeuralAmpModeler/dsp/dsp.cpp
@@ -509,13 +509,18 @@ iplug::sample **dsp::DSP::_GetPointers() {
void dsp::DSP::_PrepareBuffers(const size_t numChannels,
const size_t numFrames) {
- if (this->_GetNumChannels() != numChannels) {
+ const size_t oldFrames = this->_GetNumFrames();
+ const size_t oldChannels = this->_GetNumChannels();
+
+ const bool resizeChannels = oldChannels != numChannels;
+ const bool resizeFrames = resizeChannels || (oldFrames != numFrames);
+ if (resizeChannels) {
this->mOutputs.resize(numChannels);
- for (auto c = 0; c < numChannels; c++) {
- this->mOutputs[c].resize(numFrames);
- }
this->_ResizePointers(numChannels);
}
+ if (resizeFrames)
+ for (auto c = 0; c < numChannels; c++)
+ this->mOutputs[c].resize(numFrames);
}
void dsp::DSP::_ResizePointers(const size_t numChannels) {
diff --git a/NeuralAmpModeler/dsp/dsp.h b/NeuralAmpModeler/dsp/dsp.h
@@ -338,6 +338,9 @@ protected:
void _DeallocateOutputPointers();
size_t _GetNumChannels() const { return this->mOutputs.size(); };
+ size_t _GetNumFrames() const {
+ return this->_GetNumChannels() > 0 ? this->mOutputs[0].size() : 0;
+ }
// Return a pointer-to-pointers for the DSP's output buffers (all channels)
// Assumes that ._PrepareBuffers() was called recently enough.
iplug::sample **_GetPointers();
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-iOS.xcodeproj/project.pbxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-iOS.xcodeproj/project.pbxproj
@@ -72,6 +72,7 @@
4FDF6D7B2267CE540007B686 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FDF6D752267CE540007B686 /* AppDelegate.m */; };
4FDF6D7F2267CEBA0007B686 /* IPlugAUPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4FDF6D7D2267CEBA0007B686 /* IPlugAUPlayer.mm */; };
91236D811B08F59300734C5E /* NeuralAmpModelerAppExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 91236D771B08F59300734C5E /* NeuralAmpModelerAppExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ AA9F7C4C299085BE00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C4B299085BE00D99615 /* NoiseGate.cpp */; };
AAD9F849295EC49D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F848295EC49D009DBBA2 /* RecursiveLinearFilter.cpp */; };
AAD9F85D295F762B009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F85C295F762B009DBBA2 /* ImpulseResponse.cpp */; };
AAD9F86E2960D4D0009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F86C2960D4D0009DBBA2 /* wav.cpp */; };
@@ -318,6 +319,8 @@
4FFF108820A1036200D3092F /* NeuralAmpModeler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NeuralAmpModeler.h; path = ../NeuralAmpModeler.h; sourceTree = "<group>"; };
91236D0D1B08F42B00734C5E /* NeuralAmpModeler.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NeuralAmpModeler.app; sourceTree = BUILT_PRODUCTS_DIR; };
91236D771B08F59300734C5E /* NeuralAmpModelerAppExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NeuralAmpModelerAppExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ AA9F7C4B299085BE00D99615 /* NoiseGate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoiseGate.cpp; sourceTree = "<group>"; };
+ AA9F7C50299085DA00D99615 /* NoiseGate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NoiseGate.h; sourceTree = "<group>"; };
AAD9F848295EC49D009DBBA2 /* RecursiveLinearFilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RecursiveLinearFilter.cpp; path = /Users/steve/src/NeuralAmpModelerPlugin/NeuralAmpModeler/dsp/RecursiveLinearFilter.cpp; sourceTree = "<absolute>"; };
AAD9F84A295EC4A0009DBBA2 /* RecursiveLinearFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RecursiveLinearFilter.h; path = /Users/steve/src/NeuralAmpModelerPlugin/NeuralAmpModeler/dsp/RecursiveLinearFilter.h; sourceTree = "<absolute>"; };
AAD9F85B295F761C009DBBA2 /* ImpulseResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImpulseResponse.h; sourceTree = "<group>"; };
@@ -377,6 +380,8 @@
AAD9F85C295F762B009DBBA2 /* ImpulseResponse.cpp */,
4F3EF90928DE0BA7002972F2 /* lstm.h */,
4F3EF90C28DE0BA7002972F2 /* lstm.cpp */,
+ AA9F7C4B299085BE00D99615 /* NoiseGate.cpp */,
+ AA9F7C50299085DA00D99615 /* NoiseGate.h */,
4F3EF91228DE0BA7002972F2 /* numpy_util.h */,
4F3EF90F28DE0BA7002972F2 /* numpy_util.cpp */,
AAD9F84A295EC4A0009DBBA2 /* RecursiveLinearFilter.h */,
@@ -960,6 +965,7 @@
4F914A4C26B4911A00E19BD1 /* IPlugAUViewController.mm in Sources */,
4FA61F8E22E89B4300A92C58 /* IGraphicsIOS.mm in Sources */,
4FA61F9022E8A1F500A92C58 /* IGraphics.cpp in Sources */,
+ AA9F7C4C299085BE00D99615 /* NoiseGate.cpp in Sources */,
AAD9F85D295F762B009DBBA2 /* ImpulseResponse.cpp in Sources */,
AAD9F86E2960D4D0009DBBA2 /* wav.cpp in Sources */,
4F3EF91928DE0BA7002972F2 /* cnpy.cpp in Sources */,
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/project.pbxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/project.pbxproj
@@ -391,6 +391,18 @@
4FFBB93520863B0E00DDD0E7 /* vstnoteexpressiontypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F81588E205D50EB00393585 /* vstnoteexpressiontypes.cpp */; };
AA355E2D295B688F0061AA3D /* Colors.h in Headers */ = {isa = PBXBuildFile; fileRef = AA355E2C295B688F0061AA3D /* Colors.h */; };
AA355E2E295B688F0061AA3D /* Colors.h in Headers */ = {isa = PBXBuildFile; fileRef = AA355E2C295B688F0061AA3D /* Colors.h */; };
+ AA9F7C412990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C422990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C432990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C442990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C452990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C462990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C472990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C482990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C492990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C4A2990859B00D99615 /* NoiseGate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA9F7C402990859B00D99615 /* NoiseGate.cpp */; };
+ AA9F7C4E299085D600D99615 /* NoiseGate.h in Headers */ = {isa = PBXBuildFile; fileRef = AA9F7C4D299085D600D99615 /* NoiseGate.h */; };
+ AA9F7C4F299085D600D99615 /* NoiseGate.h in Headers */ = {isa = PBXBuildFile; fileRef = AA9F7C4D299085D600D99615 /* NoiseGate.h */; };
AAD9F83B295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */; };
AAD9F83C295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */; };
AAD9F83D295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */; };
@@ -977,6 +989,8 @@
4FFF72B8214BB71400839091 /* main.rc */ = {isa = PBXFileReference; lastKnownFileType = text; name = main.rc; path = ../resources/main.rc; sourceTree = "<group>"; };
52FBBED30D0CF143001C8B8A /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../config.h; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
AA355E2C295B688F0061AA3D /* Colors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Colors.h; path = ../Colors.h; sourceTree = "<group>"; };
+ AA9F7C402990859B00D99615 /* NoiseGate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoiseGate.cpp; sourceTree = "<group>"; };
+ AA9F7C4D299085D600D99615 /* NoiseGate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NoiseGate.h; sourceTree = "<group>"; };
AAD2484E29542F2800F55DD4 /* APPRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = APPRelease.entitlements; sourceTree = "<group>"; };
AAD2484F2954325200F55DD4 /* AUv3Release.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AUv3Release.entitlements; sourceTree = "<group>"; };
AAD248502954339400F55DD4 /* AUv3AppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AUv3AppRelease.entitlements; sourceTree = "<group>"; };
@@ -1280,6 +1294,8 @@
AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */,
4F3EF8AE28DE03ED002972F2 /* lstm.h */,
4F3EF8B128DE03ED002972F2 /* lstm.cpp */,
+ AA9F7C402990859B00D99615 /* NoiseGate.cpp */,
+ AA9F7C4D299085D600D99615 /* NoiseGate.h */,
4F3EF8B728DE03ED002972F2 /* numpy_util.h */,
4F3EF8B428DE03ED002972F2 /* numpy_util.cpp */,
AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */,
@@ -1919,6 +1935,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ AA9F7C4F299085D600D99615 /* NoiseGate.h in Headers */,
4F3EF8CD28DE03ED002972F2 /* lstm.h in Headers */,
4F4856862773C3B5005BCF8E /* IPlugAUAudioUnit.h in Headers */,
4F3EF8E228DE03ED002972F2 /* dsp.h in Headers */,
@@ -1959,6 +1976,7 @@
4F6369EA20A466470022C370 /* IControl.h in Headers */,
4F03A5D520A4621100EBDFFB /* IGraphicsConstants.h in Headers */,
4FF3205820B2BFAB00269268 /* IPlugPaths.h in Headers */,
+ AA9F7C4E299085D600D99615 /* NoiseGate.h in Headers */,
4F03A5B720A4621100EBDFFB /* IGraphics.h in Headers */,
4F6FD2B022675B6300FC59E6 /* IGraphicsCoreText.h in Headers */,
4F03A5D220A4621100EBDFFB /* IGraphicsPopupMenu.h in Headers */,
@@ -2540,6 +2558,7 @@
4FB1F59020E4B010004157C8 /* IGraphicsMac_view.mm in Sources */,
4F3EF8BC28DE03ED002972F2 /* util.cpp in Sources */,
AAD9F850295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
+ AA9F7C422990859B00D99615 /* NoiseGate.cpp in Sources */,
4F993F7223055C96000313AF /* IPlugProcessor.cpp in Sources */,
4F35DEAE207E5C5A00867D8F /* IPlugPluginBase.cpp in Sources */,
4F6FD2B222675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
@@ -2561,6 +2580,7 @@
4F3EF8E928DE03ED002972F2 /* numpy_util.cpp in Sources */,
4F3EF8F328DE03ED002972F2 /* wavenet.cpp in Sources */,
AAD9F8682960D4BA009DBBA2 /* wav.cpp in Sources */,
+ AA9F7C472990859B00D99615 /* NoiseGate.cpp in Sources */,
4F3EF8DE28DE03ED002972F2 /* lstm.cpp in Sources */,
4F3EF8D428DE03ED002972F2 /* cnpy.cpp in Sources */,
);
@@ -2581,6 +2601,7 @@
4FD52131202A5B9B00A4D22A /* IPlugAU_view_factory.mm in Sources */,
4F993F7423055C96000313AF /* IPlugProcessor.cpp in Sources */,
4F8C10E320BA2796006320CD /* IGraphicsEditorDelegate.cpp in Sources */,
+ AA9F7C442990859B00D99615 /* NoiseGate.cpp in Sources */,
AAD9F852295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
AAD9F8652960D4BA009DBBA2 /* wav.cpp in Sources */,
4F3EF8D128DE03ED002972F2 /* cnpy.cpp in Sources */,
@@ -2628,6 +2649,7 @@
4F7C496D255DDFCB00DF7588 /* IControls.cpp in Sources */,
4F3EE1D2231438D000004786 /* swell-misc.mm in Sources */,
4F3EE1D3231438D000004786 /* swell-wnd.mm in Sources */,
+ AA9F7C4A2990859B00D99615 /* NoiseGate.cpp in Sources */,
4F3EE1D4231438D000004786 /* swell.cpp in Sources */,
4F3EF8FF28DE03ED002972F2 /* get_dsp.cpp in Sources */,
4F3EE1D5231438D000004786 /* IPlugAPP_host.cpp in Sources */,
@@ -2687,6 +2709,7 @@
4F78BE2822E7406D00AD537E /* IPlugParameter.cpp in Sources */,
4F78BE2922E7406D00AD537E /* IPlugTimer.cpp in Sources */,
4F3EF8EA28DE03ED002972F2 /* numpy_util.cpp in Sources */,
+ AA9F7C492990859B00D99615 /* NoiseGate.cpp in Sources */,
4F78BE2A22E7406D00AD537E /* IPlugPaths.mm in Sources */,
4F7C496B255DDFCB00DF7588 /* IPopupMenuControl.cpp in Sources */,
);
@@ -2717,6 +2740,7 @@
4F815995205D51F000393585 /* vstpresetfile.cpp in Sources */,
4F3EF8F928DE03ED002972F2 /* get_dsp.cpp in Sources */,
4F815983205D50EB00393585 /* timer.cpp in Sources */,
+ AA9F7C432990859B00D99615 /* NoiseGate.cpp in Sources */,
4F815989205D50EB00393585 /* funknown.cpp in Sources */,
4FB1F58B20E4B006004157C8 /* IGraphicsMac.mm in Sources */,
4F81598E205D51F000393585 /* vstbus.cpp in Sources */,
@@ -2791,6 +2815,7 @@
4F6FD2B522675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
4FB600281567CB0A0020189A /* IPlugAAX_Describe.cpp in Sources */,
4F3EF8E728DE03ED002972F2 /* numpy_util.cpp in Sources */,
+ AA9F7C452990859B00D99615 /* NoiseGate.cpp in Sources */,
4FB1F58D20E4B007004157C8 /* IGraphicsMac.mm in Sources */,
4F7C4962255DDFC600DF7588 /* IPopupMenuControl.cpp in Sources */,
);
@@ -2809,6 +2834,7 @@
4F6369F120A466470022C370 /* IControl.cpp in Sources */,
4F6FD2B722675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
4F8C10E620BA2796006320CD /* IGraphicsEditorDelegate.cpp in Sources */,
+ AA9F7C482990859B00D99615 /* NoiseGate.cpp in Sources */,
4FC3EFF92086CE5700BD11FA /* parameterchanges.cpp in Sources */,
4FC3EFCE2086C35D00BD11FA /* IPlugPluginBase.cpp in Sources */,
4F7C4965255DDFC800DF7588 /* IPopupMenuControl.cpp in Sources */,
@@ -2846,6 +2872,7 @@
4F7C4955255DDFC300DF7588 /* IControls.cpp in Sources */,
4FD16D4013B635A0001D0217 /* swell-misc.mm in Sources */,
4FD16D4213B635AB001D0217 /* swell-wnd.mm in Sources */,
+ AA9F7C412990859B00D99615 /* NoiseGate.cpp in Sources */,
4FD16D4413B635B2001D0217 /* swell.cpp in Sources */,
4F3EF8F728DE03ED002972F2 /* get_dsp.cpp in Sources */,
4F690CA3203A45C700A4A13E /* IPlugAPP_host.cpp in Sources */,
@@ -2890,6 +2917,7 @@
4F3EF8F228DE03ED002972F2 /* wavenet.cpp in Sources */,
4FFBB90F20863B0E00DDD0E7 /* IPlugParameter.cpp in Sources */,
AAD9F854295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
+ AA9F7C462990859B00D99615 /* NoiseGate.cpp in Sources */,
4FFBB91020863B0E00DDD0E7 /* pluginview.cpp in Sources */,
4FFBB91120863B0E00DDD0E7 /* fstring.cpp in Sources */,
4FFBB91320863B0E00DDD0E7 /* vstpresetfile.cpp in Sources */,
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/xcshareddata/xcschemes/macOS-APP.xcscheme b/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/xcshareddata/xcschemes/macOS-APP.xcscheme
@@ -40,7 +40,7 @@
</Testables>
</TestAction>
<LaunchAction
- buildConfiguration = "Release"
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/xcshareddata/xcschemes/macOS-VST3.xcscheme b/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/xcshareddata/xcschemes/macOS-VST3.xcscheme
@@ -31,7 +31,7 @@
</Testables>
</TestAction>
<LaunchAction
- buildConfiguration = "Debug"
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"