diff --git a/Source/Module/AnalogClipperModule/AnalogClipperModule.cpp b/Source/Module/AnalogClipperModule/AnalogClipperModule.cpp new file mode 100755 index 0000000..697ecad --- /dev/null +++ b/Source/Module/AnalogClipperModule/AnalogClipperModule.cpp @@ -0,0 +1,357 @@ +/* + ============================================================================== + + AnalogClipperModule.cpp + + Copyright (c) 2021 KillBizz - Gabriel Bizzo e Francesco Magoga + + ============================================================================== +*/ + +/* + +This file is part of Biztortion software. + +Biztortion is free software : you can redistribute it and /or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Biztortion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Biztortion. If not, see < http://www.gnu.org/licenses/>. + +*/ + +#include "AnalogClipperModule.h" + +//============================================================================== + +/* AnalogClipperModule DSP */ + +//============================================================================== + +AnalogClipperModuleDSP::AnalogClipperModuleDSP(juce::AudioProcessorValueTreeState& _apvts) + : DSPModule(_apvts) +{ +} + +AnalogClipperModuleDSP::~AnalogClipperModuleDSP() +{ + +} + +void AnalogClipperModuleDSP::prepareToPlay(double sampleRate, int samplesPerBlock) +{ + for (auto channel = 0; channel < 2; channel++) + clippers[channel].setSampleRate(sampleRate); +} + +void AnalogClipperModuleDSP::processBlock(juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages, double sampleRate) +{ + + updateDSPState(sampleRate); + + if (!bypassed) { + + // SAFETY CHECK :::: since some hosts will change buffer sizes without calling prepToPlay (ex: Bitwig) + int numSamples = buffer.getNumSamples(); + if (wetBuffer.getNumSamples() != numSamples) + { + wetBuffer.setSize(2, numSamples, false, true, true); // clears + tempBuffer.setSize(2, numSamples, false, true, true); // clears + } + + // Wet Buffer feeding + for (auto channel = 0; channel < 2; channel++) + wetBuffer.copyFrom(channel, 0, buffer, channel, 0, numSamples); + + // Oversampling wetBuffer for processing + /*juce::dsp::AudioBlock block(wetBuffer); + auto leftBlock = block.getSingleChannelBlock(0); + auto rightBlock = block.getSingleChannelBlock(1); + juce::dsp::ProcessContextReplacing leftContext(leftBlock); + juce::dsp::ProcessContextReplacing rightContext(rightBlock); + auto oversampledLeftBlock = oversampler.processSamplesUp(leftContext.getInputBlock()); + auto oversampledRightBlock = oversampler.processSamplesUp(rightContext.getInputBlock());*/ + + // Gain + gainGain.applyGain(wetBuffer, numSamples); + + // Temp Buffer feeding for applying asymmetry + for (auto channel = 0; channel < 2; channel++) + tempBuffer.copyFrom(channel, 0, wetBuffer, channel, 0, numSamples); + + // Analog Clipper + for (auto channel = 0; channel < 2; channel++) + { + auto* channelData = wetBuffer.getWritePointer(channel); + + clippers[channel].process(channelData, numSamples); + } + + // Volume + volumeValue.applyGain(wetBuffer, numSamples); + + // Sampling back down the wetBuffer after processing + /*oversampler.processSamplesDown(leftContext.getOutputBlock()); + oversampler.processSamplesDown(rightContext.getOutputBlock()); + + auto* channelData = wetBuffer.getWritePointer(0); + for (auto i = 0; i < numSamples; i++) + channelData[i] = leftContext.getOutputBlock().getSample(0, i); + channelData = wetBuffer.getWritePointer(1); + for (auto i = 0; i < numSamples; i++) + channelData[i] = rightContext.getOutputBlock().getSample(0, i);*/ + + // Mixing buffers + dryGain.applyGain(buffer, numSamples); + wetGain.applyGain(wetBuffer, numSamples); + + for (auto channel = 0; channel < 2; channel++) + buffer.addFrom(channel, 0, wetBuffer, channel, 0, numSamples); + + // Hard clipper for limiting + for (auto channel = 0; channel < 2; channel++) + { + auto* channelData = buffer.getWritePointer(channel); + + for (auto i = 0; i < numSamples; i++) + channelData[i] = juce::jlimit(-1.f, 1.f, channelData[i]); + } + } +} + +void AnalogClipperModuleDSP::addParameters(juce::AudioProcessorValueTreeState::ParameterLayout& layout) +{ + using namespace juce; + + for (int i = 1; i < 9; ++i) { + layout.add(std::make_unique("Analog Clipper Gain " + std::to_string(i), "Analog Clipper Gain " + std::to_string(i), NormalisableRange(0.f, 40.f, 0.01f), 0.f, "Analog Clipper " + std::to_string(i))); + layout.add(std::make_unique("Analog Clipper Mix " + std::to_string(i), "Analog Clipper Mix " + std::to_string(i), NormalisableRange(0.f, 100.f, 0.01f), 100.f, "Analog Clipper " + std::to_string(i))); + layout.add(std::make_unique("Analog Clipper Volume " + std::to_string(i), "Analog Clipper Volume " + std::to_string(i), NormalisableRange(0.f, 100.f, 0.01f), 100.f, "Analog Clipper " + std::to_string(i))); + layout.add(std::make_unique("Analog Clipper Bypassed " + std::to_string(i), "Analog Clipper Bypassed " + std::to_string(i), false, "Analog Clipper " + std::to_string(i))); + } +} + +AnalogClipperSettings AnalogClipperModuleDSP::getSettings(juce::AudioProcessorValueTreeState& apvts, unsigned int parameterNumber) +{ + AnalogClipperSettings settings; + + settings.gain = apvts.getRawParameterValue("Analog Clipper Gain " + std::to_string(parameterNumber))->load(); + settings.mix = apvts.getRawParameterValue("Analog Clipper Mix " + std::to_string(parameterNumber))->load(); + settings.volume = apvts.getRawParameterValue("Analog Clipper Volume " + std::to_string(parameterNumber))->load(); + settings.bypassed = apvts.getRawParameterValue("Analog Clipper Bypassed " + std::to_string(parameterNumber))->load() > 0.5f; + + return settings; +} + +void AnalogClipperModuleDSP::updateDSPState(double) +{ + auto settings = getSettings(apvts, parameterNumber); + + bypassed = settings.bypassed; + + auto mix = settings.mix * 0.01f; + dryGain.setTargetValue(1.f - mix); + wetGain.setTargetValue(mix); + gainGain.setTargetValue(juce::Decibels::decibelsToGain(settings.gain)); + volumeValue.setTargetValue(settings.volume * 0.01f); +} + +//============================================================================== + +/* AnalogClipperModule GUI */ + +//============================================================================== + +AnalogClipperModuleGUI::AnalogClipperModuleGUI(PluginState& p, unsigned int parameterNumber) + : GUIModule(), pluginState(p), + gainSlider(*pluginState.apvts.getParameter("Analog Clipper Gain " + std::to_string(parameterNumber)), "dB"), + mixSlider(*pluginState.apvts.getParameter("Analog Clipper Mix " + std::to_string(parameterNumber)), "%"), + volumeSlider(*pluginState.apvts.getParameter("Analog Clipper Volume " + std::to_string(parameterNumber)), "%"), + transferFunctionGraph(p, parameterNumber), + gainSliderAttachment(pluginState.apvts, "Analog Clipper Gain " + std::to_string(parameterNumber), gainSlider), + mixSliderAttachment(pluginState.apvts, "Analog Clipper Mix " + std::to_string(parameterNumber), mixSlider), + volumeSliderAttachment(pluginState.apvts, "Analog Clipper Volume " + std::to_string(parameterNumber), volumeSlider), + bypassButtonAttachment(pluginState.apvts, "Analog Clipper Bypassed " + std::to_string(parameterNumber), bypassButton) +{ + // title setup + title.setText("Analog Clipper", juce::dontSendNotification); + title.setFont(ModuleLookAndFeel::getTitlesFont()); + + // labels + gainLabel.setText("Gain", juce::dontSendNotification); + gainLabel.setFont(ModuleLookAndFeel::getLabelsFont()); + mixLabel.setText("Mix", juce::dontSendNotification); + mixLabel.setFont(ModuleLookAndFeel::getLabelsFont()); + volumeLabel.setText("Volume", juce::dontSendNotification); + volumeLabel.setFont(ModuleLookAndFeel::getLabelsFont()); + + gainSlider.labels.add({ 0.f, "0dB" }); + gainSlider.labels.add({ 1.f, "40dB" }); + mixSlider.labels.add({ 0.f, "0%" }); + mixSlider.labels.add({ 1.f, "100%" }); + volumeSlider.labels.add({ 0.f, "0%" }); + volumeSlider.labels.add({ 1.f, "100%" }); + + bypassButton.setLookAndFeel(&lnf); + + auto safePtr = juce::Component::SafePointer(this); + bypassButton.onClick = [safePtr]() + { + if (auto* comp = safePtr.getComponent()) + { + auto bypassed = comp->bypassButton.getToggleState(); + comp->handleParamCompsEnablement(bypassed); + } + }; + + // tooltips + bypassButton.setTooltip("Bypass this module"); + gainSlider.setTooltip("Select the amount of gain to be applied to the module input signal"); + mixSlider.setTooltip("Select the blend between the unprocessed and processed signal"); + volumeSlider.setTooltip("Select the volume of the processed signal"); + + for (auto* comp : getAllComps()) + { + addAndMakeVisible(comp); + } + + handleParamCompsEnablement(bypassButton.getToggleState()); +} + +AnalogClipperModuleGUI::~AnalogClipperModuleGUI() +{ + bypassButton.setLookAndFeel(nullptr); +} + +std::vector AnalogClipperModuleGUI::getAllComps() +{ + return { + &title, + &transferFunctionGraph, + &gainSlider, + &mixSlider, + &volumeSlider, + // labels + &gainLabel, + &mixLabel, + &volumeLabel, + // bypass + &bypassButton + }; +} + +std::vector AnalogClipperModuleGUI::getParamComps() +{ + return { + &gainSlider, + &mixSlider, + &volumeSlider + }; +} + +void AnalogClipperModuleGUI::updateParameters(const juce::Array& values) +{ + auto value = values.begin(); + + bypassButton.setToggleState(*(value++), juce::NotificationType::sendNotificationSync); + gainSlider.setValue(*(value++), juce::NotificationType::sendNotificationSync); + mixSlider.setValue(*(value++), juce::NotificationType::sendNotificationSync); + volumeSlider.setValue(*(value++), juce::NotificationType::sendNotificationSync); +} + +void AnalogClipperModuleGUI::resetParameters(unsigned int parameterNumber) +{ + auto gain = pluginState.apvts.getParameter("Analog Clipper Gain " + std::to_string(parameterNumber)); + auto mix = pluginState.apvts.getParameter("Analog Clipper Mix " + std::to_string(parameterNumber)); + auto volume = pluginState.apvts.getParameter("Analog Clipper Volume " + std::to_string(parameterNumber)); + auto bypassed = pluginState.apvts.getParameter("Analog Clipper Bypassed " + std::to_string(parameterNumber)); + + gain->setValueNotifyingHost(gain->getDefaultValue()); + mix->setValueNotifyingHost(mix->getDefaultValue()); + volume->setValueNotifyingHost(volume->getDefaultValue()); + bypassed->setValueNotifyingHost(bypassed->getDefaultValue()); +} + +juce::Array AnalogClipperModuleGUI::getParamValues() +{ + juce::Array values; + + values.add(juce::var(bypassButton.getToggleState())); + values.add(juce::var(gainSlider.getValue())); + values.add(juce::var(mixSlider.getValue())); + values.add(juce::var(volumeSlider.getValue())); + + return values; +} + +void AnalogClipperModuleGUI::resized() +{ + auto clipperArea = getContentRenderArea(); + + // bypass + auto temp = clipperArea; + auto bypassButtonArea = temp.removeFromTop(25); + + bypassButtonArea.setWidth(35.f); + bypassButtonArea.setX(145.f); + bypassButtonArea.setY(20.f); + + bypassButton.setBounds(bypassButtonArea); + + auto titleAndBypassArea = clipperArea.removeFromTop(30); + titleAndBypassArea.translate(0, 4); + + auto clipperGraphArea = clipperArea.removeFromLeft(clipperArea.getWidth() * (4.f / 10.f)); + clipperGraphArea.reduce(10, 10); + + clipperArea.translate(0, 8); + + auto topArea = clipperArea.removeFromTop(clipperArea.getHeight() * (1.f / 2.f)); + + auto topLabelsArea = topArea.removeFromTop(14); + + // label areas + temp = topLabelsArea.removeFromLeft(topLabelsArea.getWidth() * (1.f / 2.f)); + auto gainLabelArea = temp.removeFromLeft(temp.getWidth() * (1.f / 2.f)); + auto mixLabelArea = temp; + auto volumeLabelArea = topLabelsArea.removeFromRight(topLabelsArea.getWidth() * (1.f / 2.f)); + + // slider areas + temp = topArea.removeFromLeft(topArea.getWidth() * (1.f / 2.f)); + auto gainArea = temp.removeFromLeft(temp.getWidth() * (1.f / 2.f)); + auto mixArea = temp; + auto volumeArea = topArea.removeFromRight(topArea.getWidth() * (1.f / 2.f)); + + juce::Rectangle renderArea; + renderArea.setSize(gainArea.getWidth(), gainArea.getWidth()); + + title.setBounds(titleAndBypassArea); + title.setJustificationType(juce::Justification::centredBottom); + + transferFunctionGraph.setBounds(clipperGraphArea); + + renderArea.setCentre(gainArea.getCentre()); + renderArea.setY(gainArea.getTopLeft().getY()); + gainSlider.setBounds(renderArea); + gainLabel.setBounds(gainLabelArea); + gainLabel.setJustificationType(juce::Justification::centred); + + renderArea.setCentre(mixArea.getCentre()); + renderArea.setY(mixArea.getTopLeft().getY()); + mixSlider.setBounds(renderArea); + mixLabel.setBounds(mixLabelArea); + mixLabel.setJustificationType(juce::Justification::centred); + + renderArea.setCentre(volumeArea.getCentre()); + renderArea.setY(volumeArea.getTopLeft().getY()); + volumeSlider.setBounds(renderArea); + volumeLabel.setBounds(volumeLabelArea); + volumeLabel.setJustificationType(juce::Justification::centred); +} diff --git a/Source/Module/AnalogClipperModule/AnalogClipperModule.h b/Source/Module/AnalogClipperModule/AnalogClipperModule.h new file mode 100755 index 0000000..b122713 --- /dev/null +++ b/Source/Module/AnalogClipperModule/AnalogClipperModule.h @@ -0,0 +1,120 @@ +/* + ============================================================================== + + AnalogClipperModule.h + + Copyright (c) 2021 KillBizz - Gabriel Bizzo e Francesco Magoga + + ============================================================================== +*/ + +/* + +This file is part of Biztortion software. + +Biztortion is free software : you can redistribute it and /or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Biztortion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Biztortion. If not, see < http://www.gnu.org/licenses/>. + +*/ + +#ifndef AnalogClipperModule_h +#define AnalogClipperModule_h + +#include + +#include "../../Shared/GUIStuff.h" +#include "../../Component/TransferFunctionGraphComponent.h" +#include "../../Module/DSPModule.h" +#include "../../Module/GUIModule.h" +#include "../../Shared/PluginState.h" + +#include "Clipper.h" + +struct AnalogClipperSettings +{ + float mix{ 0 }, gain{ 0 }, volume{ 0 }; + bool bypassed{ false }; +}; + +class AnalogClipperModuleDSP : public DSPModule +{ + public: + AnalogClipperModuleDSP(juce::AudioProcessorValueTreeState& _apvts); + virtual ~AnalogClipperModuleDSP(); + + void updateDSPState(double sampleRate) override; + void prepareToPlay(double sampleRate, int samplesPerBlock) override; + void processBlock(juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages, double sampleRate) override; + + static void addParameters(juce::AudioProcessorValueTreeState::ParameterLayout&); + static AnalogClipperSettings getSettings(juce::AudioProcessorValueTreeState& apvts, unsigned int parameterNumber); + + private: + Clipper clippers[2]; + + bool bypassed = false; + juce::AudioBuffer wetBuffer, tempBuffer; + juce::LinearSmoothedValue gainGain, dryGain, wetGain, volumeValue; + + /*static const size_t numChannels = 2; + static const size_t oversamplingOrder = 4; + static const int oversamplingFactor = 1 << oversamplingOrder; + static const auto filterType = juce::dsp::Oversampling::filterHalfBandPolyphaseIIR; + + juce::dsp::Oversampling oversampler{ numChannels, oversamplingOrder, filterType };*/ +}; + +//============================================================================== + +/* WaveshaperModule GUI */ + +//============================================================================== + +class AnalogClipperModuleGUI : public GUIModule +{ + public: + AnalogClipperModuleGUI(PluginState& p, unsigned int parameterNumber); + virtual ~AnalogClipperModuleGUI(); + + std::vector getAllComps() override; + std::vector getParamComps() override; + virtual void updateParameters(const juce::Array& values) override; + virtual void resetParameters(unsigned int parameterNumber) override; + virtual juce::Array getParamValues() override; + + void resized() override; + + private: + + using APVTS = juce::AudioProcessorValueTreeState; + using Attachment = APVTS::SliderAttachment; + using ButtonAttachment = APVTS::ButtonAttachment; + + PluginState& pluginState; + + juce::Label title; + + TransferFunctionGraphComponent transferFunctionGraph; + + juce::Label gainLabel, mixLabel, volumeLabel; + RotarySliderWithLabels gainSlider, mixSlider, volumeSlider; + Attachment gainSliderAttachment, mixSliderAttachment, volumeSliderAttachment; + + PowerButton bypassButton; + + ButtonAttachment bypassButtonAttachment; + + ButtonsLookAndFeel lnf; +}; + +#endif /* AnalogClipperModule_h */ diff --git a/Source/Module/AnalogClipperModule/Clipper.cpp b/Source/Module/AnalogClipperModule/Clipper.cpp new file mode 100755 index 0000000..8018541 --- /dev/null +++ b/Source/Module/AnalogClipperModule/Clipper.cpp @@ -0,0 +1,98 @@ +// +// Clipper.cpp +// clipper - VST3 +// +// Created by Francesco Magoga +// + +#include "Clipper.h" + +#include + +#include + +#include + +#define THRESHOLD 1e-4 //in Volts +#define MAX_ITERATIONS 250 + +#include + +using juce_math = juce::dsp::FastMathApproximations; + +Clipper::Clipper() +{ + diode.alpha = 1/(2*23e-3); //23mV + diode.beta = 2.52e-9; //2.52nA + + Rin = 1e3; //1kOhm + C = 100e-9; //100nF + + lastFPOutput = 0; + + inputGain = 0.0f; +} + +Clipper::~Clipper() +{ + +} + +void Clipper::setSampleRate(unsigned int sampleRate) +{ + this->sampleRate = sampleRate; + + T = 1.0f/sampleRate; +} + +void Clipper::process(float *buffer, size_t size) +{ + /* + for(int i=0;i<200;i++) + buffer[i] *= i/200.0f; + */ + + for(int sample=0;sample THRESHOLD && iteration < MAX_ITERATIONS) + { + oldVDiodes = vDiodes; + + //vDiodes = oldVDiodes-diodesFunction(vIn, oldVDiodes, lastFPOutput)/diodesFunctionDerivative(vIn, oldVDiodes, lastFPOutput); + vDiodes = vDiodes-(vDiodes-diodesFunction(vIn, oldVDiodes, lastFPOutput))/(1-diodesFunctionDerivative(vIn, oldVDiodes, lastFPOutput)); + + iteration++; + } + + lastFPOutput = vDiodes; + + //if(vDiodes > 0 || std::isnan(vDiodes)) { std::ofstream f("/Users/francesco/Desktop/output.txt", std::ios::app); f << vDiodes << std::endl; } + + return vDiodes; +} + +float Clipper::diodesFunction(float vIn, float vDiodes, float oldVDiodes) +{ + float f = (vIn-vDiodes)/Rin-2*diode.beta*juce_math::sinh(diode.alpha*vDiodes); + float ret = T * f / C + oldVDiodes; + + return ret; +} + +float Clipper::diodesFunctionDerivative(float vIn, float vDiodes, float oldVDiodes) +{ + float f = 2*diode.alpha*diode.beta*juce_math::cosh(diode.alpha*vDiodes)+1/Rin; + float ret = - T * f / C; + + if(ret == 0) { std::ofstream f("/Users/francesco/Desktop/output.txt", std::ios::app); f << vDiodes << std::endl; } + + return ret; +} diff --git a/Source/Module/AnalogClipperModule/Clipper.h b/Source/Module/AnalogClipperModule/Clipper.h new file mode 100755 index 0000000..2af4060 --- /dev/null +++ b/Source/Module/AnalogClipperModule/Clipper.h @@ -0,0 +1,46 @@ +// +// Clipper.hpp +// clipper - VST3 +// +// Created by Francesco Magoga +// + +#ifndef Clipper_hpp +#define Clipper_hpp + +#define MAX_L_VALUE 50.0 + +#include + +class Diode +{ + public: + float alpha; + float beta; +}; + +class Clipper +{ + public: + Clipper(); + ~Clipper(); + + void setSampleRate(unsigned int sampleRate); + + void process(float *buffer, size_t size); + + private: + Diode diode; + + float Rin, C, T, lastFPOutput; + + float capacitorVoltage(float vIn); + float diodesFunction(float vIn, float vDiodes, float oldVDiodes); + float diodesFunctionDerivative(float vIn, float vDiodes, float oldVDiodes); + + unsigned int sampleRate; + + float inputGain; +}; + +#endif /* Clipper_hpp */ diff --git a/Source/Shared/ModuleGenerator.cpp b/Source/Shared/ModuleGenerator.cpp index 0ca43a4..dfbf065 100644 --- a/Source/Shared/ModuleGenerator.cpp +++ b/Source/Shared/ModuleGenerator.cpp @@ -61,6 +61,10 @@ DSPModule* ModuleGenerator::createDSPModule(ModuleType mt) newModule = new SlewLimiterModuleDSP(pluginState.apvts); break; } + case ModuleType::AnalogClipper: { + newModule = new AnalogClipperModuleDSP(pluginState.apvts); + break; + } default: break; } @@ -113,6 +117,10 @@ GUIModule* ModuleGenerator::createGUIModule(ModuleType type, unsigned int parame newModule = new SlewLimiterModuleGUI(pluginState, parameterNumber); break; } + case ModuleType::AnalogClipper: { + newModule = new AnalogClipperModuleGUI(pluginState, parameterNumber); + break; + } default: break; } diff --git a/Source/Shared/ModuleGenerator.h b/Source/Shared/ModuleGenerator.h index c2acdc0..4ea6c73 100644 --- a/Source/Shared/ModuleGenerator.h +++ b/Source/Shared/ModuleGenerator.h @@ -41,6 +41,7 @@ along with Biztortion. If not, see < http://www.gnu.org/licenses/>. #include "../Module/MeterModule.h" #include "../Module/OscilloscopeModule.h" #include "../Module/SlewLimiterModule.h" +#include "../Module/AnalogClipperModule/AnalogClipperModule.h" class ModuleGenerator { public: @@ -60,4 +61,4 @@ class ModuleGenerator { PluginState& pluginState; -}; \ No newline at end of file +}; diff --git a/Source/Shared/ModuleType.h b/Source/Shared/ModuleType.h index bf90eab..0c84212 100644 --- a/Source/Shared/ModuleType.h +++ b/Source/Shared/ModuleType.h @@ -37,7 +37,8 @@ enum ModuleType { Waveshaper, ClassicBitcrusher, SpectrumBitcrusher, - SlewLimiter + SlewLimiter, + AnalogClipper }; const std::unordered_map moduleType_names({ @@ -47,5 +48,6 @@ const std::unordered_map moduleType_names({ {ModuleType::Waveshaper, "Waveshaper"}, {ModuleType::ClassicBitcrusher, "Classic Bitcrusher"}, {ModuleType::SpectrumBitcrusher, "Spectrum Bitcrusher"}, - {ModuleType::SlewLimiter, "Slew Limiter"} - }); \ No newline at end of file + {ModuleType::SlewLimiter, "Slew Limiter"}, + {ModuleType::AnalogClipper, "Analog Clipper"} + }); diff --git a/Source/Shared/PluginState.cpp b/Source/Shared/PluginState.cpp index c089485..e031db0 100644 --- a/Source/Shared/PluginState.cpp +++ b/Source/Shared/PluginState.cpp @@ -36,6 +36,7 @@ along with Biztortion. If not, see < http://www.gnu.org/licenses/>. #include "../Module/SpectrumBitcrusherModule.h" #include "../Module/SlewLimiterModule.h" #include "../Module/OscilloscopeModule.h" +#include "../Module/AnalogClipperModule/AnalogClipperModule.h" PluginState::PluginState(juce::AudioProcessor& ap) : audioProcessor(ap), @@ -307,7 +308,8 @@ juce::AudioProcessorValueTreeState::ParameterLayout PluginState::createParameter OscilloscopeModuleDSP::addParameters(layout); ClassicBitcrusherModuleDSP::addParameters(layout); SpectrumBitcrusherModuleDSP::addParameters(layout); - SlewLimiterModuleDSP::addParameters(layout); + SlewLimiterModuleDSP::addParameters(layout); + AnalogClipperModuleDSP::addParameters(layout); return layout; -} \ No newline at end of file +}