commit e64ee92001449b9595d6847405aaa2f2621a18b5
parent 6d2a46aa1be134f957422d5283ea7839081ae063
Author: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Date: Sun, 24 Feb 2019 21:29:59 -0800
Add timing effects to plugin
Diffstat:
11 files changed, 322 insertions(+), 6 deletions(-)
diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer
@@ -14,12 +14,21 @@
<FILE id="S8Xybc" name="MainControls.cpp" compile="1" resource="0"
file="Source/GUI Components/MainControls.cpp"/>
<FILE id="zXwghi" name="MainControls.h" compile="0" resource="0" file="Source/GUI Components/MainControls.h"/>
+ <FILE id="E0NC0D" name="TimingControls.cpp" compile="1" resource="0"
+ file="Source/GUI Components/TimingControls.cpp"/>
+ <FILE id="vA4R0k" name="TimingControls.h" compile="0" resource="0"
+ file="Source/GUI Components/TimingControls.h"/>
</GROUP>
<GROUP id="{7867F016-80C7-7749-B604-ED042C040FC7}" name="GUI Extras">
<FILE id="xd7cQs" name="ChowSlider.h" compile="0" resource="0" file="Source/GUI Extras/ChowSlider.h"/>
<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="{6480D4F8-23CE-4932-1E51-CF3CF4FCC37F}" name="Timing Effects">
+ <FILE id="sF9JlS" name="TimingEffect.cpp" compile="1" resource="0"
+ file="Source/Processors/Timing Effects/TimingEffect.cpp"/>
+ <FILE id="vDhRoW" name="TimingEffect.h" compile="0" resource="0" file="Source/Processors/Timing Effects/TimingEffect.h"/>
+ </GROUP>
<GROUP id="{99069F32-1399-FF98-7C4A-19F8E855C2AA}" name="Loss Effects">
<FILE id="aEdcW7" name="LossEffects.cpp" compile="1" resource="0" file="Source/Processors/Loss Effects/LossEffects.cpp"/>
<FILE id="TFnfhT" name="LossEffects.h" compile="0" resource="0" file="Source/Processors/Loss Effects/LossEffects.h"/>
diff --git a/Plugin/Source/GUI Components/TimingControls.cpp b/Plugin/Source/GUI Components/TimingControls.cpp
@@ -0,0 +1,45 @@
+#include "TimingControls.h"
+#include "../PluginEditor.h"
+
+TimingControls::TimingControls (ChowtapeModelAudioProcessor& proc) :
+ processor (proc)
+{
+ ChowtapeModelAudioProcessorEditor::createSlider (flutterDepthSlide, processor.flutterDepth, myLNF, this);
+ flutterDepthSlide.setSkewFactorFromMidPoint (1.0);
+
+ ChowtapeModelAudioProcessorEditor::createLabel (flutterDepthLabel, processor.flutterDepth, this);
+}
+
+void TimingControls::paint (Graphics& g)
+{
+ g.setColour (Colours::antiquewhite);
+ g.setFont (Font ((float) nameHeight).boldened());
+
+ g.drawFittedText ("Timing Parameters:", Rectangle<int> (xOffset, yOffset, width, labelHeight),
+ Justification::centredLeft, 1);
+}
+
+void TimingControls::resized()
+{
+ flutterDepthLabel.setBounds (0, 2 * yOffset + labelY, sliderWidth, labelHeight);
+ flutterDepthSlide.setBounds (0, 2 * yOffset + sliderY, sliderWidth, sliderWidth);
+}
+
+void TimingControls::sliderValueChanged (Slider* slider)
+{
+ if (AudioParameterFloat* param = ChowtapeModelAudioProcessorEditor::getParamForSlider (slider, processor))
+ *param = (float) slider->getValue();
+}
+
+void TimingControls::sliderDragStarted(Slider* slider)
+{
+ if (AudioParameterFloat* param = ChowtapeModelAudioProcessorEditor::getParamForSlider (slider, processor))
+ param->beginChangeGesture();
+}
+
+void TimingControls::sliderDragEnded(Slider* slider)
+{
+ if (AudioParameterFloat* param = ChowtapeModelAudioProcessorEditor::getParamForSlider (slider, processor))
+ param->endChangeGesture();
+}
+
diff --git a/Plugin/Source/GUI Components/TimingControls.h b/Plugin/Source/GUI Components/TimingControls.h
@@ -0,0 +1,32 @@
+#ifndef TIMINGCONTROLS_H_INCLUDED
+#define TIMINGCONTROLS_H_INCLUDED
+
+#include "../PluginProcessor.h"
+#include "../GUI Extras/ChowSlider.h"
+#include "../GUI Extras/MyLNF.h"
+
+class TimingControls : public Component,
+ public Slider::Listener
+{
+public:
+ TimingControls (ChowtapeModelAudioProcessor& proc);
+
+ void paint (Graphics&) override;
+ void resized() override;
+
+private:
+ void sliderValueChanged (Slider* slider) override;
+ void sliderDragStarted (Slider* slider) override;
+ void sliderDragEnded (Slider* slider) override;
+
+ MyLNF myLNF;
+
+ ChowtapeModelAudioProcessor& processor;
+
+ ChowSlider flutterDepthSlide;
+ Label flutterDepthLabel;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimingControls)
+};
+
+#endif //TIMINGCONTROLS_H_INCLUDED
diff --git a/Plugin/Source/PluginEditor.cpp b/Plugin/Source/PluginEditor.cpp
@@ -14,6 +14,9 @@ ChowtapeModelAudioProcessorEditor::ChowtapeModelAudioProcessorEditor (ChowtapeMo
lossControls.reset (new LossControls (processor));
addAndMakeVisible (lossControls.get());
+ timingControls.reset (new TimingControls (processor));
+ addAndMakeVisible (timingControls.get());
+
setSize (width, height);
}
@@ -80,6 +83,7 @@ void ChowtapeModelAudioProcessorEditor::paint (Graphics& g)
g.drawHorizontalLine (sectionHeight, 0, width);
g.drawHorizontalLine (2 * sectionHeight, 0, width);
+ g.drawHorizontalLine (3 * sectionHeight, 0, width);
}
void ChowtapeModelAudioProcessorEditor::resized()
@@ -87,6 +91,7 @@ void ChowtapeModelAudioProcessorEditor::resized()
mainControls->setBounds (0, 0, width, sectionHeight);
biasControls->setBounds (0, sectionHeight, width, sectionHeight);
lossControls->setBounds (0, 2 * sectionHeight, width, sectionHeight);
+ timingControls->setBounds (0, 3 * sectionHeight, width, sectionHeight);
}
AudioParameterFloat* ChowtapeModelAudioProcessorEditor::getParamForSlider (Slider* slider, ChowtapeModelAudioProcessor& proc)
@@ -105,6 +110,8 @@ AudioParameterFloat* ChowtapeModelAudioProcessorEditor::getParamForSlider (Slide
return proc.tapeThickness;
else if (proc.gapWidth->name == slider->getName())
return proc.gapWidth;
+ else if (proc.flutterDepth->name == slider->getName())
+ return proc.flutterDepth;
else
return nullptr;
}
diff --git a/Plugin/Source/PluginEditor.h b/Plugin/Source/PluginEditor.h
@@ -6,12 +6,13 @@
#include "GUI Components/BiasControls.h"
#include "GUI Components/MainControls.h"
#include "GUI Components/LossControls.h"
+#include "GUI Components/TimingControls.h"
enum
{
width = 375,
sectionHeight = 150,
- height = 3 * sectionHeight,
+ height = 4 * sectionHeight,
nameHeight = 20,
@@ -57,6 +58,7 @@ private:
std::unique_ptr<MainControls> mainControls;
std::unique_ptr<BiasControls> biasControls;
std::unique_ptr<LossControls> lossControls;
+ std::unique_ptr<TimingControls> timingControls;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowtapeModelAudioProcessorEditor)
};
diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp
@@ -50,9 +50,14 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor()
addParameter (gapWidth = new AudioParameterFloat (String ("gapWidth"), String ("Gap Width"), 2.5f, 12.0f, 3.0f));
gapWidth->addListener (this);
+ //Timing Controls
+ addParameter (flutterDepth = new AudioParameterFloat (String ("flutterDepth"), String ("Flutter Depth"), 0.0f, 5.0f, 1.0f));
+ flutterDepth->addListener (this);
+
lossEffects.setSpeed (*tapeSpeed);
hysteresis.setOverSamplingFactor (*overSampling);
hysteresis.setBiasFreq (*biasFreq);
+ timingEffect.setDepth (*flutterDepth);
}
ChowtapeModelAudioProcessor::~ChowtapeModelAudioProcessor()
@@ -68,7 +73,10 @@ void ChowtapeModelAudioProcessor::parameterValueChanged (int paramIndex, float n
else if (paramIndex == overSampling->getParameterIndex())
hysteresis.setOverSamplingFactor (*overSampling);
else if (paramIndex == tapeSpeed->getParameterIndex())
+ {
lossEffects.setSpeed (*tapeSpeed);
+ timingEffect.setTapeSpeed (*tapeSpeed);
+ }
else if (paramIndex == tapeType->getParameterIndex())
return; //@TODO
else if (paramIndex == biasFreq->getParameterIndex())
@@ -81,6 +89,8 @@ void ChowtapeModelAudioProcessor::parameterValueChanged (int paramIndex, float n
lossEffects.setThickness (tapeThickness->convertFrom0to1 (newValue));
else if (paramIndex == gapWidth->getParameterIndex())
lossEffects.setGap (gapWidth->convertFrom0to1 (newValue));
+ else if (paramIndex == flutterDepth->getParameterIndex())
+ timingEffect.setDepth (flutterDepth->convertFrom0to1 (newValue));
}
//==============================================================================
@@ -150,6 +160,7 @@ void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesP
{
inGainProc.prepareToPlay (sampleRate, samplesPerBlock);
hysteresis.prepareToPlay (sampleRate, samplesPerBlock);
+ timingEffect.prepareToPlay (sampleRate, samplesPerBlock);
lossEffects.prepareToPlay (sampleRate, samplesPerBlock);
outGainProc.prepareToPlay (sampleRate, samplesPerBlock);
}
@@ -158,6 +169,7 @@ void ChowtapeModelAudioProcessor::releaseResources()
{
inGainProc.releaseResources();
hysteresis.releaseResources();
+ timingEffect.releaseResources();
lossEffects.releaseResources();
outGainProc.releaseResources();
}
@@ -194,6 +206,8 @@ void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi
hysteresis.processBlock (buffer, midiMessages);
+ timingEffect.processBlock (buffer, midiMessages);
+
lossEffects.processBlock (buffer, midiMessages);
outGainProc.processBlock (buffer, midiMessages);
diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h
@@ -4,6 +4,7 @@
#include "Processors/Hysteresis/HysteresisProcessor.h"
#include "Processors/GainProcessor.h"
#include "Processors/Loss Effects/LossEffectsFilter.h"
+#include "Processors/Timing Effects/TimingEffect.h"
//==============================================================================
/**
@@ -62,12 +63,15 @@ public:
AudioParameterFloat* tapeThickness;
AudioParameterFloat* gapWidth;
+ AudioParameterFloat* flutterDepth;
+
void parameterValueChanged (int paramIndex, float newValue) override;
void parameterGestureChanged (int /*paramIndex*/, bool /*gestureIsStarting*/) override {}
private:
HysteresisProcessor hysteresis;
LossEffectsFilter lossEffects;
+ TimingEffect timingEffect;
GainProcessor inGainProc;
GainProcessor outGainProc;
diff --git a/Plugin/Source/Processors/Timing Effects/TimingEffect.cpp b/Plugin/Source/Processors/Timing Effects/TimingEffect.cpp
@@ -0,0 +1,145 @@
+#include "TimingEffect.h"
+
+void TimingEffect::DelayChannel::setReadPtr (int maxLength)
+{
+ readPtr++;
+ if (readPtr >= maxLength) //wrap
+ readPtr = 0;
+}
+
+TimingEffect::TimingEffect() : ProcessorBase (String ("Timing Processor"))
+{
+ delayBuffer.setSize (2, maxDelaySamples);
+}
+
+double TimingEffect::getTailLengthSeconds() const
+{
+ int maxLengthSamples = 0;
+ for (auto& ch : dChannels)
+ maxLengthSamples = jmax ((int) ch.length.getTargetValue(), maxLengthSamples);
+
+ return (double) maxLengthSamples / getSampleRate();
+}
+
+void TimingEffect::setLength (int channel, int lengthSamples)
+{
+ int newLength = jmax (0, jmin (lengthSamples, (int) maxDelaySamples));
+ dChannels[channel].length.setValue ((float) newLength);
+}
+
+int TimingEffect::getLength (int channel) const
+{
+ return roundToInt (dChannels[channel].length.getTargetValue());
+}
+
+void TimingEffect::setDepth (float newDepth)
+{
+ setLength (0, roundToInt (newDepth * dChannels[0].length.getTargetValue() / depth));
+ setLength (1, roundToInt (newDepth * dChannels[1].length.getTargetValue() / depth));
+
+ depth = newDepth;
+}
+
+void TimingEffect::prepareToPlay (double sampleRate, int maximumExpectedSamplesPerBlock)
+{
+ setRateAndBufferSizeDetails (sampleRate, maximumExpectedSamplesPerBlock);
+
+ delayBuffer.clear();
+
+ resetDelay();
+}
+
+void TimingEffect::releaseResources()
+{
+ delayBuffer.clear();
+
+ for (auto& ch : dChannels)
+ ch.resetPtrs();
+}
+
+void TimingEffect::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midiBuffer*/)
+{
+ countFreq += buffer.getNumSamples();
+
+ if (n >= periodLen)
+ n = 0;
+
+ if (countFreq > numSamplesFreq)
+ {
+ countFreq = 0;
+ n++;
+
+ updateDelay();
+ }
+
+ for (int channel = 0; channel < buffer.getNumChannels(); channel++)
+ {
+ float* x = buffer.getWritePointer(channel);
+ for (int samp = 0; samp < buffer.getNumSamples(); samp++)
+ x[samp] = delay (channel, x[samp]);
+ }
+}
+
+float TimingEffect::delay (int channel, float x)
+{
+ auto& ch = dChannels[channel];
+
+ // Erase head
+ const int erasePtr = negativeAwareModulo(ch.readPtr - 1, (int) maxDelaySamples);
+ delayBuffer.setSample (channel, erasePtr, 0.0f);
+
+ // Write head
+ const float len = ch.length.getNextValue();
+ const float fractionSample = len - (int) len;
+ const int writePtr = (ch.readPtr + (int) floorf (len)) % (int) maxDelaySamples;
+
+ delayBuffer.addSample (channel, writePtr, x * (1.0f - fractionSample));
+ delayBuffer.addSample (channel, (writePtr + 1) % (int) maxDelaySamples, x * fractionSample);
+
+ // Read head
+ float y = delayBuffer.getSample (channel, ch.readPtr);
+
+ //update pointers
+ ch.setReadPtr ((int) maxDelaySamples);
+
+ return y;
+}
+
+void TimingEffect::updateDelay()
+{
+ float t = 0;
+
+ for (int i = 0; i < polyOrder+1; i++)
+ t += coefs[i] * (float) pow (n, i);
+
+ setLength (0, roundToInt (depth * t));
+ setLength (1, roundToInt (depth * t));
+}
+
+void TimingEffect::resetDelay()
+{
+ n = 0;
+ countFreq = 0;
+ numSamplesFreq = roundToInt<double> ((1.0f / freq) * getSampleRate());
+
+ for (auto& ch : dChannels)
+ ch.length.reset (getSampleRate(), 1.0 / freq); //(1 / freq) seconds to interpolate
+}
+
+void TimingEffect::setTapeSpeed (String tapeSpeed)
+{
+ auto newFreq = freq;
+
+ if (tapeSpeed == "3.75 ips")
+ newFreq = 5.0f;
+ else if (tapeSpeed == "7.5 ips")
+ newFreq = 10.0f;
+ else if (tapeSpeed == "15 ips")
+ newFreq = 20.0f;
+
+ if (newFreq != freq)
+ {
+ freq = newFreq;
+ resetDelay();
+ }
+}
diff --git a/Plugin/Source/Processors/Timing Effects/TimingEffect.h b/Plugin/Source/Processors/Timing Effects/TimingEffect.h
@@ -0,0 +1,62 @@
+#ifndef TIMINGEFFECT_H_INCLUDED
+#define TIMINGEFFECT_H_INCLUDED
+
+#include "../ProcessorBase.h"
+
+class TimingEffect : public ProcessorBase
+{
+public:
+ TimingEffect();
+
+ void prepareToPlay (double sampleRate, int maxExpectedBlockSize) override;
+ void releaseResources() override;
+ void processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midiBuffer*/) override;
+
+ void setLength (int channel, int length);
+ int getLength (int channel) const;
+
+ void setDepth (float newDepth);
+ void setTapeSpeed (String tapeSpeed);
+
+ double getTailLengthSeconds() const override;
+
+private:
+ enum
+ {
+ maxDelaySamples = 600 * 10,
+ polyOrder = 10,
+ periodLen = 490,
+ };
+
+ struct DelayChannel
+ {
+ LinearSmoothedValue<float> length = 0.0f;
+ int readPtr = 0;
+
+ void resetPtrs() { readPtr = 0; }
+ void setReadPtr (int maxLength);
+ };
+
+ DelayChannel dChannels[2];
+
+ AudioBuffer<float> delayBuffer;
+
+ float delay (int channel, float x);
+ void updateDelay();
+ void resetDelay();
+
+ float coefs[polyOrder+1] = { 5.06485313f, 2.45320771f, (float) 5.90645745e-2,
+ (float) -3.05750415e-4, (float) -1.16380361e-5, (float) 1.95163044e-7,
+ (float) -1.36154581e-9, (float) 5.11840498e-12, (float) -1.08010214e-14,
+ (float) 1.20503554e-17, (float) -5.53696539e-21 };
+ int n = 0;
+ int numSamplesFreq = 0;
+ int countFreq = 0;
+
+ float freq = 10.0f;
+ float depth = 1.0f;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimingEffect)
+};
+
+#endif //TIMINGEFFECT_H_INCLUDED
diff --git a/Simulations/TimingEffects/AnalyzeTiming.py b/Simulations/TimingEffects/AnalyzeTiming.py
@@ -104,6 +104,7 @@ t = np.arange (len (diffs_pos))
p = np.polyfit (t, diffs_pos, 10)
print (p)
+print (len (diffs_pos))
y = np.poly1d (p)
diff --git a/Simulations/TimingEffects/test.py b/Simulations/TimingEffects/test.py
@@ -1,5 +0,0 @@
-x = [1, 2, 3]
-x2 = [3, 2, 1]
-
-x3 = x + x2
-print (x3)