commit 250f7859ba4f7ce92ca9a515643da425088f05b2
parent 3e204dc866677332d456f1f84a18e72b5f840ff5
Author: Steven Atkinson <steven@atkinson.mn>
Date: Mon, 2 Jan 2023 20:46:58 -0600
IR loader (#31)
* Refactor colors to separate header
* Refactor to be ready for second meter. Seems to still work.
* numframes and channels as functions
* Add in input meter control (not upating yet)
* ProcessBlock on inputs
* Update OnReset()
* Transmit data
* Rearrange meters to be vertical on sides of the plugin
* Make knobs for tone stack
* Implement filters
* Tune knob settings
* Skeleton of IR loader
GUI elements added
Refactor DSP to make room for new-style abstract class that owns its output
Refactor filters to conform to new DSP abstraction
IR class started, implementation missing for dummy implementation for now
* IR loader working
WAV reading in
dsp::History
module staging in NAM class
Serialization and Unserialization
OnUIOpen
Treble knob seems to behave strangely.
* Buttons to remove model and IR (need new icon)
* Add wav and ImpulseResponse to VS projects
* Start of resampling
* Square icons
* Load WAV files containing junk
* IR resampling
* Start work on close button SVG. How to add to resources?
* Center close button graphic
* std::floor
* Use close button SVG
* Clamp output, adjust meter limits
* Fix tone stack bug
* Remove hpp remnants
Diffstat:
24 files changed, 1195 insertions(+), 229 deletions(-)
diff --git a/NeuralAmpModeler/NeuralAmpModeler.cpp b/NeuralAmpModeler/NeuralAmpModeler.cpp
@@ -1,3 +1,4 @@
+#include <algorithm> // std::clamp
#include <filesystem>
#include <iostream>
#include <utility>
@@ -67,7 +68,14 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
mStagedDSP(nullptr),
mToneBass(),
mToneMid(),
- mToneTreble()
+ mToneTreble(),
+ mIR(),
+ mIRFileName(),
+ mIRPath(),
+ mFlagRemoveDSP(false),
+ mFlagRemoveIR(false),
+ mDefaultModelString("Select model..."),
+ mDefaultIRString("Select IR...")
{
GetParam(kInputLevel)->InitGain("Input", 0.0, -20.0, 20.0, 0.1);
GetParam(kToneBass)->InitDouble("Bass", 5.0, 0.0, 10.0, 0.1);
@@ -93,12 +101,14 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
pGraphics->EnableMouseOver(true);
auto helpSVG = pGraphics->LoadSVG(HELP_FN);
auto folderSVG = pGraphics->LoadSVG(FOLDER_FN);
+ auto closeButtonSVG = pGraphics->LoadSVG(CLOSE_BUTTON_FN);
pGraphics->LoadFont("Roboto-Regular", ROBOTO_FN);
const IRECT b = pGraphics->GetBounds();
const IRECT mainArea = b.GetPadded(-20);
const auto content = mainArea.GetPadded(-10);
const auto titleLabel = content.GetFromTop(50);
+ // Areas for knobs
const float knobHalfPad = 10.0f;
const float knobPad = 2.0f * knobHalfPad;
const float knobHalfHeight = 70.0f;
@@ -109,7 +119,14 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
IRECT trebleKnobArea = knobs.GetGridCell(0, kToneTreble, 1, kNumParams).GetPadded(-10);
IRECT outputKnobArea =knobs.GetGridCell(0, kOutputLevel, 1, kNumParams).GetPadded(-10);
- const auto modelArea = content.GetFromBottom(30).GetMidHPadded(150);
+ // Areas for model and IR
+ const float fileWidth = 250.0f;
+ const float fileHeight = 30.0f;
+ const float fileSpace = 10.0f;
+ const auto modelArea = content.GetFromBottom(2.0f * fileHeight + fileSpace).GetFromTop(fileHeight).GetMidHPadded(fileWidth);
+ const auto irArea = content.GetFromBottom(fileHeight).GetMidHPadded(fileWidth);
+
+ // Areas for meters
const float meterHalfHeight = 0.5f * 250.0f;
const auto inputMeterArea = inputKnobArea.GetFromLeft(knobHalfPad).GetMidHPadded(knobHalfPad).GetMidVPadded(meterHalfHeight).GetTranslated(-knobPad, 0.0f);
const auto outputMeterArea = outputKnobArea.GetFromRight(knobHalfPad).GetMidHPadded(knobHalfPad).GetMidVPadded(meterHalfHeight).GetTranslated(knobPad, 0.0f);
@@ -153,6 +170,7 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
// Model loader button
auto loadModel = [&, pGraphics](IControl* pCaller) {
+ // TODO start from last directory on second load if possible.
WDL_String dir;
pGraphics->PromptForDirectory(dir, [&](const WDL_String& fileName, const WDL_String& path){
if (path.GetLength())
@@ -160,10 +178,36 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
});
};
- // Tells us what model is loaded
- pGraphics->AttachControl(new IVPanelControl(modelArea, "", style.WithColor(kFG, PluginColors::NAM_1))); // .WithContrast(-0.75)
- pGraphics->AttachControl(new IRolloverSVGButtonControl(modelArea.GetFromLeft(30).GetPadded(-2.f), loadModel, folderSVG));
- pGraphics->AttachControl(new IVUpdateableLabelControl(modelArea.GetReducedFromLeft(30), "Select model...", style.WithDrawFrame(false).WithValueText(style.valueText.WithVAlign(EVAlign::Middle))), kCtrlTagModelName);
+ auto getIRPath = [&, pGraphics](IControl* pCaller) {
+ WDL_String fileName;
+ WDL_String path(this->mIRPath.Get());
+ pGraphics->PromptForFile(fileName, path);
+ if (fileName.GetLength()) {
+ this->mIRPath = path;
+ this->_GetIR(fileName);
+ }
+ };
+
+ // Model-clearing function
+ auto ClearModel = [&, pGraphics](IControl* pCaller) {
+ this->mFlagRemoveDSP = true;
+ };
+ // IR-clearing function
+ auto ClearIR = [&, pGraphics](IControl* pCaller) {
+ this->mFlagRemoveIR = true;
+ };
+
+ // Graphics objects for what model is loaded
+ const float iconWidth = fileHeight; // Square icon
+ pGraphics->AttachControl(new IVPanelControl(modelArea, "", style.WithColor(kFG, PluginColors::NAM_1)));
+ pGraphics->AttachControl(new IRolloverSVGButtonControl(modelArea.GetFromLeft(iconWidth).GetPadded(-2.f), loadModel, folderSVG));
+ pGraphics->AttachControl(new IRolloverSVGButtonControl(modelArea.GetFromRight(iconWidth).GetPadded(-2.f), ClearModel, closeButtonSVG));
+ pGraphics->AttachControl(new IVUpdateableLabelControl(modelArea.GetReducedFromLeft(iconWidth).GetReducedFromRight(iconWidth), this->mDefaultModelString.Get(), style.WithDrawFrame(false).WithValueText(style.valueText.WithVAlign(EVAlign::Middle))), kCtrlTagModelName);
+ // IR
+ pGraphics->AttachControl(new IVPanelControl(irArea, "", style.WithColor(kFG, PluginColors::NAM_1)));
+ pGraphics->AttachControl(new IRolloverSVGButtonControl(irArea.GetFromLeft(iconWidth).GetPadded(-2.f), getIRPath, folderSVG));
+ pGraphics->AttachControl(new IRolloverSVGButtonControl(irArea.GetFromRight(iconWidth).GetPadded(-2.f), ClearIR, closeButtonSVG));
+ pGraphics->AttachControl(new IVUpdateableLabelControl(irArea.GetReducedFromLeft(iconWidth).GetReducedFromRight(iconWidth), this->mDefaultIRString.Get(), style.WithDrawFrame(false).WithValueText(style.valueText.WithVAlign(EVAlign::Middle))), kCtrlTagIRName);
// The knobs
pGraphics->AttachControl(new IVKnobControl(inputKnobArea, kInputLevel, "", style));
@@ -174,7 +218,7 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
// The meters
const float meterMin = -60.0f;
- const float meterMax = 12.0f;
+ const float meterMax = -0.01f;
pGraphics->AttachControl(new IVPeakAvgMeterControl(inputMeterArea, "", style.WithWidgetFrac(0.5)
.WithShowValue(false)
.WithColor(kFG, PluginColors::NAM_2), EDirection::Vertical, {}, 0, meterMin, meterMax, {}), kCtrlTagInputMeter)
@@ -231,17 +275,11 @@ void NeuralAmpModeler::ProcessBlock(iplug::sample** inputs, iplug::sample** outp
const int nChans = this->NOutChansConnected();
this->_PrepareBuffers(nFrames);
this->_ProcessInput(inputs, nFrames);
- if (mStagedDSP != nullptr)
- {
- // Move from staged to active DSP
- mDSP = std::move(mStagedDSP);
- mStagedDSP = nullptr;
- }
+ this->_ApplyDSPStaging();
if (mDSP != nullptr)
{
// TODO remove input / output gains from here.
- const int nChans = this->NOutChansConnected();
const double inputGain = 1.0;
const double outputGain = 1.0;
mDSP->process(this->mInputPointers, this->mOutputPointers, nChans, nFrames, inputGain, outputGain, mDSPParams);
@@ -269,16 +307,26 @@ void NeuralAmpModeler::ProcessBlock(iplug::sample** inputs, iplug::sample** outp
// Define filter parameters
- recursive_linear_filter::LowShelfParams bassParams(sampleRate, bassFrequency, bassQuality, bassGainDB);
- recursive_linear_filter::PeakingParams midParams(sampleRate, midFrequency, midQuality, midGainDB);
- recursive_linear_filter::HighShelfParams trebleParams(sampleRate, trebleFrequency, trebleQuality, trebleGainDB);
+ recursive_linear_filter::BiquadParams bassParams(sampleRate, bassFrequency, bassQuality, bassGainDB);
+ recursive_linear_filter::BiquadParams midParams(sampleRate, midFrequency, midQuality, midGainDB);
+ recursive_linear_filter::BiquadParams trebleParams(sampleRate, trebleFrequency, trebleQuality, trebleGainDB);
// Apply tone stack
- sample** bassPointers = this->mToneBass.Process(this->mOutputPointers, nChans, nFrames, &bassParams);
- sample** midPointers = this->mToneMid.Process(bassPointers, nChans, nFrames, &midParams);
- sample** treblePointers = this->mToneTreble.Process(midPointers, nChans, nFrames, &trebleParams);
+ // Set parameters
+ this->mToneBass.SetParams(bassParams);
+ this->mToneMid.SetParams(midParams);
+ this->mToneTreble.SetParams(trebleParams);
+ const size_t numChannels = (size_t) nChans;
+ const size_t numFrames = (size_t) nFrames;
+ sample** bassPointers = this->mToneBass.Process(this->mOutputPointers, numChannels, numFrames);
+ sample** midPointers = this->mToneMid.Process(bassPointers, numChannels, numFrames);
+ sample** treblePointers = this->mToneTreble.Process(midPointers, numChannels, numFrames);
+
+ sample** irPointers = treblePointers;
+ if (this->mIR != nullptr)
+ irPointers = this->mIR->Process(treblePointers, numChannels, numFrames);
// Let's get outta here
- this->_ProcessOutput(treblePointers, outputs, nFrames);
+ this->_ProcessOutput(irPointers, outputs, nFrames);
// * Output of input leveling (inputs -> mInputPointers),
// * Output of output leveling (mOutputPointers -> outputs)
this->_UpdateMeters(this->mInputPointers, outputs, nFrames);
@@ -288,6 +336,7 @@ bool NeuralAmpModeler::SerializeState(IByteChunk& chunk) const
{
// Model directory (don't serialize the model itself; we'll just load it again when we unserialize)
chunk.PutStr(mModelPath.Get());
+ chunk.PutStr(this->mIRFileName.Get());
return SerializeParams(chunk);
}
@@ -295,10 +344,14 @@ int NeuralAmpModeler::UnserializeState(const IByteChunk& chunk, int startPos)
{
WDL_String dir;
startPos = chunk.GetStr(mModelPath, startPos);
- mDSP = nullptr;
+ startPos = chunk.GetStr(this->mIRFileName, startPos);
+ this->mDSP = nullptr;
+ this->mIR = nullptr;
int retcode = UnserializeParams(chunk, startPos);
if (this->mModelPath.GetLength())
this->_GetDSP(this->mModelPath);
+ if (this->mIRFileName.GetLength())
+ this->_GetIR(this->mIRFileName);
return retcode;
}
@@ -307,10 +360,38 @@ void NeuralAmpModeler::OnUIOpen()
Plugin::OnUIOpen();
if (this->mModelPath.GetLength())
this->_SetModelMsg(this->mModelPath);
+ if (this->mIRFileName.GetLength())
+ this->_SetIRMsg(this->mIRFileName);
}
// Private methods ============================================================
+void NeuralAmpModeler::_ApplyDSPStaging()
+{
+ // Move things from staged to live
+ if (this->mStagedDSP != nullptr)
+ {
+ // Move from staged to active DSP
+ this->mDSP = std::move(this->mStagedDSP);
+ this->mStagedDSP = nullptr;
+ }
+ if (this->mStagedIR != nullptr) {
+ this->mIR = std::move(this->mStagedIR);
+ this->mStagedIR = nullptr;
+ }
+ // Remove marked modules
+ if (this->mFlagRemoveDSP) {
+ this->mDSP = nullptr;
+ this->_UnsetModelMsg();
+ this->mFlagRemoveDSP = false;
+ }
+ if (this->mFlagRemoveIR) {
+ this->mIR = nullptr;
+ this->_UnsetIRMsg();
+ this->mFlagRemoveIR = false;
+ }
+}
+
void NeuralAmpModeler::_FallbackDSP(const int nFrames)
{
const int nChans = this->NOutChansConnected();
@@ -341,6 +422,28 @@ void NeuralAmpModeler::_GetDSP(const WDL_String& modelPath)
}
}
+void NeuralAmpModeler::_GetIR(const WDL_String& irFileName)
+{
+ WDL_String previousIRFileName;
+ try {
+ previousIRFileName = this->mIRFileName;
+ const double sampleRate = this->GetSampleRate();
+ this->mStagedIR = std::make_unique<dsp::ImpulseResponse>(irFileName, sampleRate);
+ this->_SetIRMsg(irFileName);
+ }
+ catch (std::exception& e) {
+ std::stringstream ss;
+ ss << "FAILED to load IR";
+ SendControlMsgFromDelegate(kCtrlTagIRName, 0, int(strlen(ss.str().c_str())), ss.str().c_str());
+ if (this->mStagedIR != nullptr) {
+ this->mStagedIR = nullptr;
+ }
+ this->mIRFileName = previousIRFileName;
+ std::cerr << "Failed to read IR" << std::endl;
+ std::cerr << e.what() << std::endl;
+ }
+}
+
size_t NeuralAmpModeler::_GetBufferNumChannels() const
{
return this->mInputArray.size();
@@ -410,16 +513,40 @@ void NeuralAmpModeler::_ProcessOutput(iplug::sample** inputs, iplug::sample **ou
// Assume _PrepareBuffers() was already called
for (int c=0; c<nChans; c++)
for (int s=0; s<nFrames; s++)
- outputs[c][s] = gain * inputs[c][s];
+ outputs[c][s] = std::clamp(gain * inputs[c][s], -1.0, 1.0);
}
void NeuralAmpModeler::_SetModelMsg(const WDL_String& modelPath)
{
- auto dspPath = std::filesystem::path(modelPath.Get());
- mModelPath = modelPath;
- std::stringstream ss;
- ss << "Loaded " << dspPath.parent_path().filename();
- SendControlMsgFromDelegate(kCtrlTagModelName, 0, int(strlen(ss.str().c_str())), ss.str().c_str());
+ auto dspPath = std::filesystem::path(modelPath.Get());
+ mModelPath = modelPath;
+ std::stringstream ss;
+ ss << "Loaded " << dspPath.parent_path().filename();
+ SendControlMsgFromDelegate(kCtrlTagModelName, 0, int(strlen(ss.str().c_str())), ss.str().c_str());
+}
+
+void NeuralAmpModeler::_SetIRMsg(const WDL_String& irFileName)
+{
+ this->mIRFileName = irFileName;
+ auto dspPath = std::filesystem::path(irFileName.Get());
+ std::stringstream ss;
+ ss << "Loaded " << dspPath.filename();
+ SendControlMsgFromDelegate(kCtrlTagIRName, 0, int(strlen(ss.str().c_str())), ss.str().c_str());
+}
+
+void NeuralAmpModeler::_UnsetModelMsg()
+{
+ this->_UnsetMsg(kCtrlTagModelName, this->mDefaultModelString);
+}
+
+void NeuralAmpModeler::_UnsetIRMsg()
+{
+ this->_UnsetMsg(kCtrlTagIRName, this->mDefaultIRString);
+}
+
+void NeuralAmpModeler::_UnsetMsg(const int tag, const WDL_String &msg)
+{
+ SendControlMsgFromDelegate(tag, 0, int(strlen(msg.Get())), msg.Get());
}
void NeuralAmpModeler::_UpdateMeters(sample** inputPointer, sample** outputPointer, const int nFrames)
diff --git a/NeuralAmpModeler/NeuralAmpModeler.h b/NeuralAmpModeler/NeuralAmpModeler.h
@@ -4,6 +4,7 @@
#include "dsp.h"
#include "choc_ReenableAllWarnings.h"
#include "dsp/RecursiveLinearFilter.h"
+#include "dsp/ImpulseResponse.h"
#include "IPlug_include_in_plug_hdr.h"
@@ -24,6 +25,7 @@ enum EParams
enum ECtrlTags
{
kCtrlTagModelName = 0,
+ kCtrlTagIRName,
kCtrlTagInputMeter,
kCtrlTagOutputMeter,
kNumCtrlTags
@@ -45,18 +47,25 @@ public:
this->mOutputSender.TransmitData(*this);
}
- bool SerializeState(IByteChunk& chunk) const override;
- int UnserializeState(const IByteChunk& chunk, int startPos) override;
+ bool SerializeState(iplug::IByteChunk& chunk) const override;
+ int UnserializeState(const iplug::IByteChunk& chunk, int startPos) override;
void OnUIOpen() override;
private:
+ // Moves DSP modules from staging area to the main area.
+ // Also deletes DSP modules that are flagged for removal.
+ // Exists so that we don't try to use a DSP module that's only
+ // partially-instantiated.
+ void _ApplyDSPStaging();
// Fallback that just copies inputs to outputs if mDSP doesn't hold a model.
void _FallbackDSP(const int nFrames);
- // Gets a new DSP object and stores it to mStagedDSP
- void _GetDSP(const WDL_String& dspPath);
// Sizes based on mInputArray
size_t _GetBufferNumChannels() const;
size_t _GetBufferNumFrames() const;
+ // Gets a new DSP object and stores it to mStagedDSP
+ void _GetDSP(const WDL_String& dspPath);
+ // Gets the IR and stores to mStagedIR
+ void _GetIR(const WDL_String& irFileName);
// Update the message about which model is loaded.
void _SetModelMsg(const WDL_String& dspPath);
bool _HaveModel() const {
@@ -70,31 +79,46 @@ private:
void _ProcessInput(iplug::sample** inputs, const int nFrames);
// Copy the output to the output buffer, applying output level.
void _ProcessOutput(iplug::sample** inputs, iplug::sample** outputs, const int nFrames);
+ // Update the text in the IR area to say what's loaded.
+ void _SetIRMsg(const WDL_String& modelPath);
+ void _UnsetModelMsg();
+ void _UnsetIRMsg();
+ void _UnsetMsg(const int tag, const WDL_String& msg);
// Update level meters
// Called within ProcessBlock().
// Assume _ProcessInput() and _ProcessOutput() were run immediately before.
- void _UpdateMeters(sample** inputPointer, sample** outputPointer, const int nFrames);
+ void _UpdateMeters(iplug::sample** inputPointer, iplug::sample** outputPointer, const int nFrames);
// Input arrays
- std::vector<std::vector<sample>> mInputArray;
+ std::vector<std::vector<iplug::sample>> mInputArray;
// Output arrays
- std::vector<std::vector<sample>> mOutputArray;
+ std::vector<std::vector<iplug::sample>> mOutputArray;
// Pointer versions
- sample** mInputPointers;
- sample** mOutputPointers;
+ iplug::sample** mInputPointers;
+ iplug::sample** mOutputPointers;
- // The DSP actually being used:
+ // The DSPs actually being used:
std::unique_ptr<DSP> mDSP;
+ std::unique_ptr<dsp::ImpulseResponse> mIR;
// Manages switching what DSP is being used.
std::unique_ptr<DSP> mStagedDSP;
+ std::unique_ptr<dsp::ImpulseResponse> mStagedIR;
+ // Flags to take away the modules at a safe time.
+ bool mFlagRemoveDSP;
+ bool mFlagRemoveIR;
+ const WDL_String mDefaultModelString;
+ const WDL_String mDefaultIRString;
// Tone stack modules
- // TODO low shelf, peaking, high shelf
recursive_linear_filter::LowShelf mToneBass;
recursive_linear_filter::Peaking mToneMid;
recursive_linear_filter::HighShelf mToneTreble;
+ // Paths to model and IR
WDL_String mModelPath;
+ WDL_String mIRFileName;
+ // Directory containing the file (for better file browsing on second load-in)
+ WDL_String mIRPath; // Do we need this, or can we manipualte mITFileName (".dirname")
std::unordered_map<std::string, double> mDSPParams = {
{ "Input", 0.0 },
diff --git a/NeuralAmpModeler/config.h b/NeuralAmpModeler/config.h
@@ -24,7 +24,7 @@
#define PLUG_DOES_STATE_CHUNKS 0
#define PLUG_HAS_UI 1
#define PLUG_WIDTH 600
-#define PLUG_HEIGHT 300
+#define PLUG_HEIGHT 400
#define PLUG_FPS 60
#define PLUG_SHARED_RESOURCES 0
#define PLUG_HOST_RESIZE 0
@@ -57,5 +57,6 @@
#define ROBOTO_FN "Roboto-Regular.ttf"
#define HELP_FN "help.svg"
#define FOLDER_FN "folder.svg"
+#define CLOSE_BUTTON_FN "close-button.svg"
#define TOLEX_FN "tolex.jpeg"
#define TOLEX2X_FN "tolex@2x.jpeg"
diff --git a/NeuralAmpModeler/dsp/ImpulseResponse.cpp b/NeuralAmpModeler/dsp/ImpulseResponse.cpp
@@ -0,0 +1,67 @@
+//
+// ImpulseResponse.cpp
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 12/30/22.
+//
+
+#include "Resample.h"
+#include "wav.h"
+
+#include "ImpulseResponse.h"
+
+dsp::ImpulseResponse::ImpulseResponse(const WDL_String& fileName,
+ const double sampleRate)
+{
+ // Try to load the WAV
+ if (dsp::wav::Load(fileName, this->mRawAudio, this->mRawAudioSampleRate) != dsp::wav::RET_SUCCESS) {
+ std::stringstream ss;
+ ss << "Failed to load IR at " << fileName.Get() << std::endl;
+ throw std::runtime_error(ss.str());
+ }
+ // Set the weights based on the raw audio.
+ this->_SetWeights(sampleRate);
+}
+
+iplug::sample** dsp::ImpulseResponse::Process(iplug::sample** inputs,
+ const size_t numChannels,
+ const size_t numFrames)
+{
+ this->_PrepareBuffers(numChannels, numFrames);
+ this->_UpdateHistory(inputs, numChannels, numFrames);
+
+ for (size_t i=0, j=this->mHistoryIndex - this->mHistoryRequired; i<numFrames; i++, j++) {
+ auto input = Eigen::Map<const Eigen::VectorXf>(&this->mHistory[j], this->mHistoryRequired+1);
+ this->mOutputs[0][i] = (double) this->mWeight.dot(input);
+ }
+ // Copy out for more-than-mono.
+ for (size_t c=1; c<numChannels; c++)
+ for (size_t i=0; i<numFrames; i++)
+ this->mOutputs[c][i] = this->mOutputs[0][i];
+
+ this->_AdvanceHistoryIndex(numFrames);
+ return this->_GetPointers();
+}
+
+void dsp::ImpulseResponse::_SetWeights(const double sampleRate)
+{
+ if (this->mRawAudioSampleRate == sampleRate) {
+ this->mResampled.resize(this->mRawAudio.size());
+ memcpy(this->mResampled.data(), this->mRawAudio.data(), this->mResampled.size());
+ }
+ else {
+ // Cubic resampling
+ std::vector<float> padded;
+ padded.resize(this->mRawAudio.size() + 2);
+ padded[0] = 0.0f;
+ padded[padded.size()-1] = 0.0f;
+ memcpy(padded.data() + 1, this->mRawAudio.data(), this->mRawAudio.size());
+ dsp::ResampleCubic<float>(padded, this->mRawAudioSampleRate, sampleRate, 0.0, this->mResampled);
+ }
+ // Simple implementation w/ no resample...
+ const size_t irLength = std::min(this->mResampled.size(), this->mMaxLength);
+ this->mWeight.resize(irLength);
+ for (size_t i=0, j=irLength-1; i<irLength; i++,j--)
+ this->mWeight[j] = this->mResampled[i];
+ this->mHistoryRequired = irLength - 1;
+}
diff --git a/NeuralAmpModeler/dsp/ImpulseResponse.h b/NeuralAmpModeler/dsp/ImpulseResponse.h
@@ -0,0 +1,43 @@
+//
+// ImpulseResponse.h
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 12/30/22.
+//
+// Impulse response processing
+
+#ifndef ImpulseResponse_h
+#define ImpulseResponse_h
+
+#include <filesystem>
+
+#include <Eigen/Dense>
+
+#include "wdlstring.h" // WDL_String
+#include "IPlugConstants.h" // sample
+#include "dsp.h"
+
+namespace dsp {
+ class ImpulseResponse : public History {
+ public:
+ ImpulseResponse(const WDL_String& fileName, const double sampleRate);
+ iplug::sample** Process(iplug::sample** inputs,
+ const size_t numChannels,
+ const size_t numFrames) override;
+ private:
+ // Set the weights, given that the plugin is running at the provided sample rate.
+ void _SetWeights(const double sampleRate);
+
+ // Keep a copy of the raw audio that was loaded so that it can be resampled
+ std::vector<float> mRawAudio;
+ double mRawAudioSampleRate;
+ // Resampled to the required sample rate.
+ std::vector<float> mResampled;
+
+ const size_t mMaxLength = 8192;
+ // The weights
+ Eigen::VectorXf mWeight;
+ };
+};
+
+#endif /* ImpulseResponse_h */
diff --git a/NeuralAmpModeler/dsp/RecursiveLinearFilter.cpp b/NeuralAmpModeler/dsp/RecursiveLinearFilter.cpp
@@ -7,15 +7,13 @@
// See: https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
#include <algorithm> // std::fill
-#include <cmath> // pow, sin
#include <stdexcept>
#include "RecursiveLinearFilter.h"
recursive_linear_filter::Base::Base(const size_t inputDegree,
const size_t outputDegree) :
-mOutputPointers(nullptr),
-mOutputPointersSize(0),
+dsp::DSP(),
mInputStart(inputDegree), // 1 is subtracted before first use
mOutputStart(outputDegree)
{
@@ -24,12 +22,10 @@ mOutputStart(outputDegree)
}
iplug::sample** recursive_linear_filter::Base::Process(iplug::sample** inputs,
- const int numChannels,
- const int numFrames,
- const Params *params)
+ const size_t numChannels,
+ const size_t numFrames)
{
this->_PrepareBuffers(numChannels, numFrames);
- params->SetCoefficients(this->mInputCoefficients, this->mOutputCoefficients);
long inputStart=0;
long outputStart=0;
// Degree = longest history
@@ -65,55 +61,51 @@ iplug::sample** recursive_linear_filter::Base::Process(iplug::sample** inputs,
}
this->mInputStart = inputStart;
this->mOutputStart = outputStart;
- return this->GetPointers();
+ return this->_GetPointers();
}
-iplug::sample** recursive_linear_filter::Base::GetPointers()
+void recursive_linear_filter::Base::_PrepareBuffers(const size_t numChannels, const size_t numFrames)
{
- for (auto c=0; c<this->_GetNumChannels(); c++)
- this->mOutputPointers[c] = this->mOutputs[c].data();
- return this->mOutputPointers;
-}
-
-
-void recursive_linear_filter::Base::_PrepareBuffers(const int numChannels, const int numFrames)
-{
- if (this->_GetNumChannels() != numChannels) {
+ // Check for new channel count *before* parent class ensures they match!
+ const bool newChannels = this->_GetNumChannels() != numChannels;
+ // Parent implementation takes care of mOutputs and mOutputPointers
+ this->dsp::DSP::_PrepareBuffers(numChannels, numFrames);
+ if (newChannels) {
this->mInputHistory.resize(numChannels);
this->mOutputHistory.resize(numChannels);
- this->mOutputs.resize(numChannels);
const size_t inputDegree = this->_GetInputDegree();
const size_t outputDegree = this->_GetOutputDegree();
for (auto c=0; c<numChannels; c++) {
this->mInputHistory[c].resize(inputDegree);
this->mOutputHistory[c].resize(outputDegree);
- this->mOutputs[c].resize(numFrames);
std::fill(this->mInputHistory[c].begin(), this->mInputHistory[c].end(), 0.0);
std::fill(this->mOutputHistory[c].begin(), this->mOutputHistory[c].end(), 0.0);
}
- this->_ResizePointers((size_t) numChannels);
}
}
-void recursive_linear_filter::Base::_ResizePointers(const size_t numChannels)
+void recursive_linear_filter::Biquad::_AssignCoefficients(const double a0,
+ const double a1,
+ const double a2,
+ const double b0,
+ const double b1,
+ const double b2)
{
- if (this->mOutputPointersSize == numChannels)
- return;
- if (this->mOutputPointers != nullptr)
- delete[] this->mOutputPointers;
- this->mOutputPointers = new iplug::sample*[numChannels];
- if (this->mOutputPointers == nullptr)
- throw std::runtime_error("Failed to allocate pointer to output buffer!\n");
- this->mOutputPointersSize = numChannels;
+ this->mInputCoefficients[0] = b0 / a0;
+ this->mInputCoefficients[1] = b1 / a0;
+ this->mInputCoefficients[2] = b2 / a0;
+ // this->mOutputCoefficients[0] = 0.0; // Always
+ // Sign flip due so we add during main loop (cf Eq. (4))
+ this->mOutputCoefficients[1] = -a1 / a0;
+ this->mOutputCoefficients[2] = -a2 / a0;
}
-void recursive_linear_filter::LowShelfParams::SetCoefficients(std::vector<double> &inputCoefficients,
- std::vector<double> &outputCoefficients) const
+void recursive_linear_filter::LowShelf::SetParams(const recursive_linear_filter::BiquadParams ¶ms)
{
- const double a = pow(10.0, this->mGainDB / 40.0);
- const double omega_0 = 2.0 * MATH_PI * this->mFrequency / this->mSampleRate;
- const double alpha = sin(omega_0) / (2.0 * this->mQuality);
- const double cosw = cos(omega_0);
+ const double a = params.GetA();
+ const double omega_0 = params.GetOmega0();
+ const double alpha = params.GetAlpha(omega_0);
+ const double cosw = params.GetCosW(omega_0);
const double ap = a + 1.0;
const double am = a - 1.0;
@@ -126,22 +118,15 @@ void recursive_linear_filter::LowShelfParams::SetCoefficients(std::vector<double
const double a1 = -2.0 * (am + ap * cosw);
const double a2 = ap + am * cosw - roota2alpha;
- inputCoefficients[0] = b0 / a0;
- inputCoefficients[1] = b1 / a0;
- inputCoefficients[2] = b2 / a0;
- // outputCoefficients[0] = 0.0; // Always
- // Sign flip due so we add during main loop (cf Eq. (4))
- outputCoefficients[1] = -a1 / a0;
- outputCoefficients[2] = -a2 / a0;
+ this->_AssignCoefficients(a0, a1, a2, b0, b1, b2);
}
-void recursive_linear_filter::PeakingParams::SetCoefficients(std::vector<double> &inputCoefficients,
- std::vector<double> &outputCoefficients) const
+void recursive_linear_filter::Peaking::SetParams(const recursive_linear_filter::BiquadParams ¶ms)
{
- const double a = pow(10.0, this->mGainDB / 40.0);
- const double omega_0 = 2.0 * MATH_PI * this->mFrequency / this->mSampleRate;
- const double alpha = sin(omega_0) / (2.0 * this->mQuality);
- const double cosw = cos(omega_0);
+ const double a = params.GetA();
+ const double omega_0 = params.GetOmega0();
+ const double alpha = params.GetAlpha(omega_0);
+ const double cosw = params.GetCosW(omega_0);
const double b0 = 1.0 + alpha * a;
const double b1 = -2.0 * cosw;
@@ -150,24 +135,17 @@ void recursive_linear_filter::PeakingParams::SetCoefficients(std::vector<double>
const double a1 = -2.0 * cosw;
const double a2 = 1.0 - alpha / a;
- inputCoefficients[0] = b0 / a0;
- inputCoefficients[1] = b1 / a0;
- inputCoefficients[2] = b2 / a0;
- // outputCoefficients[0] = 0.0; // Always
- // Sign flip due so we add during main loop (cf Eq. (4))
- outputCoefficients[1] = -a1 / a0;
- outputCoefficients[2] = -a2 / a0;
+ this->_AssignCoefficients(a0, a1, a2, b0, b1, b2);
}
-void recursive_linear_filter::HighShelfParams::SetCoefficients(std::vector<double> &inputCoefficients,
- std::vector<double> &outputCoefficients) const
+void recursive_linear_filter::HighShelf::SetParams(const recursive_linear_filter::BiquadParams ¶ms)
{
- const double a = pow(10.0, this->mGainDB / 40.0);
- const double omega_0 = 2.0 * MATH_PI * this->mFrequency / this->mSampleRate;
- const double alpha = sin(omega_0) / (2.0 * this->mQuality);
- const double cosw = cos(omega_0);
- const double roota2alpha = 2.0 * sqrt(a) * alpha;
+ const double a = params.GetA();
+ const double omega_0 = params.GetOmega0();
+ const double alpha = params.GetAlpha(omega_0);
+ const double cosw = params.GetCosW(omega_0);
+ const double roota2alpha = 2.0 * sqrt(a) * alpha;
const double ap = a + 1.0;
const double am = a - 1.0;
@@ -178,12 +156,5 @@ void recursive_linear_filter::HighShelfParams::SetCoefficients(std::vector<doubl
const double a1 = 2.0 * (am - ap * cosw);
const double a2 = ap - am * cosw - roota2alpha;
-
- inputCoefficients[0] = b0 / a0;
- inputCoefficients[1] = b1 / a0;
- inputCoefficients[2] = b2 / a0;
- // outputCoefficients[0] = 0.0; // Always
- // Sign flip due so we add during main loop (cf Eq. (4))
- outputCoefficients[1] = -a1 / a0;
- outputCoefficients[2] = -a2 / a0;
+ this->_AssignCoefficients(a0, a1, a2, b0, b1, b2);
}
diff --git a/NeuralAmpModeler/dsp/RecursiveLinearFilter.h b/NeuralAmpModeler/dsp/RecursiveLinearFilter.h
@@ -9,53 +9,28 @@
#ifndef RecursiveLinearFilter_h
#define RecursiveLinearFilter_h
+#include <cmath> // pow, sin
#include <vector>
#include "IPlugConstants.h" // sample
+#include "dsp.h"
#define MATH_PI 3.14159265358979323846
// TODO refactor base DSP into a common abstraction.
namespace recursive_linear_filter {
- class Params{
- public:
- // Based on the parameters in this object, set the coefficients in the
- // provided arrays for the filter.
- virtual void SetCoefficients(std::vector<double>& inputCoefficients,
- std::vector<double>& outputCoefficients) const=0;
- };
-
- class Base { // TODO: inherit from DSP
+ class Base : public dsp::DSP {
public:
Base(const size_t inputDegree, const size_t outputDegree);
-
- // Compute the DSP, saving to mOutputs, owned by this instance.
- // Return a pointer-to-pointers of the output array
- // TODO return const
iplug::sample** Process(iplug::sample** inputs,
- const int numChannels,
- const int numFrames,
- const Params *params);
-
- // Return a pointer-to-pointers for the DSP's output buffers (all channels)
- // Assumes that ._PrepareBuffers() was called recently enough.
- iplug::sample** GetPointers();
+ const size_t numChannels,
+ const size_t numFrames) override;
protected:
- size_t _GetNumChannels() const {return this->mInputHistory.size();};
+ // Methods
size_t _GetInputDegree() const {return this->mInputCoefficients.size();};
size_t _GetOutputDegree() const {return this->mOutputCoefficients.size();};
- void _PrepareBuffers(const int numChannels, const int numFrames);
- // Resizes the pointer-to-pointers for the vector-of-vectors.
- void _ResizePointers(const size_t numChannels);
-
- // Where the output will get written...
- // Indexing is [channel][sample]
- std::vector<std::vector<iplug::sample>> mOutputs;
- // ...And a pointer to it.
- // Only the first level is allocated; the second level are duplicate
- // pointers to the data that are primarily owned by the vectors.
- iplug::sample** mOutputPointers;
- size_t mOutputPointersSize; // Keep track of its size.
+ // Additionally prepares mInputHistory and mOutputHistory.
+ void _PrepareBuffers(const size_t numChannels, const size_t numFrames) override;
// Coefficients for the DSP filter
std::vector<double> mInputCoefficients;
@@ -72,13 +47,10 @@ namespace recursive_linear_filter {
long mOutputStart;
};
- class LevelParams : public Params {
+ class LevelParams : public dsp::Params {
public:
LevelParams(const double gain) : Params(), mGain(gain) {};
- void SetCoefficients(std::vector<double>& inputCoefficients,
- std::vector<double>& outputCoefficients) const {
- inputCoefficients[0] = this->mGain;
- };
+ double GetGain() const {return this->mGain;};
private:
// The gain (multiplicative, i.e. not dB)
double mGain;
@@ -87,75 +59,66 @@ namespace recursive_linear_filter {
class Level : public Base {
public:
Level() : Base(1, 0) {};
+ // Invalid usage: require a pointer to recursive_linear_filter::Params so
+ // that SetCoefficients() is defined.
+ void SetParams(const LevelParams ¶ms) {
+ this->mInputCoefficients[0] = params.GetGain();
+ };;
};
- class LowShelfParams : public Params {
+ // The same 3 params (frequency, quality, gain) describe a bunch of filters.
+ // (Low shelf, high shelf, peaking)
+ class BiquadParams : public dsp::Params {
public:
- LowShelfParams(const double sampleRate, const double frequency, const double quality, const double gainDB) :
- Params(),
- mSampleRate(sampleRate),
+ BiquadParams(const double sampleRate,
+ const double frequency,
+ const double quality,
+ const double gainDB) :
+ dsp::Params(),
mFrequency(frequency),
+ mGainDB(gainDB),
mQuality(quality),
- mGainDB(gainDB) {};
+ mSampleRate(sampleRate) {};
- void SetCoefficients(std::vector<double>& inputCoefficients,
- std::vector<double>& outputCoefficients) const;
+ // Parameters defined in
+ // https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
+ double GetA() const {return pow(10.0, this->mGainDB / 40.0);};
+ double GetOmega0() const {return 2.0 * MATH_PI * this->mFrequency / this->mSampleRate;};
+ double GetAlpha(const double omega_0) const {return sin(omega_0) / (2.0 * this->mQuality);};
+ double GetCosW(const double omega_0) const {return cos(omega_0);};
private:
- double mSampleRate;
double mFrequency;
- double mQuality;
double mGainDB;
+ double mQuality;
+ double mSampleRate;
};
- class LowShelf : public Base {
- public:
- LowShelf() : Base(3,3) {};
- };
-
- class PeakingParams : public Params {
+ class Biquad : public Base {
public:
- PeakingParams(const double sampleRate, const double frequency, const double quality, const double gainDB) :
- Params(),
- mSampleRate(sampleRate),
- mFrequency(frequency),
- mQuality(quality),
- mGainDB(gainDB) {};
-
- void SetCoefficients(std::vector<double>& inputCoefficients,
- std::vector<double>& outputCoefficients) const;
- private:
- double mSampleRate;
- double mFrequency;
- double mQuality;
- double mGainDB;
+ Biquad() : Base(3, 3) {};
+ virtual void SetParams(const BiquadParams ¶ms) = 0;
+ protected:
+ void _AssignCoefficients(const double a0,
+ const double a1,
+ const double a2,
+ const double b0,
+ const double b1,
+ const double b2);
};
- class Peaking : public Base {
+ class LowShelf : public Biquad {
public:
- Peaking() : Base(3,3) {};
+ void SetParams(const BiquadParams ¶ms) override;
};
- class HighShelfParams : public Params {
+ class Peaking : public Biquad {
public:
- HighShelfParams(const double sampleRate, const double frequency, const double quality, const double gainDB) :
- Params(),
- mSampleRate(sampleRate),
- mFrequency(frequency),
- mQuality(quality),
- mGainDB(gainDB) {};
-
- void SetCoefficients(std::vector<double>& inputCoefficients,
- std::vector<double>& outputCoefficients) const;
- private:
- double mSampleRate;
- double mFrequency;
- double mQuality;
- double mGainDB;
+ void SetParams(const BiquadParams ¶ms) override;
};
- class HighShelf : public Base {
+ class HighShelf : public Biquad {
public:
- HighShelf() : Base(3,3) {};
+ void SetParams(const BiquadParams ¶ms) override;
};
};
diff --git a/NeuralAmpModeler/dsp/Resample.h b/NeuralAmpModeler/dsp/Resample.h
@@ -0,0 +1,81 @@
+//
+// Resample.h
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 1/2/23.
+//
+
+#ifndef Resample_h
+#define Resample_h
+
+#include <cmath>
+#include <vector>
+#include <stdexcept>
+
+namespace dsp {
+ // Resample a provided vector in inputs to outputs.
+ // Creates an array of the required length to fill all points from the SECOND
+ // input to the SECOND-TO-LAST input point, exclusive.
+ // (Seconds bc cubic. and ew want to only interpoalte between points 2 and
+ // 3.)
+ // tOutputStart: location of first output point relative to the second input
+ // point (should be >=0.0)
+ template<typename T>
+ void ResampleCubic(const std::vector<T>& inputs,
+ const double originalSampleRate,
+ const double desiredSampleRate,
+ const double tOutputStart,
+ std::vector<T>& outputs);
+ // Interpolate the 4 provided equispaced points to x in [-1,2]
+ template<typename T>
+ T _CubicInterpolation(T p[4], T x) {
+ return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
+ };
+};
+
+template <typename T>
+void dsp::ResampleCubic(const std::vector<T>& inputs,
+ const double originalSampleRate,
+ const double desiredSampleRate,
+ const double tOutputStart,
+ std::vector<T>& outputs)
+{
+ if (tOutputStart < 0.0)
+ throw std::runtime_error("Starting time must be non-negative");
+
+ // Time increment for each sample in the original audio file
+ const double timeIncrement = 1.0 / originalSampleRate;
+
+ // Time increment for each sample in the resampled audio file
+ const double resampledTimeIncrement = 1.0 / desiredSampleRate;
+
+ // Current time
+ double time = timeIncrement + tOutputStart;
+
+ const double endTimeOriginal = (inputs.size() - 1) * timeIncrement;
+ while (time < endTimeOriginal) {
+ // Find the index of the sample in the original audio file that is just before the current time in the resampled audio file
+ int index = (long) std::floor(time / timeIncrement);
+
+ // Calculate the time difference between the current time in the resampled audio file and the sample in the original audio file
+ double timeDifference = time - index * timeIncrement;
+
+ // Get the four surrounding samples in the original audio file for cubic interpolation
+ double p[4];
+ p[0] = (index == 0) ? inputs[0] : inputs[index - 1];
+ p[1] = inputs[index];
+ p[2] = (index == inputs.size() - 1) ? inputs[inputs.size() - 1] : inputs[index + 1];
+ p[3] = (index == inputs.size() - 2) ? inputs[inputs.size() - 1] : inputs[index + 2];
+
+ // Use cubic interpolation to estimate the value of the audio signal at the current time in the resampled audio file
+ T resampledValue = dsp::_CubicInterpolation(p, timeDifference / timeIncrement);
+
+ // Add the estimated value to the resampled audio file
+ outputs.push_back(resampledValue);
+
+ // Update the current time in the resampled audio file
+ time += resampledTimeIncrement;
+ }
+}
+
+#endif /* Resample_h */
diff --git a/NeuralAmpModeler/dsp/dsp.cpp b/NeuralAmpModeler/dsp/dsp.cpp
@@ -18,8 +18,8 @@ DSP::DSP()
{ this->_stale_params = true; }
void DSP::process(
- sample** inputs,
- sample** outputs,
+ iplug::sample** inputs,
+ iplug::sample** outputs,
const int num_channels,
const int num_frames,
const double input_gain,
@@ -52,7 +52,7 @@ void DSP::_get_params_(const std::unordered_map<std::string, double>& input_para
}
}
-void DSP::_apply_input_level_(sample** inputs, const int num_channels, const int num_frames, const double gain)
+void DSP::_apply_input_level_(iplug::sample** inputs, const int num_channels, const int num_frames, const double gain)
{
// Must match exactly; we're going to use the size of _input_post_gain later for num_frames.
if (this->_input_post_gain.size() != num_frames)
@@ -76,7 +76,7 @@ void DSP::_process_core_()
this->_core_dsp_output[i] = this->_input_post_gain[i];
}
-void DSP::_apply_output_level_(sample** outputs, const int num_channels, const int num_frames, const double gain)
+void DSP::_apply_output_level_(iplug::sample** outputs, const int num_channels, const int num_frames, const double gain)
{
for (int c = 0; c < num_channels; c++)
for (int s = 0; s < num_frames; s++)
@@ -567,3 +567,94 @@ void convnet::ConvNet::_reset_anti_pop_()
receptive_field += this->_blocks[i].conv.get_dilation();
this->_anti_pop_countdown = -receptive_field;
}
+
+// ============================================================================
+// Implementation of Version 2 interface
+
+dsp::DSP::DSP() :
+mOutputPointers(nullptr),
+mOutputPointersSize(0)
+{}
+
+dsp::DSP::~DSP()
+{
+ if (this->mOutputPointers != nullptr)
+ delete[] this->mOutputPointers;
+};
+
+iplug::sample** dsp::DSP::_GetPointers()
+{
+ for (auto c=0; c<this->_GetNumChannels(); c++)
+ this->mOutputPointers[c] = this->mOutputs[c].data();
+ return this->mOutputPointers;
+}
+
+
+void dsp::DSP::_PrepareBuffers(const size_t numChannels, const size_t numFrames)
+{
+ if (this->_GetNumChannels() != numChannels) {
+ this->mOutputs.resize(numChannels);
+ for (auto c=0; c<numChannels; c++) {
+ this->mOutputs[c].resize(numFrames);
+ }
+ this->_ResizePointers(numChannels);
+ }
+}
+
+void dsp::DSP::_ResizePointers(const size_t numChannels)
+{
+ if (this->mOutputPointersSize == numChannels)
+ return;
+ if (this->mOutputPointers != nullptr)
+ delete[] this->mOutputPointers;
+ this->mOutputPointers = new iplug::sample*[numChannels];
+ if (this->mOutputPointers == nullptr)
+ throw std::runtime_error("Failed to allocate pointer to output buffer!\n");
+ this->mOutputPointersSize = numChannels;
+}
+
+dsp::History::History() :
+DSP(),
+mHistoryRequired(0),
+mHistoryIndex(0)
+{
+}
+
+void dsp::History::_AdvanceHistoryIndex(const size_t bufferSize)
+{
+ this->mHistoryIndex += bufferSize;
+}
+
+void dsp::History::_EnsureHistorySize(const size_t bufferSize)
+{
+ const size_t repeatSize = std::max(bufferSize, this->mHistoryRequired);
+ const size_t requiredHistoryArraySize = 10 * repeatSize; // Just so we don't spend too much time copying back.
+ if (this->mHistory.size() < requiredHistoryArraySize) {
+ this->mHistory.resize(requiredHistoryArraySize);
+ std::fill(this->mHistory.begin(), this->mHistory.end(), 0.0f);
+ this->mHistoryIndex = this->mHistoryRequired; // Guaranteed to be less than requiredHistoryArraySize
+ }
+}
+
+void dsp::History::_RewindHistory()
+{
+ // TODO memcpy? Should be fine w/ history array being >2x the history length.
+ for (size_t i=0, j=this->mHistoryIndex - this->mHistoryRequired; i<this->mHistoryRequired; i++, j++)
+ this->mHistory[i] = this->mHistory[j];
+ this->mHistoryIndex = this->mHistoryRequired;
+}
+
+void dsp::History::_UpdateHistory(iplug::sample **inputs,
+ const size_t numChannels,
+ const size_t numFrames)
+{
+ this->_EnsureHistorySize(numFrames);
+ if (numChannels < 1)
+ throw std::runtime_error("Zero channels?");
+ if (this->mHistoryIndex + numFrames >= this->mHistory.size())
+ this->_RewindHistory();
+ // Grabs channel 1, drops hannel 2.
+ for (size_t i=0, j=this->mHistoryIndex; i<numFrames; i++, j++)
+ // Convert down to float here.
+ this->mHistory[j] = (float) inputs[0][i];
+}
diff --git a/NeuralAmpModeler/dsp/dsp.h b/NeuralAmpModeler/dsp/dsp.h
@@ -23,9 +23,6 @@ enum EArchitectures
kNumModels
};
-// HACK
-using namespace iplug;
-
// Class for providing params from the plugin to the DSP module
// For now, we'll work with doubles. Later, we'll add other types.
class DSPParam
@@ -50,8 +47,8 @@ public:
// overridden in subclasses).
// 4. The output level is applied and the result stored to `output`.
virtual void process(
- sample** inputs,
- sample** outputs,
+ iplug::sample** inputs,
+ iplug::sample** outputs,
const int num_channels,
const int num_frames,
const double input_gain,
@@ -85,7 +82,10 @@ protected:
// Apply the input gain
// Result populates this->_input_post_gain
- void _apply_input_level_(sample** inputs, const int num_channels, const int num_frames, const double gain);
+ void _apply_input_level_(iplug::sample** inputs,
+ const int num_channels,
+ const int num_frames,
+ const double gain);
// i.e. ensure the size is correct.
void _ensure_core_dsp_output_ready_();
@@ -96,7 +96,10 @@ protected:
virtual void _process_core_();
// Copy this->_core_dsp_output to output and apply the output volume
- void _apply_output_level_(sample** outputs, const int num_channels, const int num_frames, const double gain);
+ void _apply_output_level_(iplug::sample** outputs,
+ const int num_channels,
+ const int num_frames,
+ const double gain);
};
// Class where an input buffer is kept so that long-time effects can be captured.
@@ -369,4 +372,89 @@ std::unique_ptr<DSP> get_dsp(const std::filesystem::path dirname);
// Hard-coded model:
std::unique_ptr<DSP> get_hard_dsp();
+// Version 2 DSP abstraction ==================================================
+
+namespace dsp {
+ class Params {};
+
+ class DSP {
+ public:
+ DSP();
+ ~DSP();
+ // The main interface for processing audio.
+ // The incoming audio is given as a raw pointer-to-pointers.
+ // The indexing is [channel][frame].
+ // The output shall be a pointer-to-pointers of matching size.
+ // This object instance will own the data referenced by the pointers and be
+ // responsible for its allocation and deallocation.
+ virtual iplug::sample** Process(iplug::sample** inputs, const size_t numChannels, const size_t numFrames) = 0;
+ // Update the parameters of the DSP object according to the provided params.
+ // Not declaring a pure virtual bc there's no concrete definition that can
+ // use Params.
+ // But, use this name :)
+ // virtual void SetParams(Params* params) = 0;
+
+ protected:
+ // Methods
+
+ size_t _GetNumChannels() const {return this->mOutputs.size();};
+ // Return a pointer-to-pointers for the DSP's output buffers (all channels)
+ // Assumes that ._PrepareBuffers() was called recently enough.
+ iplug::sample** _GetPointers();
+ // Resize mOutputs to (numChannels, numFrames) and ensure that the raw
+ // pointers are also keeping up.
+ virtual void _PrepareBuffers(const size_t numChannels, const size_t numFrames);
+ // Resize the pointer-to-pointers for the vector-of-vectors.
+ void _ResizePointers(const size_t numChannels);
+
+
+ // Attributes
+
+ // The output array into which the DSP module's calculations will be written.
+ // Pointers to this member's data will be returned by .Process(), and std
+ // Will ensure proper allocation.
+ std::vector<std::vector<iplug::sample>> mOutputs;
+ // A pointer to pointers of which copies will be given out as the output of .Process().
+ // This object will ensure proper allocation and deallocation of the first level;
+ // The second level points to .data() from mOutputs.
+ iplug::sample** mOutputPointers;
+ size_t mOutputPointersSize;
+ };
+
+ // A class where a longer buffer of history is needed to correctly calculate
+ // the DSP algorithm (e.g. algorithms involving convolution).
+ //
+ // Hacky stuff:
+ // * Mono
+ // * Single-precision floats.
+ class History : public DSP {
+ public:
+ History();
+ protected:
+ // Called at the end of the DSP, advance the hsitory index to the next open
+ // spot. Does not ensure that it's at a valid address.
+ void _AdvanceHistoryIndex(const size_t bufferSize);
+ // Drop the new samples into the history array.
+ // Manages history array size
+ void _UpdateHistory(iplug::sample** inputs,
+ const size_t numChannels,
+ const size_t numFrames);
+
+ // The history array that's used for DSP calculations.
+ std::vector<float> mHistory;
+ // How many samples previous are required.
+ // Zero means that no history is required--only the current sample.
+ size_t mHistoryRequired;
+ // Location of the first sample in the current buffer.
+ // Shall always be in the range [mHistoryRequired, mHistory.size()).
+ size_t mHistoryIndex;
+
+ private:
+ // Make sure that the history array is long enough.
+ void _EnsureHistorySize(const size_t bufferSize);
+ // Copy the end of the history back to the fron and reset mHistoryIndex
+ void _RewindHistory();
+ };
+};
+
#endif // IPLUG_DSP
diff --git a/NeuralAmpModeler/dsp/wav.cpp b/NeuralAmpModeler/dsp/wav.cpp
@@ -0,0 +1,204 @@
+//
+// wav.cpp
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 12/31/22.
+//
+
+#include <cmath> // pow
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "wav.h"
+
+bool idIsJunk(char* id)
+{
+ return (strncmp(id, "junk", 4) == 0) || (strncmp(id, "JUNK", 4) == 0);
+}
+
+void ReadChunkAndSkipJunk(std::ifstream& file, char* chunkID)
+{
+ file.read(chunkID, 4);
+ if (idIsJunk(chunkID)) {
+ int junkSize;
+ file.read(reinterpret_cast<char*>(&junkSize), 4);
+ file.ignore(junkSize);
+ // Unused byte if junkSize is odd
+ if ((junkSize % 2) == 1)
+ file.ignore(1);
+ // And now we should be ready for data...
+ file.read(chunkID, 4);
+ }
+ if (idIsJunk(chunkID))
+ throw std::runtime_error("Found more than 1 junk chunk");
+}
+
+int dsp::wav::Load(const WDL_String &fileName, std::vector<float> &audio, double &sampleRate)
+{
+ // FYI: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
+ // Open the WAV file for reading
+ std::ifstream wavFile(fileName.Get(), std::ios::binary);
+
+ // Check if the file was opened successfully
+ if (!wavFile.is_open()) {
+ std::cerr << "Error opening WAV file" << std::endl;
+ return dsp::wav::RET_ERROR_OPENING;
+ }
+
+ // WAV file has 3 "chunks": RIFF ("RIFF"), format ("fmt ") and data ("data").
+ // Read the WAV file header
+ char chunkId[4];
+ ReadChunkAndSkipJunk(wavFile, chunkId);
+ if (strncmp(chunkId, "RIFF", 4) != 0) {
+ std::cerr << "Error: Not a WAV file" << std::endl;
+ return dsp::wav::RET_ERROR_NOT_WAV;
+ }
+
+ int chunkSize;
+ wavFile.read(reinterpret_cast<char*>(&chunkSize), 4);
+
+ char format[4];
+ wavFile.read(format, 4);
+ if (strncmp(format, "WAVE", 4) != 0) {
+ std::cerr << "Error: Not a WAV file" << std::endl;
+ return dsp::wav::RET_ERROR_NOT_WAV;
+ }
+
+ // Read the format chunk
+ char subchunk1Id[4];
+ ReadChunkAndSkipJunk(wavFile, subchunk1Id);
+ if (strncmp(subchunk1Id, "fmt ", 4) != 0) {
+ std::cerr << "Error: Invalid WAV file" << std::endl;
+ return dsp::wav::RET_ERROR_INVALID_WAV;
+ }
+
+ int subchunk1Size;
+ wavFile.read(reinterpret_cast<char*>(&subchunk1Size), 4);
+
+ unsigned short audioFormat;
+ wavFile.read(reinterpret_cast<char*>(&audioFormat), 2);
+ if (audioFormat != 1) {
+ std::cerr << "Error: Only PCM format is supported" << std::endl;
+ return dsp::wav::RET_ERROR_NOT_PCM;
+ }
+
+ short numChannels;
+ wavFile.read(reinterpret_cast<char*>(&numChannels), 2);
+ // HACK
+ if (numChannels != 1) {
+ std::cerr << "Require mono (using for IR loading)" << std::endl;
+ return dsp::wav::RET_ERROR_INVALID_WAV;
+ }
+
+ int iSampleRate;
+ wavFile.read(reinterpret_cast<char*>(&iSampleRate), 4);
+ // Store in format we assume (SR is double)
+ sampleRate = (double) iSampleRate;
+
+ int byteRate;
+ wavFile.read(reinterpret_cast<char*>(&byteRate), 4);
+
+ short blockAlign;
+ wavFile.read(reinterpret_cast<char*>(&blockAlign), 2);
+
+ short bitsPerSample;
+ wavFile.read(reinterpret_cast<char*>(&bitsPerSample), 2);
+
+ // Read the data chunk
+ char subchunk2Id[4];
+ ReadChunkAndSkipJunk(wavFile, subchunk2Id);
+ if (strncmp(subchunk2Id, "data", 4) != 0) {
+ std::cerr << "Error: Invalid WAV file" << std::endl;
+ return dsp::wav::RET_ERROR_INVALID_WAV;
+ }
+
+ // Size of the data chunk, in bits.
+ int subchunk2Size;
+ wavFile.read(reinterpret_cast<char*>(&subchunk2Size), 4);
+
+ if (bitsPerSample == 16)
+ dsp::wav::_LoadSamples16(wavFile, subchunk2Size, audio);
+ else if (bitsPerSample == 24)
+ dsp::wav::_LoadSamples24(wavFile, subchunk2Size, audio);
+ else if (bitsPerSample == 32)
+ dsp::wav::_LoadSamples32(wavFile, subchunk2Size, audio);
+ else {
+ std::cerr << "Error: Unsupported bits per sample: " << bitsPerSample << std::endl;
+ return 1;
+ }
+
+ // Close the WAV file
+ wavFile.close();
+
+ // Print the number of samples
+ // std::cout << "Number of samples: " << samples.size() << std::endl;
+
+ return dsp::wav::RET_SUCCESS;
+}
+
+void dsp::wav::_LoadSamples16(std::ifstream &wavFile,
+ const int chunkSize,
+ std::vector<float>& samples)
+{
+ // Allocate an array to hold the samples
+ std::vector<short> tmp(chunkSize / 2); // 16 bits (2 bytes) per sample
+
+ // Read the samples from the file into the array
+ wavFile.read(reinterpret_cast<char*>(tmp.data()), chunkSize);
+
+ // Copy into the return array
+ const float scale = 1.0 / ((double) (1 << 15));
+ samples.resize(tmp.size());
+ for (auto i=0; i<samples.size(); i++)
+ samples[i] = scale * ((float) tmp[i]); // 2^16
+}
+
+void dsp::wav::_LoadSamples24(std::ifstream &wavFile,
+ const int chunkSize,
+ std::vector<float>& samples)
+{
+ // Allocate an array to hold the samples
+ std::vector<int> tmp(chunkSize / 3); // 24 bits (3 bytes) per sample
+ // Read in and convert the samples
+ for (int& x : tmp) {
+ x = dsp::wav::_ReadSigned24BitInt(wavFile);
+ }
+
+ // Copy into the return array
+ const float scale = 1.0 / ((double) (1 << 23));
+ samples.resize(tmp.size());
+ for (auto i=0; i<samples.size(); i++)
+ samples[i] = scale * ((float) tmp[i]);
+}
+
+int dsp::wav::_ReadSigned24BitInt(std::ifstream& stream) {
+ // Read the three bytes of the 24-bit integer.
+ std::uint8_t bytes[3];
+ stream.read(reinterpret_cast<char*>(bytes), 3);
+
+ // Combine the three bytes into a single integer using bit shifting and masking.
+ // This works by isolating each byte using a bit mask (0xff) and then shifting
+ // the byte to the correct position in the final integer.
+ int value = bytes[0] | (bytes[1] << 8) | (bytes[2]<<16);
+
+ // The value is stored in two's complement format, so if the most significant
+ // bit (the 24th bit) is set, then the value is negative. In this case, we
+ // need to extend the sign bit to get the correct negative value.
+ if (value & (1 << 23)) {
+ value |= ~((1 << 24) - 1);
+ }
+
+ return value;
+}
+
+
+void dsp::wav::_LoadSamples32(std::ifstream &wavFile,
+ const int chunkSize,
+ std::vector<float>& samples)
+{
+ // NOTE: 32-bit is float.
+ samples.resize(chunkSize / 4); // 32 bits (4 bytes) per sample
+ // Read the samples from the file into the array
+ wavFile.read(reinterpret_cast<char*>(samples.data()), chunkSize);
+}
diff --git a/NeuralAmpModeler/dsp/wav.h b/NeuralAmpModeler/dsp/wav.h
@@ -0,0 +1,39 @@
+//
+// wav.h
+// NeuralAmpModeler-macOS
+//
+// Created by Steven Atkinson on 12/31/22.
+//
+
+#ifndef wav_h
+#define wav_h
+
+#include "wdlstring.h" // WDL_String
+
+namespace dsp {
+ namespace wav {
+ // Return cases
+ const int RET_SUCCESS = 0;
+ const int RET_ERROR_OPENING = 1;
+ const int RET_ERROR_NOT_WAV = 2;
+ const int RET_ERROR_INVALID_WAV = 3;
+ const int RET_ERROR_NOT_PCM = 4;
+ // Load a WAV file into a provided array of doubles,
+ // And note the sample rate.
+ //
+ // Returns: as per return cases above
+ int Load(const WDL_String& fileName, std::vector<float> &audio, double& sampleRate);
+
+ // Load samples, 16-bit
+ void _LoadSamples16(std::ifstream &wavFile, const int chunkSize, std::vector<float>& samples);
+ // Load samples, 24-bit
+ void _LoadSamples24(std::ifstream &wavFile, const int chunkSize, std::vector<float>& samples);
+ // Load samples, 32-bit
+ void _LoadSamples32(std::ifstream &wavFile, const int chunkSize, std::vector<float>& samples);
+
+ // Read in a 24-bit sample and convert it to an int
+ int _ReadSigned24BitInt(std::ifstream& stream);
+ };
+};
+
+#endif /* wav_h */
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-aax.vcxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-aax.vcxproj
@@ -412,10 +412,12 @@
<ClCompile Include="..\dsp\cnpy.cpp" />
<ClCompile Include="..\dsp\dsp.cpp" />
<ClCompile Include="..\dsp\get_dsp.cpp" />
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp" />
<ClCompile Include="..\dsp\lstm.cpp" />
<ClCompile Include="..\dsp\numpy_util.cpp" />
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp" />
<ClCompile Include="..\dsp\util.cpp" />
+ <ClCompile Include="..\dsp\wav.cpp" />
<ClCompile Include="..\dsp\wavenet.cpp" />
<ClCompile Include="..\NeuralAmpModeler.cpp" />
</ItemGroup>
@@ -503,10 +505,13 @@
<ClInclude Include="..\dsp\cnpy.h" />
<ClInclude Include="..\dsp\dsp.h" />
<ClInclude Include="..\dsp\HardCodedModel.h" />
+ <ClInclude Include="..\dsp\ImpulseResponse.h" />
<ClInclude Include="..\dsp\lstm.h" />
<ClInclude Include="..\dsp\numpy_util.h" />
<ClInclude Include="..\dsp\RecursiveLinearFilter.h" />
+ <ClInclude Include="..\dsp\Resample.h" />
<ClInclude Include="..\dsp\util.h" />
+ <ClInclude Include="..\dsp\wav.h" />
<ClInclude Include="..\dsp\wavenet.h" />
<ClInclude Include="..\NeuralAmpModeler.h" />
<ClInclude Include="..\resources\resource.h" />
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-aax.vcxproj.filters b/NeuralAmpModeler/projects/NeuralAmpModeler-aax.vcxproj.filters
@@ -83,6 +83,12 @@
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp">
<Filter>dsp</Filter>
</ClCompile>
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dsp\wav.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\NeuralAmpModeler.h" />
@@ -262,6 +268,15 @@
<ClInclude Include="..\dsp\RecursiveLinearFilter.h">
<Filter>dsp</Filter>
</ClInclude>
+ <ClInclude Include="..\dsp\ImpulseResponse.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\wav.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\Resample.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="resources">
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-app.vcxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-app.vcxproj
@@ -322,10 +322,13 @@
<ClInclude Include="..\dsp\cnpy.h" />
<ClInclude Include="..\dsp\dsp.h" />
<ClInclude Include="..\dsp\HardCodedModel.h" />
+ <ClInclude Include="..\dsp\ImpulseResponse.h" />
<ClInclude Include="..\dsp\lstm.h" />
<ClInclude Include="..\dsp\numpy_util.h" />
<ClInclude Include="..\dsp\RecursiveLinearFilter.h" />
+ <ClInclude Include="..\dsp\Resample.h" />
<ClInclude Include="..\dsp\util.h" />
+ <ClInclude Include="..\dsp\wav.h" />
<ClInclude Include="..\dsp\wavenet.h" />
<ClInclude Include="..\NeuralAmpModeler.h" />
<ClInclude Include="..\resources\resource.h" />
@@ -373,10 +376,12 @@
<ClCompile Include="..\dsp\cnpy.cpp" />
<ClCompile Include="..\dsp\dsp.cpp" />
<ClCompile Include="..\dsp\get_dsp.cpp" />
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp" />
<ClCompile Include="..\dsp\lstm.cpp" />
<ClCompile Include="..\dsp\numpy_util.cpp" />
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp" />
<ClCompile Include="..\dsp\util.cpp" />
+ <ClCompile Include="..\dsp\wav.cpp" />
<ClCompile Include="..\dsp\wavenet.cpp" />
<ClCompile Include="..\NeuralAmpModeler.cpp" />
</ItemGroup>
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-app.vcxproj.filters b/NeuralAmpModeler/projects/NeuralAmpModeler-app.vcxproj.filters
@@ -101,6 +101,12 @@
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp">
<Filter>dsp</Filter>
</ClCompile>
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dsp\wav.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\NeuralAmpModeler.h" />
@@ -310,6 +316,15 @@
<ClInclude Include="..\dsp\RecursiveLinearFilter.h">
<Filter>dsp</Filter>
</ClInclude>
+ <ClInclude Include="..\dsp\ImpulseResponse.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\wav.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\Resample.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="resources">
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-iOS.xcodeproj/project.pbxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-iOS.xcodeproj/project.pbxproj
@@ -73,6 +73,9 @@
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, ); }; };
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 */; };
+ AAD9F873296377A6009DBBA2 /* Resample.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F872296377A6009DBBA2 /* Resample.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -317,6 +320,11 @@
91236D771B08F59300734C5E /* NeuralAmpModelerAppExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NeuralAmpModelerAppExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
+ AAD9F85C295F762B009DBBA2 /* ImpulseResponse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImpulseResponse.cpp; sourceTree = "<group>"; };
+ AAD9F86C2960D4D0009DBBA2 /* wav.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wav.cpp; sourceTree = "<group>"; };
+ AAD9F86D2960D4D0009DBBA2 /* wav.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wav.h; sourceTree = "<group>"; };
+ AAD9F872296377A6009DBBA2 /* Resample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Resample.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -359,22 +367,27 @@
4F3EF90528DE0BA7002972F2 /* dsp */ = {
isa = PBXGroup;
children = (
- AAD9F84A295EC4A0009DBBA2 /* RecursiveLinearFilter.h */,
- AAD9F848295EC49D009DBBA2 /* RecursiveLinearFilter.cpp */,
- 4F3EF90728DE0BA7002972F2 /* util.cpp */,
+ 4F3EF91028DE0BA7002972F2 /* activations.h */,
+ 4F3EF91428DE0BA7002972F2 /* cnpy.h */,
+ 4F3EF90A28DE0BA7002972F2 /* cnpy.cpp */,
+ 4F3EF90E28DE0BA7002972F2 /* dsp.h */,
4F3EF90828DE0BA7002972F2 /* dsp.cpp */,
+ 4F3EF91328DE0BA7002972F2 /* get_dsp.cpp */,
+ AAD9F85B295F761C009DBBA2 /* ImpulseResponse.h */,
+ AAD9F85C295F762B009DBBA2 /* ImpulseResponse.cpp */,
4F3EF90928DE0BA7002972F2 /* lstm.h */,
- 4F3EF90A28DE0BA7002972F2 /* cnpy.cpp */,
- 4F3EF90B28DE0BA7002972F2 /* wavenet.h */,
4F3EF90C28DE0BA7002972F2 /* lstm.cpp */,
- 4F3EF90D28DE0BA7002972F2 /* util.h */,
- 4F3EF90E28DE0BA7002972F2 /* dsp.h */,
+ 4F3EF91228DE0BA7002972F2 /* numpy_util.h */,
4F3EF90F28DE0BA7002972F2 /* numpy_util.cpp */,
- 4F3EF91028DE0BA7002972F2 /* activations.h */,
+ AAD9F84A295EC4A0009DBBA2 /* RecursiveLinearFilter.h */,
+ AAD9F848295EC49D009DBBA2 /* RecursiveLinearFilter.cpp */,
+ AAD9F872296377A6009DBBA2 /* Resample.h */,
+ 4F3EF90D28DE0BA7002972F2 /* util.h */,
+ 4F3EF90728DE0BA7002972F2 /* util.cpp */,
+ 4F3EF90B28DE0BA7002972F2 /* wavenet.h */,
4F3EF91128DE0BA7002972F2 /* wavenet.cpp */,
- 4F3EF91228DE0BA7002972F2 /* numpy_util.h */,
- 4F3EF91328DE0BA7002972F2 /* get_dsp.cpp */,
- 4F3EF91428DE0BA7002972F2 /* cnpy.h */,
+ AAD9F86D2960D4D0009DBBA2 /* wav.h */,
+ AAD9F86C2960D4D0009DBBA2 /* wav.cpp */,
);
name = dsp;
path = ../dsp;
@@ -732,6 +745,7 @@
buildActionMask = 2147483647;
files = (
4F3EF91828DE0BA7002972F2 /* lstm.h in Headers */,
+ AAD9F873296377A6009DBBA2 /* Resample.h in Headers */,
4F4856882773C41E005BCF8E /* IPlugAUAudioUnit.h in Headers */,
4F3EF91D28DE0BA7002972F2 /* dsp.h in Headers */,
4F3EF92128DE0BA7002972F2 /* numpy_util.h in Headers */,
@@ -946,6 +960,8 @@
4F914A4C26B4911A00E19BD1 /* IPlugAUViewController.mm in Sources */,
4FA61F8E22E89B4300A92C58 /* IGraphicsIOS.mm in Sources */,
4FA61F9022E8A1F500A92C58 /* IGraphics.cpp in Sources */,
+ AAD9F85D295F762B009DBBA2 /* ImpulseResponse.cpp in Sources */,
+ AAD9F86E2960D4D0009DBBA2 /* wav.cpp in Sources */,
4F3EF91928DE0BA7002972F2 /* cnpy.cpp in Sources */,
4F3EF92028DE0BA7002972F2 /* wavenet.cpp in Sources */,
4FA61F7E22E89AFF00A92C58 /* IPlugPluginBase.cpp in Sources */,
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/project.pbxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-macOS.xcodeproj/project.pbxproj
@@ -403,6 +403,32 @@
AAD9F844295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */; };
AAD9F846295E6CB7009DBBA2 /* RecursiveLinearFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */; };
AAD9F847295E6CB7009DBBA2 /* RecursiveLinearFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */; };
+ AAD9F84F295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F850295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F851295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F852295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F853295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F854295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F855295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F856295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F857295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F858295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */; };
+ AAD9F859295F75F6009DBBA2 /* ImpulseResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F84E295F75F6009DBBA2 /* ImpulseResponse.h */; };
+ AAD9F85A295F75F6009DBBA2 /* ImpulseResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F84E295F75F6009DBBA2 /* ImpulseResponse.h */; };
+ AAD9F85F2960D0C6009DBBA2 /* wav.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F85E2960D0C6009DBBA2 /* wav.h */; };
+ AAD9F8602960D0C6009DBBA2 /* wav.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F85E2960D0C6009DBBA2 /* wav.h */; };
+ AAD9F8622960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8632960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8642960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8652960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8662960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8672960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8682960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8692960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F86A2960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F86B2960D4BA009DBBA2 /* wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAD9F8612960D4BA009DBBA2 /* wav.cpp */; };
+ AAD9F8702963777E009DBBA2 /* Resample.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F86F2963777E009DBBA2 /* Resample.h */; };
+ AAD9F8712963777E009DBBA2 /* Resample.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9F86F2963777E009DBBA2 /* Resample.h */; };
B885CBC52304AE7300D73128 /* IPlugProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F8F61A8202807B9003F2573 /* IPlugProcessor.cpp */; };
B8E22A0C220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8E22A0A220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp */; };
B8E22A0D220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8E22A0A220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp */; };
@@ -956,6 +982,11 @@
AAD248502954339400F55DD4 /* AUv3AppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AUv3AppRelease.entitlements; sourceTree = "<group>"; };
AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecursiveLinearFilter.cpp; sourceTree = "<group>"; };
AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecursiveLinearFilter.h; sourceTree = "<group>"; };
+ AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImpulseResponse.cpp; sourceTree = "<group>"; };
+ AAD9F84E295F75F6009DBBA2 /* ImpulseResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImpulseResponse.h; sourceTree = "<group>"; };
+ AAD9F85E2960D0C6009DBBA2 /* wav.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wav.h; sourceTree = "<group>"; };
+ AAD9F8612960D4BA009DBBA2 /* wav.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wav.cpp; sourceTree = "<group>"; };
+ AAD9F86F2963777E009DBBA2 /* Resample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Resample.h; sourceTree = "<group>"; };
B8E22A0A220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = IPlugVST3_ProcessorBase.cpp; path = ../../iPlug2/IPlug/VST3/IPlugVST3_ProcessorBase.cpp; sourceTree = "<group>"; tabWidth = 2; };
B8E22A0B220268C4007CBF4C /* IPlugVST3_ProcessorBase.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = IPlugVST3_ProcessorBase.h; path = ../../iPlug2/IPlug/VST3/IPlugVST3_ProcessorBase.h; sourceTree = "<group>"; tabWidth = 2; };
B8EA6B932203868500D23A86 /* IPlugVST3_Common.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = IPlugVST3_Common.h; path = ../../iPlug2/IPlug/VST3/IPlugVST3_Common.h; sourceTree = "<group>"; tabWidth = 2; };
@@ -1239,22 +1270,27 @@
4F3EF8AA28DE03ED002972F2 /* dsp */ = {
isa = PBXGroup;
children = (
- AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */,
- AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */,
- 4F3EF8AC28DE03ED002972F2 /* util.cpp */,
+ 4F3EF8B528DE03ED002972F2 /* activations.h */,
+ 4F3EF8B928DE03ED002972F2 /* cnpy.h */,
+ 4F3EF8AF28DE03ED002972F2 /* cnpy.cpp */,
4F3EF8B328DE03ED002972F2 /* dsp.h */,
4F3EF8AD28DE03ED002972F2 /* dsp.cpp */,
+ 4F3EF8B828DE03ED002972F2 /* get_dsp.cpp */,
+ AAD9F84E295F75F6009DBBA2 /* ImpulseResponse.h */,
+ AAD9F84D295F75F6009DBBA2 /* ImpulseResponse.cpp */,
4F3EF8AE28DE03ED002972F2 /* lstm.h */,
- 4F3EF8AF28DE03ED002972F2 /* cnpy.cpp */,
- 4F3EF8B028DE03ED002972F2 /* wavenet.h */,
4F3EF8B128DE03ED002972F2 /* lstm.cpp */,
- 4F3EF8B228DE03ED002972F2 /* util.h */,
+ 4F3EF8B728DE03ED002972F2 /* numpy_util.h */,
4F3EF8B428DE03ED002972F2 /* numpy_util.cpp */,
- 4F3EF8B528DE03ED002972F2 /* activations.h */,
+ AAD9F845295E6CB7009DBBA2 /* RecursiveLinearFilter.h */,
+ AAD9F83A295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp */,
+ AAD9F86F2963777E009DBBA2 /* Resample.h */,
+ 4F3EF8B228DE03ED002972F2 /* util.h */,
+ 4F3EF8AC28DE03ED002972F2 /* util.cpp */,
+ 4F3EF8B028DE03ED002972F2 /* wavenet.h */,
4F3EF8B628DE03ED002972F2 /* wavenet.cpp */,
- 4F3EF8B728DE03ED002972F2 /* numpy_util.h */,
- 4F3EF8B828DE03ED002972F2 /* get_dsp.cpp */,
- 4F3EF8B928DE03ED002972F2 /* cnpy.h */,
+ AAD9F85E2960D0C6009DBBA2 /* wav.h */,
+ AAD9F8612960D4BA009DBBA2 /* wav.cpp */,
);
name = dsp;
path = ../dsp;
@@ -1888,11 +1924,14 @@
4F3EF8E228DE03ED002972F2 /* dsp.h in Headers */,
4F3EF8F628DE03ED002972F2 /* numpy_util.h in Headers */,
4F78BE1222E73DD900AD537E /* NeuralAmpModelerAU.h in Headers */,
+ AAD9F8602960D0C6009DBBA2 /* wav.h in Headers */,
4F4856852773C3B5005BCF8E /* IPlugAUViewController.h in Headers */,
4F3EF8E128DE03ED002972F2 /* util.h in Headers */,
4F3EF8EC28DE03ED002972F2 /* activations.h in Headers */,
+ AAD9F85A295F75F6009DBBA2 /* ImpulseResponse.h in Headers */,
4F3EF8D728DE03ED002972F2 /* wavenet.h in Headers */,
AA355E2E295B688F0061AA3D /* Colors.h in Headers */,
+ AAD9F8712963777E009DBBA2 /* Resample.h in Headers */,
AAD9F847295E6CB7009DBBA2 /* RecursiveLinearFilter.h in Headers */,
4F3EF90028DE03ED002972F2 /* cnpy.h in Headers */,
);
@@ -1902,10 +1941,12 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ AAD9F85F2960D0C6009DBBA2 /* wav.h in Headers */,
AA355E2D295B688F0061AA3D /* Colors.h in Headers */,
4FC3EFF02086CE5700BD11FA /* eventlist.h in Headers */,
4FC3EFF42086CE5700BD11FA /* module.h in Headers */,
4F03A58C20A4621100EBDFFB /* IGraphicsNanoVG.h in Headers */,
+ AAD9F8702963777E009DBBA2 /* Resample.h in Headers */,
4F03A5D320A4621100EBDFFB /* IGraphicsStructs.h in Headers */,
4FC3EFF82086CE5700BD11FA /* optional.h in Headers */,
4FC3F0012086CE5700BD11FA /* uid.h in Headers */,
@@ -1923,6 +1964,7 @@
4F03A5D220A4621100EBDFFB /* IGraphicsPopupMenu.h in Headers */,
4FC3EFFE2086CE5700BD11FA /* processdata.h in Headers */,
4FC3F0002086CE5700BD11FA /* stringconvert.h in Headers */,
+ AAD9F859295F75F6009DBBA2 /* ImpulseResponse.h in Headers */,
4F8C10E720BA2796006320CD /* IGraphicsEditorDelegate.h in Headers */,
4FC3EFFA2086CE5700BD11FA /* parameterchanges.h in Headers */,
4F03A5B320A4621100EBDFFB /* IGraphics_include_in_plug_src.h in Headers */,
@@ -2481,6 +2523,7 @@
AAD9F83C295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */,
4F3EF8D928DE03ED002972F2 /* lstm.cpp in Sources */,
4F3EF8E428DE03ED002972F2 /* numpy_util.cpp in Sources */,
+ AAD9F8632960D4BA009DBBA2 /* wav.cpp in Sources */,
4F5F344220C0226200487201 /* IPlugPaths.mm in Sources */,
4F3EF8C528DE03ED002972F2 /* dsp.cpp in Sources */,
4F6369EC20A466470022C370 /* IControl.cpp in Sources */,
@@ -2496,6 +2539,7 @@
4F78D9BB13B63BA50032E0F3 /* IPlugAPIBase.cpp in Sources */,
4FB1F59020E4B010004157C8 /* IGraphicsMac_view.mm in Sources */,
4F3EF8BC28DE03ED002972F2 /* util.cpp in Sources */,
+ AAD9F850295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F993F7223055C96000313AF /* IPlugProcessor.cpp in Sources */,
4F35DEAE207E5C5A00867D8F /* IPlugPluginBase.cpp in Sources */,
4F6FD2B222675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
@@ -2512,9 +2556,11 @@
4F3EF8CA28DE03ED002972F2 /* dsp.cpp in Sources */,
4F4856892773CA76005BCF8E /* NeuralAmpModelerAUv3Appex.m in Sources */,
4F3EF8FD28DE03ED002972F2 /* get_dsp.cpp in Sources */,
+ AAD9F855295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F3EF8C128DE03ED002972F2 /* util.cpp in Sources */,
4F3EF8E928DE03ED002972F2 /* numpy_util.cpp in Sources */,
4F3EF8F328DE03ED002972F2 /* wavenet.cpp in Sources */,
+ AAD9F8682960D4BA009DBBA2 /* wav.cpp in Sources */,
4F3EF8DE28DE03ED002972F2 /* lstm.cpp in Sources */,
4F3EF8D428DE03ED002972F2 /* cnpy.cpp in Sources */,
);
@@ -2535,6 +2581,8 @@
4FD52131202A5B9B00A4D22A /* IPlugAU_view_factory.mm in Sources */,
4F993F7423055C96000313AF /* IPlugProcessor.cpp in Sources */,
4F8C10E320BA2796006320CD /* IGraphicsEditorDelegate.cpp in Sources */,
+ AAD9F852295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
+ AAD9F8652960D4BA009DBBA2 /* wav.cpp in Sources */,
4F3EF8D128DE03ED002972F2 /* cnpy.cpp in Sources */,
4F3EF8F028DE03ED002972F2 /* wavenet.cpp in Sources */,
4F6FD2B422675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
@@ -2589,7 +2637,9 @@
AAD9F844295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */,
4F3EF8C328DE03ED002972F2 /* util.cpp in Sources */,
4F3EE1D9231438D000004786 /* RtAudio.cpp in Sources */,
+ AAD9F86B2960D4BA009DBBA2 /* wav.cpp in Sources */,
4F3EE1DA231438D000004786 /* IGraphicsCoreText.mm in Sources */,
+ AAD9F858295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F3EE1DB231438D000004786 /* IPlugAPP_main.cpp in Sources */,
4F3EF8CC28DE03ED002972F2 /* dsp.cpp in Sources */,
4F3EE1DD231438D000004786 /* IGraphicsMac.mm in Sources */,
@@ -2611,6 +2661,7 @@
4F7C496A255DDFCB00DF7588 /* IControls.cpp in Sources */,
4F3EF8CB28DE03ED002972F2 /* dsp.cpp in Sources */,
4F78BE1422E7406D00AD537E /* NeuralAmpModeler.h in Sources */,
+ AAD9F86A2960D4BA009DBBA2 /* wav.cpp in Sources */,
4F0D965C23099F6900BFDED0 /* IPlugProcessor.cpp in Sources */,
4F78BE1522E7406D00AD537E /* NeuralAmpModeler.cpp in Sources */,
4F3EF8F428DE03ED002972F2 /* wavenet.cpp in Sources */,
@@ -2632,6 +2683,7 @@
4F3EF8DF28DE03ED002972F2 /* lstm.cpp in Sources */,
4F78BE2522E7406D00AD537E /* IPlugPluginBase.cpp in Sources */,
4F78BE2622E7406D00AD537E /* IPlugAPIBase.cpp in Sources */,
+ AAD9F857295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F78BE2822E7406D00AD537E /* IPlugParameter.cpp in Sources */,
4F78BE2922E7406D00AD537E /* IPlugTimer.cpp in Sources */,
4F3EF8EA28DE03ED002972F2 /* numpy_util.cpp in Sources */,
@@ -2684,12 +2736,14 @@
4F6369DF20A464BB0022C370 /* IGraphicsNanoVG_src.m in Sources */,
4F815991205D51F000393585 /* vstcomponentbase.cpp in Sources */,
4F03A5AE20A4621100EBDFFB /* IGraphics.cpp in Sources */,
+ AAD9F851295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F815987205D50EB00393585 /* conststringtable.cpp in Sources */,
4F722020225C1EB100FF0E7C /* commoniids.cpp in Sources */,
4FB1F59220E4B012004157C8 /* IGraphicsMac_view.mm in Sources */,
4F81598D205D51F000393585 /* vstaudioeffect.cpp in Sources */,
4F81598C205D51CF00393585 /* vstsinglecomponenteffect.cpp in Sources */,
4F815984205D50EB00393585 /* updatehandler.cpp in Sources */,
+ AAD9F8642960D4BA009DBBA2 /* wav.cpp in Sources */,
4F815990205D51F000393585 /* vstcomponent.cpp in Sources */,
4F815981205D50EB00393585 /* fstreamer.cpp in Sources */,
4F815996205D51F000393585 /* vstrepresentation.cpp in Sources */,
@@ -2711,6 +2765,7 @@
4F7C4961255DDFC600DF7588 /* IControls.cpp in Sources */,
4F3EF8C828DE03ED002972F2 /* dsp.cpp in Sources */,
4F993F7523055C97000313AF /* IPlugProcessor.cpp in Sources */,
+ AAD9F8662960D4BA009DBBA2 /* wav.cpp in Sources */,
4FDAC0EE207D76C600299363 /* IPlugTimer.cpp in Sources */,
4F8C10E420BA2796006320CD /* IGraphicsEditorDelegate.cpp in Sources */,
4F3EF8F128DE03ED002972F2 /* wavenet.cpp in Sources */,
@@ -2732,6 +2787,7 @@
4F3EF8DC28DE03ED002972F2 /* lstm.cpp in Sources */,
4FB600231567CB0A0020189A /* IPlugParameter.cpp in Sources */,
4FB600261567CB0A0020189A /* AAX_Exports.cpp in Sources */,
+ AAD9F853295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F6FD2B522675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
4FB600281567CB0A0020189A /* IPlugAAX_Describe.cpp in Sources */,
4F3EF8E728DE03ED002972F2 /* numpy_util.cpp in Sources */,
@@ -2746,6 +2802,7 @@
files = (
4F7C4964255DDFC800DF7588 /* IControls.cpp in Sources */,
4F03A5B220A4621100EBDFFB /* IGraphics.cpp in Sources */,
+ AAD9F856295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F7C4966255DDFC800DF7588 /* ITextEntryControl.cpp in Sources */,
AAD9F842295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */,
4FB1F58F20E4B009004157C8 /* IGraphicsMac.mm in Sources */,
@@ -2755,6 +2812,7 @@
4FC3EFF92086CE5700BD11FA /* parameterchanges.cpp in Sources */,
4FC3EFCE2086C35D00BD11FA /* IPlugPluginBase.cpp in Sources */,
4F7C4965255DDFC800DF7588 /* IPopupMenuControl.cpp in Sources */,
+ AAD9F8692960D4BA009DBBA2 /* wav.cpp in Sources */,
4F722021225C1EB100FF0E7C /* commoniids.cpp in Sources */,
4FB1F59620E4B017004157C8 /* IGraphicsMac_view.mm in Sources */,
4F472103209B294400A0A0A8 /* IPlugVST3_Controller.cpp in Sources */,
@@ -2797,7 +2855,9 @@
AAD9F83B295E6C8D009DBBA2 /* RecursiveLinearFilter.cpp in Sources */,
4F3EF8BB28DE03ED002972F2 /* util.cpp in Sources */,
4FAFFE5821495A4800A6E72D /* RtAudio.cpp in Sources */,
+ AAD9F8622960D4BA009DBBA2 /* wav.cpp in Sources */,
4F6FD2B122675B6300FC59E6 /* IGraphicsCoreText.mm in Sources */,
+ AAD9F84F295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4F690C9B203A345100A4A13E /* IPlugAPP_main.cpp in Sources */,
4F3EF8C428DE03ED002972F2 /* dsp.cpp in Sources */,
4FB1F58920E4B004004157C8 /* IGraphicsMac.mm in Sources */,
@@ -2822,12 +2882,14 @@
4FFBB90820863B0E00DDD0E7 /* pluginfactory.cpp in Sources */,
4FFBB90920863B0E00DDD0E7 /* vstinitiids.cpp in Sources */,
4FFBB90A20863B0E00DDD0E7 /* fdebug.cpp in Sources */,
+ AAD9F8672960D4BA009DBBA2 /* wav.cpp in Sources */,
4FFBB90C20863B0E00DDD0E7 /* IPlugAPIBase.cpp in Sources */,
B8E22A0D220268C4007CBF4C /* IPlugVST3_ProcessorBase.cpp in Sources */,
4FFBB90D20863B0E00DDD0E7 /* fdynlib.cpp in Sources */,
4FFBB90E20863B0E00DDD0E7 /* memorystream.cpp in Sources */,
4F3EF8F228DE03ED002972F2 /* wavenet.cpp in Sources */,
4FFBB90F20863B0E00DDD0E7 /* IPlugParameter.cpp in Sources */,
+ AAD9F854295F75F6009DBBA2 /* ImpulseResponse.cpp in Sources */,
4FFBB91020863B0E00DDD0E7 /* pluginview.cpp in Sources */,
4FFBB91120863B0E00DDD0E7 /* fstring.cpp in Sources */,
4FFBB91320863B0E00DDD0E7 /* vstpresetfile.cpp in Sources */,
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-vst2.vcxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-vst2.vcxproj
@@ -299,10 +299,13 @@
<ClInclude Include="..\dsp\cnpy.h" />
<ClInclude Include="..\dsp\dsp.h" />
<ClInclude Include="..\dsp\HardCodedModel.h" />
+ <ClInclude Include="..\dsp\ImpulseResponse.h" />
<ClInclude Include="..\dsp\lstm.h" />
<ClInclude Include="..\dsp\numpy_util.h" />
<ClInclude Include="..\dsp\RecursiveLinearFilter.h" />
+ <ClInclude Include="..\dsp\Resample.h" />
<ClInclude Include="..\dsp\util.h" />
+ <ClInclude Include="..\dsp\wav.h" />
<ClInclude Include="..\dsp\wavenet.h" />
<ClInclude Include="..\NeuralAmpModeler.h" />
<ClInclude Include="..\resources\resource.h" />
@@ -341,10 +344,12 @@
<ClCompile Include="..\dsp\cnpy.cpp" />
<ClCompile Include="..\dsp\dsp.cpp" />
<ClCompile Include="..\dsp\get_dsp.cpp" />
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp" />
<ClCompile Include="..\dsp\lstm.cpp" />
<ClCompile Include="..\dsp\numpy_util.cpp" />
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp" />
<ClCompile Include="..\dsp\util.cpp" />
+ <ClCompile Include="..\dsp\wav.cpp" />
<ClCompile Include="..\dsp\wavenet.cpp" />
<ClCompile Include="..\NeuralAmpModeler.cpp" />
</ItemGroup>
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-vst2.vcxproj.filters b/NeuralAmpModeler/projects/NeuralAmpModeler-vst2.vcxproj.filters
@@ -72,6 +72,12 @@
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp">
<Filter>dsp</Filter>
</ClCompile>
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dsp\wav.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="../config.h" />
@@ -245,6 +251,15 @@
<ClInclude Include="..\dsp\RecursiveLinearFilter.h">
<Filter>dsp</Filter>
</ClInclude>
+ <ClInclude Include="..\dsp\ImpulseResponse.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\wav.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\Resample.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="IPlug">
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-vst3.vcxproj b/NeuralAmpModeler/projects/NeuralAmpModeler-vst3.vcxproj
@@ -331,10 +331,13 @@
<ClInclude Include="..\dsp\cnpy.h" />
<ClInclude Include="..\dsp\dsp.h" />
<ClInclude Include="..\dsp\HardCodedModel.h" />
+ <ClInclude Include="..\dsp\ImpulseResponse.h" />
<ClInclude Include="..\dsp\lstm.h" />
<ClInclude Include="..\dsp\numpy_util.h" />
<ClInclude Include="..\dsp\RecursiveLinearFilter.h" />
+ <ClInclude Include="..\dsp\Resample.h" />
<ClInclude Include="..\dsp\util.h" />
+ <ClInclude Include="..\dsp\wav.h" />
<ClInclude Include="..\dsp\wavenet.h" />
<ClInclude Include="..\NeuralAmpModeler.h" />
<ClInclude Include="..\resources\resource.h" />
@@ -401,10 +404,12 @@
<ClCompile Include="..\dsp\cnpy.cpp" />
<ClCompile Include="..\dsp\dsp.cpp" />
<ClCompile Include="..\dsp\get_dsp.cpp" />
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp" />
<ClCompile Include="..\dsp\lstm.cpp" />
<ClCompile Include="..\dsp\numpy_util.cpp" />
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp" />
<ClCompile Include="..\dsp\util.cpp" />
+ <ClCompile Include="..\dsp\wav.cpp" />
<ClCompile Include="..\dsp\wavenet.cpp" />
<ClCompile Include="..\NeuralAmpModeler.cpp" />
</ItemGroup>
diff --git a/NeuralAmpModeler/projects/NeuralAmpModeler-vst3.vcxproj.filters b/NeuralAmpModeler/projects/NeuralAmpModeler-vst3.vcxproj.filters
@@ -158,6 +158,12 @@
<ClCompile Include="..\dsp\RecursiveLinearFilter.cpp">
<Filter>dsp</Filter>
</ClCompile>
+ <ClCompile Include="..\dsp\ImpulseResponse.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dsp\wav.cpp">
+ <Filter>dsp</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="../config.h" />
@@ -409,6 +415,15 @@
<ClInclude Include="..\dsp\RecursiveLinearFilter.h">
<Filter>dsp</Filter>
</ClInclude>
+ <ClInclude Include="..\dsp\ImpulseResponse.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\wav.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dsp\Resample.h">
+ <Filter>dsp</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="resources">
diff --git a/NeuralAmpModeler/resources/img/close-button.svg b/NeuralAmpModeler/resources/img/close-button.svg
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ id="svg2"
+ viewBox="0 0 750.00003 754.197"
+ version="1.1"
+ sodipodi:docname="close-button.svg"
+ width="699.979"
+ height="703.89606"
+ inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs24" />
+ <sodipodi:namedview
+ id="namedview22"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ showgrid="false"
+ inkscape:zoom="0.97586467"
+ inkscape:cx="194.69913"
+ inkscape:cy="369.92834"
+ inkscape:window-width="3840"
+ inkscape:window-height="2097"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <g
+ id="layer1"
+ transform="translate(1.0553132,-130.49579)">
+ <path
+ id="path2989"
+ d="m 814.29,606.65 a 314.29,314.29 0 1 1 -628.57,0 314.29,314.29 0 1 1 628.57,0 z"
+ stroke="#000000"
+ stroke-width="5"
+ transform="matrix(1.1048,0,0,1.1048,-179.21,-162.53)" />
+ <g
+ id="g3763"
+ transform="matrix(0.91837,0,0,0.91837,47.587,10.944)"
+ stroke="#ffffff"
+ stroke-linecap="round"
+ stroke-width="133.87"
+ fill="none">
+ <path
+ id="path2991"
+ d="M 176.51,362.87 532.64,719" />
+ <path
+ id="path2993"
+ d="M 532.64,362.87 176.51,719" />
+ </g>
+ </g>
+ <metadata
+ id="metadata19">
+ <rdf:RDF>
+ <cc:Work>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://openclipart.org/">
+ <dc:title>Openclipart</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:title>Close Button</dc:title>
+ <dc:date>2013-09-18T11:23:45</dc:date>
+ <dc:description>A round black close button with a white, thick, X in the center.</dc:description>
+ <dc:source>https://openclipart.org/detail/183568/close-button-by-henrikhoff-183568</dc:source>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>henrikhoff</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>black</rdf:li>
+ <rdf:li>close button</rdf:li>
+ <rdf:li>cross</rdf:li>
+ <rdf:li>icon</rdf:li>
+ <rdf:li>white</rdf:li>
+ <rdf:li>x</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+</svg>
diff --git a/NeuralAmpModeler/resources/main.rc b/NeuralAmpModeler/resources/main.rc
@@ -233,6 +233,7 @@ ROBOTO_FN TTF ROBOTO_FN
TOLEX_FN JPEG TOLEX_FN
TOLEX2X_FN JPEG TOLEX2X_FN
FOLDER_FN SVG FOLDER_FN
+CLOSE_BUTTON_FN SVG CLOSE_BUTTON_FN
HELP_FN SVG HELP_FN
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED