commit 76ac813f6f56cbc2acce5f143cf4afd6138c536c
parent 7d7eaab5f1d96293731bcb41a628945b2ea053d4
Author: jatinchowdhury18 <[email protected]>
Date: Fri, 14 Aug 2020 11:43:31 -0700
Add benchmarking console app (#68)
* Set up benchmarking console app
* Adjust benchmarking app for Linux
* Unroll Newton-Raphson loop
Co-authored-by: jatinchowdhury18 <[email protected]>
Diffstat:
3 files changed, 302 insertions(+), 4 deletions(-)
diff --git a/Plugin/Bench/CHOWTapeBench.jucer b/Plugin/Bench/CHOWTapeBench.jucer
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<JUCERPROJECT id="k9sHIZ" name="CHOWTapeBench" projectType="consoleapp" useAppConfig="0"
+ addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1">
+ <MAINGROUP id="KOQkEi" name="CHOWTapeBench">
+ <GROUP id="{C7023DBC-3FC3-D40D-C17D-322846554284}" name="Source">
+ <FILE id="HsSjYU" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
+ </GROUP>
+ </MAINGROUP>
+ <JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_PLUGINHOST_VST3="1"/>
+ <EXPORTFORMATS>
+ <LINUX_MAKE targetFolder="Builds/LinuxMakefile">
+ <CONFIGURATIONS>
+ <CONFIGURATION isDebug="1" name="Debug" targetName="CHOWTapeBench"/>
+ <CONFIGURATION isDebug="0" name="Release" targetName="CHOWTapeBench"/>
+ </CONFIGURATIONS>
+ <MODULEPATHS>
+ <MODULEPATH id="juce_audio_processors" path="../Juce/modules"/>
+ <MODULEPATH id="juce_events" path="../Juce/modules"/>
+ <MODULEPATH id="juce_core" path="../Juce/modules"/>
+ <MODULEPATH id="juce_data_structures" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_extra" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_graphics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_formats" path="../Juce/modules"/>
+ </MODULEPATHS>
+ </LINUX_MAKE>
+ <VS2017 targetFolder="Builds/VisualStudio2017">
+ <CONFIGURATIONS>
+ <CONFIGURATION isDebug="1" name="Debug" targetName="CHOWTapeBench"/>
+ <CONFIGURATION isDebug="0" name="Release" targetName="CHOWTapeBench"/>
+ </CONFIGURATIONS>
+ <MODULEPATHS>
+ <MODULEPATH id="juce_audio_processors" path="../Juce/modules"/>
+ <MODULEPATH id="juce_events" path="../Juce/modules"/>
+ <MODULEPATH id="juce_core" path="../Juce/modules"/>
+ <MODULEPATH id="juce_data_structures" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_extra" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_graphics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_formats" path="../Juce/modules"/>
+ </MODULEPATHS>
+ </VS2017>
+ <XCODE_MAC targetFolder="Builds/MacOSX">
+ <CONFIGURATIONS>
+ <CONFIGURATION isDebug="1" name="Debug" targetName="CHOWTapeBench"/>
+ <CONFIGURATION isDebug="0" name="Release" targetName="CHOWTapeBench"/>
+ </CONFIGURATIONS>
+ <MODULEPATHS>
+ <MODULEPATH id="juce_audio_processors" path="../Juce/modules"/>
+ <MODULEPATH id="juce_events" path="../Juce/modules"/>
+ <MODULEPATH id="juce_core" path="../Juce/modules"/>
+ <MODULEPATH id="juce_data_structures" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_extra" path="../Juce/modules"/>
+ <MODULEPATH id="juce_gui_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_graphics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_basics" path="../Juce/modules"/>
+ <MODULEPATH id="juce_audio_formats" path="../Juce/modules"/>
+ </MODULEPATHS>
+ </XCODE_MAC>
+ </EXPORTFORMATS>
+ <MODULES>
+ <MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ <MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
+ </MODULES>
+ <LIVE_SETTINGS>
+ <WINDOWS/>
+ <LINUX/>
+ </LIVE_SETTINGS>
+</JUCERPROJECT>
diff --git a/Plugin/Bench/Source/Main.cpp b/Plugin/Bench/Source/Main.cpp
@@ -0,0 +1,193 @@
+#include <JuceHeader.h>
+#include <iostream>
+
+namespace
+{
+ constexpr double pluginSampleRate = 44100.0;
+ constexpr int samplesPerBlock = 256;
+ constexpr int numChannels = 2;
+}
+
+using namespace juce;
+
+File getRootFolder();
+void getAudioFile(AudioBuffer<float>& buffer);
+File getPluginFile();
+std::unique_ptr<AudioPluginInstance> getPlugin (String file);
+void createRandomAudioInput (AudioBuffer<float>& buffer, double lengthSeconds);
+double timeAudioProcess (AudioPluginInstance* plugin, AudioBuffer<float>& audio, const int blockSize);
+
+int main (int argc, char* argv[])
+{
+ ignoreUnused (argc, argv);
+
+ ScopedJuceInitialiser_GUI scopedJuce;
+
+ std::cout << "Loading plugin..." << std::endl;
+ auto pluginFile = getPluginFile();
+ auto plugin = getPlugin (pluginFile.getFullPathName());
+
+ if (plugin == nullptr)
+ return 1;
+
+ std::cout << "Loading audio file..." << std::endl;
+ AudioBuffer<float> audio;
+ getAudioFile (audio);
+ const double audioLength = audio.getNumSamples() / pluginSampleRate; // seconds
+ // std::cout << "Audio file is " << audioLength << " seconds" << std::endl;
+
+ std::cout << "Setting parameters..." << std::endl;
+ auto params = plugin->getParameters();
+ for (auto param : params)
+ {
+ if (param->getName (10) == "Oversampling")
+ param->setValue (3.0f / 4.0f); // 8x
+
+ if (param->getName (10) == "Mode")
+ param->setValue (3.0f / 5.0f); // NR5
+ }
+
+ // process audio
+ std::cout << "Processing audio..." << std::endl;
+ plugin->prepareToPlay (pluginSampleRate, samplesPerBlock);
+ auto time = timeAudioProcess (plugin.get(), audio, samplesPerBlock);
+ plugin->releaseResources();
+
+ // print results
+ std::cout << "Results:" << std::endl;
+ std::cout << audioLength / time << "x real-time" << std::endl;
+ std::cout << time << std::endl;
+
+ return 0;
+}
+
+std::unique_ptr<AudioPluginInstance> getPlugin (String file)
+{
+ AudioPluginFormatManager pluginManager;
+ pluginManager.addDefaultFormats();
+
+ OwnedArray<PluginDescription> plugins;
+ KnownPluginList pluginList;
+
+ // attempt to load plugin from file
+ File pluginFile;
+ if (! File::isAbsolutePath (String(file)))
+ pluginFile = File (File::getCurrentWorkingDirectory().getFullPathName() + "/" + file);
+ else
+ pluginFile = File (file);
+ pluginList.scanAndAddDragAndDroppedFiles (pluginManager, StringArray (pluginFile.getFullPathName()), plugins);
+
+ if (plugins.isEmpty()) // check if loaded
+ {
+ std::cout << "Error: unable to load plugin" << std::endl;
+ return {};
+ }
+
+ // create plugin instance
+ String error ("Unable to load plugin");
+ auto plugin = pluginManager.createPluginInstance (*plugins.getFirst(), 44100.0, 256, error);
+
+ return std::move (plugin);
+}
+
+void createRandomAudioInput (AudioBuffer<float>& buffer, double lengthSeconds)
+{
+ const int numSamples = int (lengthSeconds * pluginSampleRate);
+ buffer.setSize (numChannels, numSamples);
+
+ Random rand;
+ for (int ch = 0; ch < numChannels; ++ch)
+ {
+ auto* x = buffer.getWritePointer (ch);
+ for (int n = 0; n < numSamples; ++n)
+ {
+ x[n] = rand.nextFloat() * 2.0f - 1.0f;
+ }
+ }
+}
+
+double timeAudioProcess (AudioPluginInstance* plugin, AudioBuffer<float>& audio, const int blockSize)
+{
+ Time time;
+
+ auto totalNumSamples = audio.getNumSamples();
+ int samplePtr = 0;
+ MidiBuffer midi;
+
+ auto start = time.getMillisecondCounterHiRes();
+ while (totalNumSamples > 0)
+ {
+ auto curBlockSize = jmin (totalNumSamples, blockSize);
+ totalNumSamples -= curBlockSize;
+
+ AudioBuffer<float> curBuff (audio.getArrayOfWritePointers(), numChannels, samplePtr, curBlockSize);
+ plugin->processBlock (curBuff, midi);
+
+ samplePtr += curBlockSize;
+ }
+
+ return (time.getMillisecondCounterHiRes() - start) / 1000.0;
+}
+
+File getPluginFile()
+{
+ File rootFolder = getRootFolder();
+
+#ifdef JUCE_WINDOWS
+ File pluginFile = rootFolder.getChildFile ("Builds/VisualStudio2017/x64/Release/VST3/CHOWTapeModel.vst3");
+#endif
+
+#ifdef JUCE_MAC
+ File pluginFile = rootFolder.getChildFile ("Builds/MacOSX/build/Release/CHOWTapeModel.vst3");
+#endif
+
+#ifdef JUCE_LINUX
+ File pluginFile = rootFolder.getChildFile ("Builds/LinuxMakefile/build/CHOWTapeModel.vst3");
+#endif
+
+ // std::cout << "Plugin File: " << pluginFile.getFullPathName() << std::endl;
+
+ jassert (pluginFile.exists());
+
+ return pluginFile;
+}
+
+File getRootFolder()
+{
+ File appFile = File::getSpecialLocation (File::currentApplicationFile);
+
+#ifdef JUCE_WINDOWS
+ int numParentsToRoot = 7;
+#endif
+
+#ifdef JUCE_MAC
+ int numParentsToRoot = 6;
+#endif
+
+#ifdef JUCE_LINUX
+ int numParentsToRoot = 5;
+#endif
+
+ File rootFolder = File (appFile);
+ for (int i = 0; i < numParentsToRoot; ++i)
+ rootFolder = File (rootFolder.getParentDirectory());
+
+ // std::cout << "Root Folder: " << rootFolder.getFullPathName() << std::endl;
+
+ return rootFolder;
+}
+
+void getAudioFile(AudioBuffer<float>& buffer)
+{
+ File rootFolder = getRootFolder();
+ File audioFile = rootFolder.getParentDirectory().getChildFile ("Testing/Canada_Dry.wav");
+
+ AudioFormatManager formatManager;
+ formatManager.registerBasicFormats();
+
+ std::unique_ptr<InputStream> inputStream = audioFile.createInputStream();
+ std::unique_ptr<AudioFormatReader> reader (formatManager.createReaderFor (std::move (inputStream)));
+
+ buffer.setSize (reader->numChannels, (int) reader->lengthInSamples);
+ reader->read (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), 0, buffer.getNumSamples());
+}
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.cpp
@@ -168,12 +168,38 @@ inline double HysteresisProcessing::NR (double H, double H_d) noexcept
{
double M = M_n1;
const double last_dMdt = hysteresisFunc (M_n1, H_n1, H_d_n1);
- for (int n = 0; n < numIter; ++n)
+
+ double dMdt, dMdtPrime, deltaNR;
+ for (int n = 0; n < numIter; n += 5)
{
- const double dMdt = hysteresisFunc (M, H, H_d);
- const double dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ // loop #1
+ dMdt = hysteresisFunc (M, H, H_d);
+ dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
+ M -= deltaNR;
+
+ // loop #2
+ dMdt = hysteresisFunc (M, H, H_d);
+ dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
+ M -= deltaNR;
+
+ // loop #3
+ dMdt = hysteresisFunc (M, H, H_d);
+ dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
+ M -= deltaNR;
+
+ // loop #4
+ dMdt = hysteresisFunc (M, H, H_d);
+ dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
+ M -= deltaNR;
- const double deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
+ // loop #5
+ dMdt = hysteresisFunc (M, H, H_d);
+ dMdtPrime = hysteresisFuncPrime (H_d, dMdt);
+ deltaNR = (M - M_n1 - Talpha * (dMdt + last_dMdt)) / (1.0 - Talpha * dMdtPrime);
M -= deltaNR;
}