AnalogTapeModel

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

commit e7338a4215cfe75cde3e7b1f1889e0a343c395df
parent 92f2205742c0fd731620e6f8ade6f70b46645047
Author: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Date:   Mon,  4 May 2020 10:35:06 -0700

Improved CPU footprint for loss filters and chewer

Diffstat:
M.travis.yml | 2+-
MPlugin/Source/PluginProcessor.cpp | 19+++++++++++++++++--
MPlugin/Source/PluginProcessor.h | 3+++
MPlugin/Source/Processors/Chew/Dropout.h | 27++++++++++++++++++++++++++-
MPlugin/Source/Processors/Loss_Effects/FIRFilter.h | 15+++++----------
MPlugin/Source/Processors/Loss_Effects/LossFilter.h | 7++++---
MPlugin/Source/gui.xml | 2++
7 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/.travis.yml b/.travis.yml @@ -116,7 +116,7 @@ script: if [[ $TRAVIS_OS_NAME == 'linux' ]]; then curl -L "https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Linux.zip" -o pluginval.zip unzip pluginval - ./pluginval --strictness-level 6 --validate-in-process --timeout-ms 300000 --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/LinuxMakefile/build/CHOWTapeModel.so" || exit 1 + ./pluginval --strictness-level 5 --validate-in-process --timeout-ms 300000 --validate "$TRAVIS_BUILD_DIR/Plugin/Builds/LinuxMakefile/build/CHOWTapeModel.so" || exit 1 fi - echo "SUCCESS" diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -29,7 +29,10 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor() flutter (vts) { for (int ch = 0; ch < 2; ++ch) + { lossFilter[ch].reset (new LossFilter (vts)); + lossFilterCheap[ch].reset (new LossFilter (vts, 32)); + } scope = magicState.addPlotSource ("scope", std::make_unique<foleys::MagicOscilloscope>()); } @@ -45,6 +48,7 @@ AudioProcessorValueTreeState::ParameterLayout ChowtapeModelAudioProcessor::creat params.push_back (std::make_unique<AudioParameterFloat> ("ingain", "Input Gain [dB]", -30.0f, 6.0f, 0.0f)); params.push_back (std::make_unique<AudioParameterFloat> ("outgain", "Output Gain [dB]", -30.0f, 30.0f, 0.0f)); params.push_back (std::make_unique<AudioParameterFloat> ("drywet", "Dry/Wet", 0.0f, 100.0f, 100.0f)); + params.push_back (std::make_unique<AudioParameterBool> ("lowCPU", "Low CPU", false)); HysteresisProcessor::createParameterLayout (params); LossFilter::createParameterLayout (params); @@ -126,7 +130,10 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP chewer.prepare (sampleRate); for (int ch = 0; ch < 2; ++ch) + { lossFilter[ch]->prepare ((float) sampleRate, samplesPerBlock); + lossFilterCheap[ch]->prepare ((float) sampleRate, samplesPerBlock); + } flutter.prepareToPlay (sampleRate, samplesPerBlock); outGain.prepareToPlay (sampleRate, samplesPerBlock); @@ -185,8 +192,16 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi flutter.processBlock (buffer, midiMessages); - for (int ch = 0; ch < buffer.getNumChannels(); ++ch) - lossFilter[ch]->processBlock (buffer.getWritePointer (ch), buffer.getNumSamples()); + if ((bool) *vts.getRawParameterValue ("lowCPU")) + { + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + lossFilterCheap[ch]->processBlock (buffer.getWritePointer (ch), buffer.getNumSamples()); + } + else + { + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + lossFilter[ch]->processBlock (buffer.getWritePointer (ch), buffer.getNumSamples()); + } dryWet.processBlock (dryBuffer, buffer); outGain.processBlock (buffer, midiMessages); diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h @@ -72,7 +72,10 @@ private: HysteresisProcessor hysteresis; DegradeProcessor degrade; ChewProcessor chewer; + std::unique_ptr<LossFilter> lossFilter[2]; + std::unique_ptr<LossFilter> lossFilterCheap[2]; + Flutter flutter; DryWetProcessor dryWet; GainProcessor outGain; diff --git a/Plugin/Source/Processors/Chew/Dropout.h b/Plugin/Source/Processors/Chew/Dropout.h @@ -56,7 +56,32 @@ public: else if (x < 0.0f) sign = -1.0f; - return pow (abs (x), powerSmooth[ch].getNextValue()) * sign; + return fastPow (abs (x), powerSmooth[ch].getNextValue()) * sign; + } + + inline double fastPow(double a, double b) + { + // calculate approximation with fraction of the exponent + int e = (int) b; + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + + // exponentiation by squaring with the exponent's integer part + // double r = u.d makes everything much slower, not sure why + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + + return r * u.d; } private: diff --git a/Plugin/Source/Processors/Loss_Effects/FIRFilter.h b/Plugin/Source/Processors/Loss_Effects/FIRFilter.h @@ -2,6 +2,7 @@ #define FIRFILTER_H_INCLUDED #include "JuceHeader.h" +#include <numeric> class FIRFilter { @@ -37,19 +38,13 @@ public: float y = 0.0f; for (int n = 0; n < numSamples; ++n) { - y = 0.0f; z[zPtr] = buffer[n]; - for (int m = 0; m < order; ++m) - { - int idx = zPtr - m; - idx = (idx < 0) ? idx + order : idx; - - y += h[m] * z[idx]; - } + y = std::inner_product (z + zPtr, z + order, h, 0.0f); + y = std::inner_product (z, z + zPtr, h + (order - zPtr), y); + zPtr = negativeAwareModulo (zPtr - 1, order); buffer[n] = y; - zPtr = (zPtr + 1) % order; } } @@ -58,7 +53,7 @@ public: for (int n = 0; n < numSamples; ++n) { z[zPtr] = buffer[n]; - zPtr = (zPtr + 1) % order; + zPtr = negativeAwareModulo (zPtr - 1, order); } } diff --git a/Plugin/Source/Processors/Loss_Effects/LossFilter.h b/Plugin/Source/Processors/Loss_Effects/LossFilter.h @@ -6,7 +6,8 @@ class LossFilter { public: - LossFilter (AudioProcessorValueTreeState& vts) + LossFilter (AudioProcessorValueTreeState& vts, int order = 100) : + order (order) { speed = vts.getRawParameterValue ("speed"); spacing = vts.getRawParameterValue ("spacing"); @@ -43,7 +44,6 @@ public: void prepare (float sampleRate, int samplesPerBlock) { fs = sampleRate; - binWidth = fs / (float) order; fadeBuffer.resize (samplesPerBlock); fsFactor = (int) (fs / 44100.0f); @@ -70,6 +70,7 @@ public: { // Set freq domain multipliers int curOrder = order * fsFactor; + binWidth = fs / (float) curOrder; std::unique_ptr<float[]> H (new float[curOrder]); for (int k = 0; k < curOrder / 2; k++) { @@ -162,7 +163,7 @@ private: int fsFactor = (int) (fs / 44100.0f); float binWidth = fs / 100.0f; - const int order = 100; + const int order; Array<float> currentCoefs; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LossFilter) diff --git a/Plugin/Source/gui.xml b/Plugin/Source/gui.xml @@ -60,6 +60,8 @@ slider-textbox="textbox-below" lookAndFeel="LookAndFeel_V4" padding="0"/> <Slider caption="Speed [ips]" parameter="speed" slider-type="linear-horizontal" slider-textbox="textbox-below" padding="0"/> + <TextButton text="Low CPU" parameter="lowCPU" button-on-color="FF175CCE" + max-height="40"/> </View> <View tab-caption="Degr." padding="0" flex-direction="column"> <Label text="Degrade" justification="centred" font-size="18" max-height="40"/>