AnalogTapeModel

Physical modelling signal processing for analog tape recording
Log | Files | Refs | Submodules | README | LICENSE

commit 24f83b8d3bf40a2e560c7b259342f2f04e8289d5
parent 19f1a8b093c4a11bcd326b1a46bab13cb24d2f21
Author: jatinchowdhury18 <jatinchowdhury18@gmail.com>
Date:   Sun, 28 Feb 2021 22:55:48 -0800

Use std::vector instead of raw pointer arrays for FIR Filter, and fix vDSP include (#147)

* Use std::vector instead of raw pointer arrays for FIR Filter, and fix vDSP include

* {Apply clang-format}

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Diffstat:
MCHANGELOG.md | 4+++-
MPlugin/Source/Headless/Benchmarks.cpp | 2+-
APlugin/Source/Headless/FirBench.h | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPlugin/Source/Headless/Main.cpp | 5++++-
MPlugin/Source/Processors/Loss_Effects/FIRFilter.h | 41++++++++++++++++++++++-------------------
5 files changed, 119 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -2,13 +2,15 @@ All notable changes to this project will be documented in this file. - ## [Unreleased] +- Added Variance and Drift parameters for Wow control. - Fixed stability issue in tone filters. - Improved parameter names for automation menus. - Fixed GUI freezing bug in Cakewalk. - Fixed gain staging bug in Renoise. - Migrated build pipeline to CMake. +- Updated "STN" hysteresis mode to use XSIMD backend. +- Created installer packages for Windows/Mac. ## [2.7.0] - 2020-11-25 - Added new hysteresis mode: State Transition Network (STN) diff --git a/Plugin/Source/Headless/Benchmarks.cpp b/Plugin/Source/Headless/Benchmarks.cpp @@ -12,7 +12,7 @@ Benchmarks::Benchmarks() { this->commandOption = "--bench"; this->argumentDescription = "--bench --file=FILE --mode=MODE"; - this->shortDescription = "Runs benchmarks for ChowTapeModel documentation"; + this->shortDescription = "Runs benchmarks for ChowTapeModel"; this->longDescription = ""; this->command = std::bind (&Benchmarks::runBenchmarks, this, std::placeholders::_1); } diff --git a/Plugin/Source/Headless/FirBench.h b/Plugin/Source/Headless/FirBench.h @@ -0,0 +1,89 @@ +#include "../Processors/Loss_Effects/FIRFilter.h" + +namespace +{ +constexpr double pluginSampleRate = 48000.0; +constexpr int samplesPerBlock = 256; +} // namespace + +class FirBench : public ConsoleApplication::Command +{ +public: + FirBench() + { + this->commandOption = "--fir-bench"; + this->argumentDescription = "--fir-bench --size=FILTER_SIZE --length=AUDIO_LENGTH"; + this->shortDescription = "Runs benchmarks for ChowTapeModel FIR Filters"; + this->longDescription = ""; + this->command = std::bind (&FirBench::runBench, this, std::placeholders::_1); + } + + void runBench (const ArgumentList& args) + { + int firSize = 128; + if (args.containsOption ("--size")) + firSize = args.getValueForOption ("--size").getIntValue(); + + std::cout << "Creating FIR filter with size " << firSize << std::endl; + FIRFilter filter { firSize }; + setFilterCoefs (filter, (size_t) firSize); + + float audioLength = 30.0f; + if (args.containsOption ("--length")) + audioLength = args.getValueForOption ("--length").getFloatValue(); + + std::cout << "Creating audio buffer of length " << audioLength << " seconds" << std::endl; + const int numSamples = int (audioLength * pluginSampleRate); + auto buffer = createAudio (numSamples); + + std::cout << "Processing audio..." << std::endl; + filter.reset(); + auto time = timeAudioProcess (filter, buffer, samplesPerBlock); + + std::cout << "Results:" << std::endl; + std::cout << audioLength / time << "x real-time" << std::endl; + std::cout << time << " seconds" << std::endl; + } + +private: + void setFilterCoefs (FIRFilter& filter, const size_t size) + { + std::vector<float> coefs (size); + Random r; + for (size_t i = 0; i < size; ++i) + coefs[i] = r.nextFloat() * 2.0f + 1.0f; + + filter.setCoefs (coefs.data()); + } + + AudioBuffer<float> createAudio (const int numSamples) + { + AudioBuffer<float> buffer (1, numSamples); + Random r; + for (int i = 0; i < numSamples; ++i) + buffer.setSample (0, i, r.nextFloat() * 2.0f + 1.0f); + + return buffer; + } + + double timeAudioProcess (FIRFilter& filter, AudioBuffer<float>& audio, const int blockSize) + { + auto totalNumSamples = audio.getNumSamples(); + int samplePtr = 0; + + Time time; + auto start = time.getMillisecondCounterHiRes(); + while (totalNumSamples > 0) + { + auto curBlockSize = jmin (totalNumSamples, blockSize); + totalNumSamples -= curBlockSize; + + filter.process (audio.getWritePointer (0) + samplePtr, curBlockSize); + samplePtr += curBlockSize; + } + + return (time.getMillisecondCounterHiRes() - start) / 1000.0; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FirBench) +}; diff --git a/Plugin/Source/Headless/Main.cpp b/Plugin/Source/Headless/Main.cpp @@ -1,6 +1,6 @@ #include "Benchmarks.h" +#include "FirBench.h" #include "ScreenshotHelper.h" -#include <JuceHeader.h> String getVersion() { @@ -31,5 +31,8 @@ int main (int argc, char* argv[]) Benchmarks benchmarks; app.addCommand (benchmarks); + FirBench firBench; + app.addCommand (firBench); + return app.findAndRunCommand (argc, argv); } diff --git a/Plugin/Source/Processors/Loss_Effects/FIRFilter.h b/Plugin/Source/Processors/Loss_Effects/FIRFilter.h @@ -1,34 +1,37 @@ #ifndef FIRFILTER_H_INCLUDED #define FIRFILTER_H_INCLUDED -#include "JuceHeader.h" +// include <Accelerate> on Apple devices so we can use vDSP_dotpr +#if JUCE_MAC || JUCE_IOS +#define Point CarbonDummyPointName +#define Component CarbonDummyCompName +#include <Accelerate/Accelerate.h> +#undef Point +#undef Component +#endif + +#include <JuceHeader.h> #include <numeric> /** FIR filter using a double-buffer and std::inner_product */ class FIRFilter { public: - FIRFilter (int order) : order (order) - { - h = new float[order]; - z = new float[2 * order]; - } - - ~FIRFilter() + FIRFilter (int filter_order) : order ((size_t) filter_order) { - delete[] h; - delete[] z; + h.resize (order); + z.resize (2 * order); } void reset() { zPtr = 0; - FloatVectorOperations::fill (z, 0.0f, 2 * order); + FloatVectorOperations::fill (z.data(), 0.0f, 2 * (int) order); } void setCoefs (float* coefs) { - FloatVectorOperations::copy (h, coefs, order); + FloatVectorOperations::copy (h.data(), coefs, (int) order); } inline void process (float* buffer, int numSamples) @@ -40,11 +43,11 @@ public: z[zPtr] = buffer[n]; z[zPtr + order] = buffer[n]; -#ifdef JUCE_USE_VDSP_FRAMEWORK +#if JUCE_MAC || JUCE_IOS y = 0.0f; - vDSP_dotpr (z + zPtr, 1, h, 1, &y, order); // use Acclerate inner product (if available) + vDSP_dotpr (z.data() + zPtr, 1, h.data(), 1, &y, order); // use Acclerate inner product (if available) #else - y = std::inner_product (z + zPtr, z + zPtr + order, h, 0.0f); // comput inner product + y = std::inner_product (z.data() + zPtr, z.data() + zPtr + order, h.data(), 0.0f); // comput inner product #endif zPtr = (zPtr == 0 ? order - 1 : zPtr - 1); // iterate state pointer in reverse @@ -63,12 +66,12 @@ public: } protected: - float* h; - const int order; + std::vector<float> h; + const size_t order; private: - float* z; - int zPtr = 0; + std::vector<float> z; + size_t zPtr = 0; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FIRFilter) };