commit 6a5341b95d9de70377b260f9a35a296a068e8796
parent 28ebd57f8613a0e94becb08bc4b410ed1e0a596d
Author: jatinchowdhury18 <[email protected]>
Date: Mon, 11 Feb 2019 15:02:42 -0800
add tape speed effects
Diffstat:
5 files changed, 177 insertions(+), 0 deletions(-)
diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer
@@ -8,6 +8,12 @@
<FILE id="mQFlPP" name="MyLNF.h" compile="0" resource="0" file="Source/GUI Extras/MyLNF.h"/>
</GROUP>
<GROUP id="{D2422983-A0E9-6A14-2092-2381CB1F3E7F}" name="Processors">
+ <GROUP id="{E3FB6B88-F1C2-FAF4-BE48-F8C36FEC858A}" name="Speed Filters">
+ <FILE id="rJ6Je0" name="SpeedFilterProcessor.cpp" compile="1" resource="0"
+ file="Source/Processors/Speed Filters/SpeedFilterProcessor.cpp"/>
+ <FILE id="vidpxP" name="SpeedFilterProcessor.h" compile="0" resource="0"
+ file="Source/Processors/Speed Filters/SpeedFilterProcessor.h"/>
+ </GROUP>
<GROUP id="{E8FF20D6-35DC-19EF-2A0D-F8363CDBE1AE}" name="Hysteresis">
<FILE id="sjUZ6l" name="HysteresisProcessing.cpp" compile="1" resource="0"
file="Source/Processors/Hysteresis/HysteresisProcessing.cpp"/>
diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp
@@ -22,9 +22,14 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor()
addParameter (overSampling = new AudioParameterChoice (String ("overSampling"), String ("Upsample"),
StringArray ({ "2x", "4x", "8x" }), 0));
+ overSampling->addListener (this);
addParameter (tapeSpeed = new AudioParameterChoice (String ("tapeSpeed"), String ("Speed"),
StringArray ({ "3.75 ips", "7.5 ips", "15 ips" }), 1));
+ tapeSpeed->addListener (this);
+
+ speedFilter.setSpeed (*tapeSpeed);
+ hysteresis.setOverSamplingFactor (*overSampling);
}
ChowtapeModelAudioProcessor::~ChowtapeModelAudioProcessor()
@@ -39,6 +44,8 @@ void ChowtapeModelAudioProcessor::parameterValueChanged (int paramIndex, float n
outGainProc.setGain (Decibels::decibelsToGain (outGain->convertFrom0to1 (newValue)));
else if (paramIndex == overSampling->getParameterIndex())
hysteresis.setOverSamplingFactor (*overSampling);
+ else if (paramIndex == tapeSpeed->getParameterIndex())
+ speedFilter.setSpeed (*tapeSpeed);
}
//==============================================================================
@@ -107,6 +114,7 @@ void ChowtapeModelAudioProcessor::changeProgramName (int /*index*/, const String
void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
inGainProc.prepareToPlay (sampleRate, samplesPerBlock);
+ speedFilter.prepareToPlay (sampleRate, samplesPerBlock);
hysteresis.prepareToPlay (sampleRate, samplesPerBlock);
outGainProc.prepareToPlay (sampleRate, samplesPerBlock);
}
@@ -114,6 +122,7 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP
void ChowtapeModelAudioProcessor::releaseResources()
{
inGainProc.releaseResources();
+ speedFilter.releaseResources();
hysteresis.releaseResources();
outGainProc.releaseResources();
}
@@ -147,6 +156,7 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi
ScopedNoDenormals noDenormals;
inGainProc.processBlock (buffer, midiMessages);
+ speedFilter.processBlock (buffer, midiMessages);
hysteresis.processBlock (buffer, midiMessages);
outGainProc.processBlock (buffer, midiMessages);
diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h
@@ -3,6 +3,7 @@
#include "../JuceLibraryCode/JuceHeader.h"
#include "Processors/Hysteresis/HysteresisProcessor.h"
#include "Processors/GainProcessor.h"
+#include "Processors/Speed Filters/SpeedFilterProcessor.h"
//==============================================================================
/**
@@ -57,6 +58,7 @@ public:
void parameterGestureChanged (int /*paramIndex*/, bool /*gestureIsStarting*/) override {}
private:
+ SpeedFilterProcessor speedFilter;
HysteresisProcessor hysteresis;
GainProcessor inGainProc;
diff --git a/Plugin/Source/Processors/Speed Filters/SpeedFilterProcessor.cpp b/Plugin/Source/Processors/Speed Filters/SpeedFilterProcessor.cpp
@@ -0,0 +1,112 @@
+#include "SpeedFilterProcessor.h"
+
+void SpeedFilterProcessor::initFilter()
+{
+ resetArrays();
+ calcCoefs();
+ clearIOs();
+}
+
+void SpeedFilterProcessor::resetArrays()
+{
+ a.reset (new float[order + 1]);
+ b.reset (new float[order + 1]);
+
+ const int numChannels = 2; //getTotalNumOutputChannels();
+ filterIOs.reset (new std::unique_ptr<float[]> [numChannels]);
+ for (int ch = 0; ch < numChannels; ch++)
+ filterIOs[ch].reset (new float[order * numChannels]);
+}
+
+void SpeedFilterProcessor::clearIOs()
+{
+ for (int ch = 0; ch < 2 /*getTotalNumOutputChannels()*/; ch++)
+ for (int samp = 0; samp < 4; samp++)
+ filterIOs[ch][samp] = 0.0f;
+}
+
+void SpeedFilterProcessor::calcCoefs()
+{
+ float alpha = tanf (MathConstants<float>::pi * cutoffFreq / (float) getSampleRate());
+ float alphaSquared = alpha * alpha;
+ float K = alphaSquared + alpha / Q + 1.0f;
+
+ // b0, b1, b2 for setCoefs()
+ float coef0 = alphaSquared * gain / K;
+ float bs[] = { coef0, // *x0
+ 2.0f * coef0, // *x1
+ coef0, // *x2
+ };
+
+ // a0, a1, a2 for setCoefs()
+ float coef1 = (2.0f * (alphaSquared - 1.0f)) / K;
+ float coef2 = (alphaSquared - alpha/Q + 1.0f) / K;
+ float as[] = { 1.0f, // *y0 (usually 1)
+ coef1, // *y1
+ coef2 // *y2
+ };
+
+ setCoefs(as, bs);
+}
+
+void SpeedFilterProcessor::setCoefs (float* as, float* bs)
+{
+ for (int i = 0; i < order + 1; i++)
+ {
+ a[i] = as[i];
+ b[i] = bs[i];
+ }
+}
+
+void SpeedFilterProcessor::prepareToPlay (double sampleRate, int maxExpectedBlockSize)
+{
+ setRateAndBufferSizeDetails (sampleRate, maxExpectedBlockSize);
+ initFilter();
+}
+
+void SpeedFilterProcessor::releaseResources()
+{
+ clearIOs();
+}
+
+void SpeedFilterProcessor::setSpeed (String speed)
+{
+ if (speed == "3.75 ips")
+ setFreq (12000);
+ else if (speed == "7.5 ips")
+ setFreq (15000);
+ else if (speed == "15 ips")
+ setFreq (18000);
+}
+
+void SpeedFilterProcessor::setFreq (float newFreq)
+{
+ cutoffFreq = newFreq;
+ calcCoefs();
+}
+
+void SpeedFilterProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midiBuffer*/)
+{
+ for (int channel = 0; channel < buffer.getNumChannels(); channel++)
+ {
+ float* x = buffer.getWritePointer(channel);
+ for (int n = 0; n < buffer.getNumSamples(); n++)
+ x[n] = filter (channel, x[n]);
+ }
+}
+
+float SpeedFilterProcessor::filter (int ch, float x)
+{
+ float y = b[0] * x + b[1] * filterIOs[ch][0] + b[2] * filterIOs[ch][1]
+ - a[1] * filterIOs[ch][2] - a[2] * filterIOs[ch][3];
+
+ //update I/O's
+ filterIOs[ch][3] = filterIOs[ch][2]; //y2 = y1
+ filterIOs[ch][1] = filterIOs[ch][0]; //x2 = x1
+ filterIOs[ch][2] = y; //y1 = y
+ filterIOs[ch][0] = x; //x1 = x
+
+ jassert (abs(y) < 1.0f);
+
+ return y;
+}
diff --git a/Plugin/Source/Processors/Speed Filters/SpeedFilterProcessor.h b/Plugin/Source/Processors/Speed Filters/SpeedFilterProcessor.h
@@ -0,0 +1,47 @@
+#ifndef SPEEDFILTERPROCESSOR_H_INCLUDED
+#define SPEEDFILTERPROCESSOR_H_INCLUDED
+
+#include "../ProcessorBase.h"
+
+class SpeedFilterProcessor : public ProcessorBase
+{
+public:
+ enum
+ {
+ order = 2,
+ maxFreq = 22000,
+ };
+
+ SpeedFilterProcessor() : ProcessorBase (String ("Filter Processor")) { initFilter(); }
+
+ void prepareToPlay (double sampleRate, int maxExpectedBlockSize) override;
+ void releaseResources() override;
+ void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiBuffer) override;
+
+ void setSpeed (String speed);
+
+ void setFreq (float newFreq);
+ float getFreq() const { return cutoffFreq; }
+
+private:
+ void initFilter();
+ void clearIOs();
+ void resetArrays();
+
+ void calcCoefs();
+ void setCoefs (float* as, float* bs);
+
+ float filter (int channel, float x);
+
+ float cutoffFreq = (float) maxFreq;
+ const float Q = 0.707f;
+ const float gain = 1.0f;
+
+ std::unique_ptr<float[]> a; // a0, a1, a2 (a0 is usually 1)
+ std::unique_ptr<float[]> b; // b0, b1, b2
+ std::unique_ptr<std::unique_ptr<float[]> []> filterIOs; //x1, x2, y1, y2
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpeedFilterProcessor)
+};
+
+#endif // !SPEEDFILTERPROCESSOR_H_INCLUDED