commit 7e88326dcabccbfc702d942a58e510fabf647302
parent f4e01fbc061c7f9403e42bb3f2c92bdd24662cb5
Author: jatinchowdhury18 <[email protected]>
Date: Sat, 9 Feb 2019 19:56:48 -0800
GUI updates and processor updates
Diffstat:
14 files changed, 463 insertions(+), 177 deletions(-)
diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer
@@ -4,11 +4,22 @@
pluginFormats="buildAU,buildStandalone,buildVST,buildVST3">
<MAINGROUP id="oKBSK1" name="CHOWTapeModel">
<GROUP id="{7659EE80-6477-48A0-C7B6-CD8900735F10}" name="Source">
+ <GROUP id="{7867F016-80C7-7749-B604-ED042C040FC7}" name="GUI Extras">
+ <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">
- <FILE id="SSdMYR" name="HysteresisProcessing.cpp" compile="1" resource="0"
- file="Source/Processors/HysteresisProcessing.cpp"/>
- <FILE id="vvuNZS" name="HysteresisProcessing.h" compile="0" resource="0"
- file="Source/Processors/HysteresisProcessing.h"/>
+ <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"/>
+ <FILE id="DrklCU" name="HysteresisProcessing.h" compile="0" resource="0"
+ file="Source/Processors/Hysteresis/HysteresisProcessing.h"/>
+ <FILE id="S8e9Mq" name="HysteresisProcessor.cpp" compile="1" resource="0"
+ file="Source/Processors/Hysteresis/HysteresisProcessor.cpp"/>
+ <FILE id="zCtaDA" name="HysteresisProcessor.h" compile="0" resource="0"
+ file="Source/Processors/Hysteresis/HysteresisProcessor.h"/>
+ </GROUP>
+ <FILE id="kdFIkd" name="GainProcessor.h" compile="0" resource="0" file="Source/Processors/GainProcessor.h"/>
+ <FILE id="eGTZ2w" name="ProcessorBase.h" compile="0" resource="0" file="Source/Processors/ProcessorBase.h"/>
</GROUP>
<FILE id="y330sp" name="PluginProcessor.cpp" compile="1" resource="0"
file="Source/PluginProcessor.cpp"/>
diff --git a/Plugin/Source/GUI Extras/MyLNF.h b/Plugin/Source/GUI Extras/MyLNF.h
@@ -0,0 +1,48 @@
+#ifndef MYLNF_H_INCLUDED
+#define MYLNF_H_INCLUDED
+
+#include "JuceHeader.h"
+
+class MyLNF : public LookAndFeel_V4
+{
+public:
+ MyLNF()
+ {
+ setColour (ComboBox::outlineColourId, Colours::darkorange);
+ }
+
+ Font getTextButtonFont (TextButton& button, int buttonHeight) override
+ {
+ return LookAndFeel_V4::getTextButtonFont (button, buttonHeight).boldened();
+ }
+
+ void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
+ float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
+ {
+ auto outline = slider.findColour (Slider::rotarySliderOutlineColourId);
+ auto fill = slider.isEnabled() ? slider.findColour (Slider::rotarySliderFillColourId) : Colours::grey;
+
+ auto bounds = Rectangle<int> (x, y, width, height).toFloat().reduced (10);
+
+ auto radius = jmin (bounds.getWidth(), bounds.getHeight()) / 2.0f;
+ auto toAngle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
+ auto lineW = jmin (5.0f, radius * 0.1f);
+ auto arcRadius = radius - lineW;
+
+ g.setColour (outline);
+ g.fillEllipse (Rectangle<float> (radius * 2.0f, radius * 2.0f).withCentre (bounds.getCentre()));
+
+ g.setColour (fill);
+ g.fillEllipse (Rectangle<float> (arcRadius * 2.0f, arcRadius * 2.0f).withCentre (bounds.getCentre()));
+
+ Point<float> thumbPoint (bounds.getCentreX() + arcRadius * std::cos (toAngle - MathConstants<float>::halfPi),
+ bounds.getCentreY() + arcRadius * std::sin (toAngle - MathConstants<float>::halfPi));
+ g.setColour (slider.findColour (Slider::thumbColourId));
+ g.drawLine (bounds.getCentreX(), bounds.getCentreY(), thumbPoint.x, thumbPoint.y, lineW);
+ }
+
+private:
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyLNF)
+};
+
+#endif //MYLNF_H_INCLUDED
diff --git a/Plugin/Source/PluginEditor.cpp b/Plugin/Source/PluginEditor.cpp
@@ -28,8 +28,8 @@ ChowtapeModelAudioProcessorEditor::ChowtapeModelAudioProcessorEditor (ChowtapeMo
// editor's size to whatever you need it to be.
setSize (width, height);
- createSlider (gainInKnob, processor.inGain);
- createSlider (gainOutKnob, processor.outGain);
+ createSlider (gainInKnob, processor.inGain, String ("dB"));
+ createSlider (gainOutKnob, processor.outGain, String ("dB"));
createComboBox (oversampling, processor.overSampling);
createComboBox (tapeSpeed, processor.tapeSpeed);
@@ -39,19 +39,25 @@ ChowtapeModelAudioProcessorEditor::~ChowtapeModelAudioProcessorEditor()
{
}
-void ChowtapeModelAudioProcessorEditor::createSlider(Slider& slide, AudioParameterFloat* param, float step){
+void ChowtapeModelAudioProcessorEditor::createSlider (ChowSlider& slide, AudioParameterFloat* param, String suffix, float step){
slide.setName(param->name);
slide.setRange(param->range.start, param->range.end, step);
+ slide.setDefaultValue (param->convertFrom0to1 (dynamic_cast<AudioProcessorParameterWithID*> (param)->getDefaultValue()));
+ slide.setValue(*param);
+
+ slide.setLookAndFeel (&myLNF);
slide.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
- slide.setColour(Slider::rotarySliderFillColourId, Colours::darkred);
- slide.setColour(Slider::rotarySliderOutlineColourId, Colours::black);
- slide.setColour(Slider::thumbColourId, Colours::navajowhite);
+ slide.setColour(Slider::rotarySliderFillColourId, Colours::black);
+ slide.setColour(Slider::rotarySliderOutlineColourId, Colours::darkred);
+ slide.setColour(Slider::thumbColourId, Colours::antiquewhite);
slide.setTextBoxStyle(Slider::TextBoxBelow, false, 80, 20);
slide.setColour(Slider::textBoxTextColourId, Colours::antiquewhite);
slide.setColour(Slider::textBoxOutlineColourId, Colours::antiquewhite);
- slide.setValue(*param);
- slide.addListener(this);
+ if (suffix.isNotEmpty())
+ slide.setTextValueSuffix (" " + suffix);
+
+ slide.addListener(this);
addAndMakeVisible (slide);
}
@@ -92,3 +98,33 @@ void ChowtapeModelAudioProcessorEditor::resized()
gainOutKnob.setBounds (tapeSpeed.getRight(), sliderY, sliderWidth, sliderWidth);
}
+
+AudioParameterFloat* ChowtapeModelAudioProcessorEditor::getParamForSlider (Slider* slider)
+{
+ if (processor.inGain->name == slider->getName())
+ return processor.inGain;
+ else if (processor.outGain->name == slider->getName())
+ return processor.outGain;
+ else
+ return nullptr;
+}
+
+void ChowtapeModelAudioProcessorEditor::sliderValueChanged (Slider* slider)
+{
+ if (AudioParameterFloat* param = getParamForSlider(slider)){
+ *param = (float) slider->getValue();
+ }
+}
+
+void ChowtapeModelAudioProcessorEditor::sliderDragStarted(Slider* slider)
+{
+ if (AudioParameterFloat* param = getParamForSlider(slider))
+ param->beginChangeGesture();
+}
+
+void ChowtapeModelAudioProcessorEditor::sliderDragEnded(Slider* slider)
+{
+ if (AudioParameterFloat* param = getParamForSlider(slider))
+ param->endChangeGesture();
+
+}
diff --git a/Plugin/Source/PluginEditor.h b/Plugin/Source/PluginEditor.h
@@ -2,10 +2,26 @@
#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"
+#include "GUI Extras/MyLNF.h"
+
+class ChowSlider : public Slider
+{
+public:
+ ChowSlider() {}
+
+ void setDefaultValue (const float value) { defaultValue = value; }
+
+ void mouseDoubleClick (const MouseEvent&) override
+ {
+ setValue (defaultValue);
+ }
+
+private:
+ float defaultValue = 0.0f;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowSlider)
+};
-//==============================================================================
-/**
-*/
class ChowtapeModelAudioProcessorEditor : public AudioProcessorEditor,
public Slider::Listener,
public ComboBox::Listener
@@ -18,21 +34,25 @@ public:
void paint (Graphics&) override;
void resized() override;
- void sliderValueChanged (Slider* slider) override {}
void comboBoxChanged (ComboBox* box) override {}
private:
- // This reference is provided as a quick way for your editor to
- // access the processor object that created it.
+ AudioParameterFloat* getParamForSlider (Slider* slider);
+ void sliderValueChanged (Slider* slider) override;
+ void sliderDragStarted (Slider* slider) override;
+ void sliderDragEnded (Slider* slider) override;
+
+ MyLNF myLNF;
+
ChowtapeModelAudioProcessor& processor;
- Slider gainInKnob;
- Slider gainOutKnob;
+ ChowSlider gainInKnob;
+ ChowSlider gainOutKnob;
ComboBox oversampling;
ComboBox tapeSpeed;
- void createSlider(Slider& slide, AudioParameterFloat* param, float step = 0.1f);
+ void createSlider (ChowSlider& slide, AudioParameterFloat* param, String suffix = String(), float step = 0.1f);
void createComboBox (ComboBox& box, AudioParameterChoice* choice);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowtapeModelAudioProcessorEditor)
diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp
@@ -15,22 +15,32 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor()
#endif
{
addParameter (inGain = new AudioParameterFloat (String ("inGain"), String ("Input Gain"), -30.0f, 30.0f, 0.0f));
+ inGain->addListener (this);
+
addParameter (outGain = new AudioParameterFloat (String ("outGain"), String ("Output Gain"), -30.0f, 30.0f, 0.0f));
+ outGain->addListener (this);
addParameter (overSampling = new AudioParameterChoice (String ("overSampling"), String ("Oversampling"),
StringArray ({ "2x", "4x", "8x" }), 0));
addParameter (tapeSpeed = new AudioParameterChoice (String ("tapeSpeed"), String ("Tape Speed"),
StringArray ({ "3.75 ips", "7.5 ips", "15 ips" }), 1));
-
-
- overSample.reset (new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple));
}
ChowtapeModelAudioProcessor::~ChowtapeModelAudioProcessor()
{
}
+void ChowtapeModelAudioProcessor::parameterValueChanged (int paramIndex, float newValue)
+{
+ if (paramIndex == inGain->getParameterIndex())
+ inGainProc.setGain (Decibels::decibelsToGain (inGain->convertFrom0to1 (newValue)));
+ else if (paramIndex == outGain->getParameterIndex())
+ outGainProc.setGain (Decibels::decibelsToGain (outGain->convertFrom0to1 (newValue)));
+ else if (paramIndex == overSampling->getParameterIndex())
+ hysteresis.setOverSamplingFactor (*overSampling);
+}
+
//==============================================================================
const String ChowtapeModelAudioProcessor::getName() const
{
@@ -80,35 +90,32 @@ int ChowtapeModelAudioProcessor::getCurrentProgram()
return 0;
}
-void ChowtapeModelAudioProcessor::setCurrentProgram (int index)
+void ChowtapeModelAudioProcessor::setCurrentProgram (int /*index*/)
{
}
-const String ChowtapeModelAudioProcessor::getProgramName (int index)
+const String ChowtapeModelAudioProcessor::getProgramName (int /*index*/)
{
return {};
}
-void ChowtapeModelAudioProcessor::changeProgramName (int index, const String& newName)
+void ChowtapeModelAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/)
{
}
//==============================================================================
void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
- // Use this method as the place to do any pre-playback
- // initialisation that you need..
- hProcs[0].setSampleRate ((float) (sampleRate * overSamplingFactor));
- hProcs[1].setSampleRate ((float) (sampleRate * overSamplingFactor));
-
- overSample->factorOversampling = overSamplingFactor;
- overSample->initProcessing (samplesPerBlock);
+ inGainProc.prepareToPlay (sampleRate, samplesPerBlock);
+ hysteresis.prepareToPlay (sampleRate, samplesPerBlock);
+ outGainProc.prepareToPlay (sampleRate, samplesPerBlock);
}
void ChowtapeModelAudioProcessor::releaseResources()
{
- // When playback stops, you can use this as an opportunity to free up any
- // spare memory, etc.
+ inGainProc.releaseResources();
+ hysteresis.releaseResources();
+ outGainProc.releaseResources();
}
#ifndef JucePlugin_PreferredChannelConfigurations
@@ -138,25 +145,11 @@ bool ChowtapeModelAudioProcessor::isBusesLayoutSupported (const BusesLayout& lay
void ChowtapeModelAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
ScopedNoDenormals noDenormals;
- auto totalNumInputChannels = getTotalNumInputChannels();
- auto totalNumOutputChannels = getTotalNumOutputChannels();
-
- dsp::AudioBlock<float> block (buffer);
- dsp::AudioBlock<float> osBlock = overSample->processSamplesUp(block);
-
- float* ptrArray[] = { osBlock.getChannelPointer(0), osBlock.getChannelPointer(1) };
- AudioBuffer<float> osBuffer (ptrArray, 2, static_cast<int> (osBlock.getNumSamples()));
-
- for (int channel = 0; channel < totalNumInputChannels; ++channel)
- {
- auto* x = osBuffer.getWritePointer (channel);
- for (int n = 0; n < osBuffer.getNumSamples(); n++)
- {
- x[n] = hProcs[channel].process (((float) 1e5) * x[n]);
- }
- }
+
+ inGainProc.processBlock (buffer, midiMessages);
+ hysteresis.processBlock (buffer, midiMessages);
- overSample->processSamplesDown(block);
+ outGainProc.processBlock (buffer, midiMessages);
}
//==============================================================================
@@ -171,14 +164,14 @@ AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor()
}
//==============================================================================
-void ChowtapeModelAudioProcessor::getStateInformation (MemoryBlock& destData)
+void ChowtapeModelAudioProcessor::getStateInformation (MemoryBlock& /*destData*/)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
}
-void ChowtapeModelAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
+void ChowtapeModelAudioProcessor::setStateInformation (const void* /*data*/, int /*sizeInBytes*/)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h
@@ -1,12 +1,14 @@
#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
-#include "Processors/HysteresisProcessing.h"
+#include "Processors/Hysteresis/HysteresisProcessor.h"
+#include "Processors/GainProcessor.h"
//==============================================================================
/**
*/
-class ChowtapeModelAudioProcessor : public AudioProcessor
+class ChowtapeModelAudioProcessor : public AudioProcessor,
+ public AudioProcessorParameter::Listener
{
public:
//==============================================================================
@@ -51,13 +53,14 @@ public:
AudioParameterChoice* overSampling;
AudioParameterChoice* tapeSpeed;
-private:
- HysteresisProcessor hProcs[2];
- std::unique_ptr<dsp::Oversampling<float>> overSample;
+ void parameterValueChanged (int paramIndex, float newValue) override;
+ void parameterGestureChanged (int /*paramIndex*/, bool /*gestureIsStarting*/) override {}
- int overSamplingFactor = 8;
+private:
+ HysteresisProcessor hysteresis;
- int n_t[2] = { 0, 0 };
+ GainProcessor inGainProc;
+ GainProcessor outGainProc;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowtapeModelAudioProcessor)
diff --git a/Plugin/Source/Processors/GainProcessor.h b/Plugin/Source/Processors/GainProcessor.h
@@ -0,0 +1,48 @@
+#ifndef GAINPROCESSOR_H_INCLUDED
+#define GAINPROCESSOR_H_INCLUDED
+
+#include "ProcessorBase.h"
+
+class GainProcessor : public ProcessorBase
+{
+public:
+ GainProcessor() : ProcessorBase (String ("Gain Processor")) {}
+
+ void prepareToPlay (double sampleRate, int maximumExpectedSamplesPerBlock) override
+ {
+ setRateAndBufferSizeDetails (sampleRate, maximumExpectedSamplesPerBlock);
+ oldGain = 0.0f;
+ }
+
+ void releaseResources() override {}
+ void processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midiMessages*/) override
+ {
+ if (curGain != oldGain)
+ {
+ buffer.applyGainRamp (0, buffer.getNumSamples(), oldGain, curGain);
+ oldGain = curGain;
+ return;
+ }
+
+ buffer.applyGain (curGain);
+ }
+
+ void setGain (float gain)
+ {
+ if (gain == curGain)
+ return;
+
+ oldGain = curGain;
+ curGain = gain;
+ }
+
+ float getGain() const { return curGain; }
+
+private:
+ float curGain = 1.0f;
+ float oldGain = 0.0f;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainProcessor)
+};
+
+#endif //GAINPROCESSOR_H_INCLUDED
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.cpp
@@ -0,0 +1,76 @@
+#include "HysteresisProcessing.h"
+#include <math.h>
+
+HysteresisProcessing::HysteresisProcessing()
+{
+
+}
+
+float HysteresisProcessing::langevin (float x)
+{
+ if (std::abs (x) > (float) (10e-4))
+ return (1.0f / (float) tanh (x)) - (1.0f / x);
+ else
+ return (x / 3.0f);
+}
+
+float HysteresisProcessing::langevinD (float x)
+{
+ if (std::abs (x) > (float) (10e-4))
+ {
+ float tanhRecip = 1.0f / tanh (x);
+ return (1.0f / (x * x)) - (tanhRecip * tanhRecip) + 1.0f;
+ }
+ else
+ return (1.0f / 3.0f);
+}
+
+float HysteresisProcessing::deriv (float x_n, float x_n1, float x_d_n1)
+{
+ return ((2.0f * fs) * (x_n - x_n1)) - x_d_n1;
+}
+
+float HysteresisProcessing::hysteresisFunc (float M, float H, float H_d)
+{
+ const float Q = (H + alpha * M) / a;
+ const float M_diff = M_s * langevin (Q) - M;
+
+ const float delta = H_d > 0 ? 1.0f : -1.0f;
+ const float delta_M = signbit (delta) == signbit (M_diff) ? 1.0f : 0.0f;
+
+ const float L_prime = langevinD (Q);
+
+ const float denominator = 1 - (c * alpha * (M_s / a) * L_prime);
+
+ const float t1_num = (1 - c) * delta_M * M_diff;
+ const float t1_den = ((1 - c) * delta * k) - (alpha * M_diff);
+ const float t1 = (t1_num / t1_den) * H_d;
+
+ const float t2 = c * (M_s / a) * H_d * L_prime;
+
+ return (t1 + t2) / denominator;
+}
+
+float HysteresisProcessing::M_n (float prevM, float k1, float k2, float k3, float k4)
+{
+ return prevM + (k1 / 6.0f) + (k2 / 3.0f) + (k3 / 3.0f) + (k4 / 6.0f);
+}
+
+float HysteresisProcessing::process (float H)
+{
+ const float H_d = deriv (H, H_n1, H_d_n1);
+
+ const float T = (1.0f / fs);
+ const float k1 = T * hysteresisFunc (M_n1, H_n1, H_d_n1);
+ const float k2 = T * hysteresisFunc (M_n1 + (k1 / 2.0f), (H + H_n1) / 2.0f, (H_d + H_d_n1) / 2.0f);
+ const float k3 = T * hysteresisFunc (M_n1 + (k2 / 2.0f), (H + H_n1) / 2.0f, (H_d + H_d_n1) / 2.0f);
+ const float k4 = T * hysteresisFunc (M_n1 + k3, H, H_d);
+
+ const float M = M_n (M_n1, k1, k2, k3, k4);
+
+ M_n1 = M;
+ H_n1 = H;
+ H_d_n1 = H_d;
+
+ return M / M_s;
+}
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.h b/Plugin/Source/Processors/Hysteresis/HysteresisProcessing.h
@@ -0,0 +1,38 @@
+#ifndef HYSTERESISPROCESSING_H_INCLUDED
+#define HYSTERESISPROCESSING_H_INCLUDED
+
+#include "JuceHeader.h"
+
+class HysteresisProcessing
+{
+public:
+ HysteresisProcessing();
+
+ float process (float H);
+
+ void setSampleRate (float newSR) { fs = newSR; }
+
+private:
+
+ float langevin (float x);
+ float langevinD (float x);
+ float deriv (float x_n, float x_n1, float x_d_n1);
+
+ float hysteresisFunc (float M, float H, float H_d);
+ float M_n (float M_n1, float k1, float k2, float k3, float k4);
+
+ float fs = 48000.0f;
+ const float M_s = 350000.0f;
+ const float a = (float) 2.2e4;
+ const float alpha = (float) 1.6e-3;
+ const float k = (float) 27.0e3;
+ const float c = (float) 1.7e-1;
+
+ float M_n1 = 0.0f;
+ float H_n1 = 0.0f;
+ float H_d_n1 = 0.0f;
+
+ //JUCE_DECLARE_NONCOPYABLE_WITH_LEAK_DETECTOR (HysteresisProcessing)
+};
+
+#endif
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp
@@ -0,0 +1,61 @@
+#include "HysteresisProcessor.h"
+
+HysteresisProcessor::HysteresisProcessor() : ProcessorBase ("HysteresisProcessor")
+{
+ overSample.reset (new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandFIREquiripple));
+}
+
+void HysteresisProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
+{
+ setRateAndBufferSizeDetails (sampleRate, samplesPerBlock);
+
+ hProcs[0].setSampleRate ((float) (sampleRate * overSamplingFactor));
+ hProcs[1].setSampleRate ((float) (sampleRate * overSamplingFactor));
+
+ overSample->factorOversampling = overSamplingFactor;
+ overSample->initProcessing (samplesPerBlock);
+}
+
+void HysteresisProcessor::releaseResources()
+{
+ overSample->reset();
+}
+
+void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midi*/)
+{
+ auto totalNumInputChannels = getTotalNumInputChannels();
+
+ dsp::AudioBlock<float> block (buffer);
+ dsp::AudioBlock<float> osBlock = overSample->processSamplesUp(block);
+
+ float* ptrArray[] = { osBlock.getChannelPointer(0), osBlock.getChannelPointer(1) };
+ AudioBuffer<float> osBuffer (ptrArray, 2, static_cast<int> (osBlock.getNumSamples()));
+
+ for (int channel = 0; channel < totalNumInputChannels; ++channel)
+ {
+ auto* x = osBuffer.getWritePointer (channel);
+ for (int n = 0; n < osBuffer.getNumSamples(); n++)
+ {
+ x[n] = hProcs[channel].process (((float) 1e5) * x[n]);
+ }
+ }
+
+ overSample->processSamplesDown(block);
+}
+
+void HysteresisProcessor::setOverSamplingFactor (String osFactor)
+{
+ int factor = overSamplingFactor;
+
+ if (osFactor == "2x")
+ factor = 2;
+ else if (osFactor == "4x")
+ factor = 4;
+ else if (osFactor == "8x")
+ factor = 8;
+
+ overSamplingFactor = factor;
+ overSample->reset();
+ overSample->factorOversampling = overSamplingFactor;
+ overSample->initProcessing (getBlockSize());
+}
diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h
@@ -0,0 +1,29 @@
+#ifndef HYSTERESISPROCESSOR_H_INCLUDED
+#define HYSTERESISPROCESSOR_H_INCLUDED
+
+#include "../ProcessorBase.h"
+#include "HysteresisProcessing.h"
+
+class HysteresisProcessor : public ProcessorBase
+{
+public:
+ HysteresisProcessor();
+
+ void prepareToPlay (double sampleRate, int samplesPerBlock) override;
+ void releaseResources() override;
+ void processBlock (AudioBuffer<float>&, MidiBuffer&) override;
+
+ double getTailLengthSeconds() const override { return overSample->getLatencyInSamples() * getSampleRate(); }
+
+ void setOverSamplingFactor (String osFactor);
+
+private:
+ HysteresisProcessing hProcs[2];
+ std::unique_ptr<dsp::Oversampling<float>> overSample;
+
+ int overSamplingFactor = 8;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HysteresisProcessor)
+};
+
+#endif //HYSTERESISPROCESSOR_H_INCLUDED
diff --git a/Plugin/Source/Processors/HysteresisProcessing.cpp b/Plugin/Source/Processors/HysteresisProcessing.cpp
@@ -1,76 +0,0 @@
-#include "HysteresisProcessing.h"
-#include <math.h>
-
-HysteresisProcessor::HysteresisProcessor()
-{
-
-}
-
-float HysteresisProcessor::langevin (float x)
-{
- if (std::abs (x) > (float) (10e-4))
- return (1.0f / (float) tanh (x)) - (1.0f / x);
- else
- return (x / 3.0f);
-}
-
-float HysteresisProcessor::langevinD (float x)
-{
- if (std::abs (x) > (float) (10e-4))
- {
- float tanhRecip = 1.0f / tanh (x);
- return (1.0f / (x * x)) - (tanhRecip * tanhRecip) + 1.0f;
- }
- else
- return (1.0f / 3.0f);
-}
-
-float HysteresisProcessor::deriv (float x_n, float x_n1, float x_d_n1)
-{
- return ((2.0f * fs) * (x_n - x_n1)) - x_d_n1;
-}
-
-float HysteresisProcessor::hysteresisFunc (float M, float H, float H_d)
-{
- const float Q = (H + alpha * M) / a;
- const float M_diff = M_s * langevin (Q) - M;
-
- const float delta = H_d > 0 ? 1.0f : -1.0f;
- const float delta_M = signbit (delta) == signbit (M_diff) ? 1.0f : 0.0f;
-
- const float L_prime = langevinD (Q);
-
- const float denominator = 1 - (c * alpha * (M_s / a) * L_prime);
-
- const float t1_num = (1 - c) * delta_M * M_diff;
- const float t1_den = ((1 - c) * delta * k) - (alpha * M_diff);
- const float t1 = (t1_num / t1_den) * H_d;
-
- const float t2 = c * (M_s / a) * H_d * L_prime;
-
- return (t1 + t2) / denominator;
-}
-
-float HysteresisProcessor::M_n (float prevM, float k1, float k2, float k3, float k4)
-{
- return prevM + (k1 / 6.0f) + (k2 / 3.0f) + (k3 / 3.0f) + (k4 / 6.0f);
-}
-
-float HysteresisProcessor::process (float H)
-{
- const float H_d = deriv (H, H_n1, H_d_n1);
-
- const float T = (1.0f / fs);
- const float k1 = T * hysteresisFunc (M_n1, H_n1, H_d_n1);
- const float k2 = T * hysteresisFunc (M_n1 + (k1 / 2.0f), (H + H_n1) / 2.0f, (H_d + H_d_n1) / 2.0f);
- const float k3 = T * hysteresisFunc (M_n1 + (k2 / 2.0f), (H + H_n1) / 2.0f, (H_d + H_d_n1) / 2.0f);
- const float k4 = T * hysteresisFunc (M_n1 + k3, H, H_d);
-
- const float M = M_n (M_n1, k1, k2, k3, k4);
-
- M_n1 = M;
- H_n1 = H;
- H_d_n1 = H_d;
-
- return M / M_s;
-}
diff --git a/Plugin/Source/Processors/HysteresisProcessing.h b/Plugin/Source/Processors/HysteresisProcessing.h
@@ -1,38 +0,0 @@
-#ifndef HYSTERESISPROCESSOR_H_INCLUDED
-#define HYSTERESISPROCESSOR_H_INCLUDED
-
-#include "JuceHeader.h"
-
-class HysteresisProcessor
-{
-public:
- HysteresisProcessor();
-
- float process (float H);
-
- void setSampleRate (float newSR) { fs = newSR; }
-
-private:
-
- float langevin (float x);
- float langevinD (float x);
- float deriv (float x_n, float x_n1, float x_d_n1);
-
- float hysteresisFunc (float M, float H, float H_d);
- float M_n (float M_n1, float k1, float k2, float k3, float k4);
-
- float fs = 48000.0f;
- const float M_s = 350000.0f;
- const float a = (float) 2.2e4;
- const float alpha = (float) 1.6e-3;
- const float k = (float) 27.0e3;
- const float c = (float) 1.7e-1;
-
- float M_n1 = 0.0f;
- float H_n1 = 0.0f;
- float H_d_n1 = 0.0f;
-
- //JUCE_DECLARE_NONCOPYABLE_WITH_LEAK_DETECTOR (HysteresisProcessor)
-};
-
-#endif
diff --git a/Plugin/Source/Processors/ProcessorBase.h b/Plugin/Source/Processors/ProcessorBase.h
@@ -0,0 +1,37 @@
+#ifndef PROCESSORBASE_H_INCLUDED
+#define PROCESSORBASE_H_INCLUDED
+
+#include "JuceHeader.h"
+
+class ProcessorBase : public AudioProcessor
+{
+public:
+ ProcessorBase (String name) : name (name) {}
+
+ const String getName() const override { return name; }
+
+ double getTailLengthSeconds() const override { return 0.0; }
+
+ bool acceptsMidi() const override { return false; }
+ bool producesMidi() const override { return false; }
+
+ AudioProcessorEditor* createEditor() override { return nullptr; };
+ bool hasEditor() const override { return false; }
+
+ int getNumPrograms() override { return 0; }
+ void setCurrentProgram (int /*index*/) override {}
+ int getCurrentProgram() override { return 0; }
+
+ const String getProgramName (int /*index*/) override { return {}; }
+ void changeProgramName (int /*index*/, const String& /*newName*/) override {}
+
+ void getStateInformation (MemoryBlock& /*destData*/) override {}
+ void setStateInformation (const void* /*data*/, int /*sizeInBytes*/) override {}
+
+private:
+ String name;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorBase)
+};
+
+#endif //PROCESSORBASE_H_INCLUDED