From 08458a4251942968cf54e15d21db06846f4591d3 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 20 Jul 2023 20:01:09 +0100 Subject: [PATCH] Add LFO and LFO Rate as parameters --- Source/PluginProcessor.cpp | 7 +- Source/audio/Effect.cpp | 6 +- Source/audio/EffectParameter.h | 213 ++++++++++++++++++++------ Source/components/EffectComponent.cpp | 4 +- 4 files changed, 175 insertions(+), 55 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 1991bfe..39241fa 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -106,8 +106,11 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() effects.insert(effects.end(), luaEffects.begin(), luaEffects.end()); for (auto effect : effects) { - for (auto parameter : effect->parameters) { - addParameter(parameter); + for (auto effectParameter : effect->parameters) { + auto parameters = effectParameter->getParameters(); + for (auto parameter : parameters) { + addParameter(parameter); + } } } } diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index 49163e4..d7b4f3e 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -55,16 +55,20 @@ void Effect::setPrecedence(int precedence) { void Effect::addListener(int index, juce::AudioProcessorParameter::Listener* listener) { parameters[index]->addListener(listener); + parameters[index]->lfo->addListener(listener); + parameters[index]->lfoRate->addListener(listener); if (enabled != nullptr) { enabled->addListener(listener); } } void Effect::removeListener(int index, juce::AudioProcessorParameter::Listener* listener) { - parameters[index]->removeListener(listener); if (enabled != nullptr) { enabled->removeListener(listener); } + parameters[index]->lfoRate->removeListener(listener); + parameters[index]->lfo->removeListener(listener); + parameters[index]->removeListener(listener); } void Effect::markEnableable(bool enable) { diff --git a/Source/audio/EffectParameter.h b/Source/audio/EffectParameter.h index bdc38da..91f4055 100644 --- a/Source/audio/EffectParameter.h +++ b/Source/audio/EffectParameter.h @@ -2,94 +2,81 @@ #include "../shape/Vector2.h" #include -enum class LfoType : int { - Static = 1, - Sine = 2, - Square = 3, - Seesaw = 4, - Triangle = 5, - Sawtooth = 6, - ReverseSawtooth = 7, - Noise = 8 -}; - -class EffectParameter : public juce::AudioProcessorParameter { +class FloatParameter : public juce::AudioProcessorParameter { public: juce::String name; juce::String id; - + std::atomic min = 0.0; std::atomic max = 1.0; std::atomic step = 0.001; - std::atomic smoothValueChange = true; - std::atomic lfoType = LfoType::Static; - EffectParameter(juce::String name, juce::String id, float value, float min, float max, float step = 0.001, bool smoothValueChange = true) : name(name), id(id), value(value), min(min), max(max), step(step), smoothValueChange(smoothValueChange) {} + FloatParameter(juce::String name, juce::String id, float value, float min, float max, float step = 0.001, juce::String label = "") : name(name), id(id), value(value), min(min), max(max), step(step), label(label) {} juce::String getName(int maximumStringLength) const override { return name.substring(0, maximumStringLength); } juce::String getLabel() const override { - return juce::String(); - } - + return label; + } + // returns value in range [0, 1] float getNormalisedValue(float value) const { // clip value to valid range - auto min = this->min.load(); - auto max = this->max.load(); + auto min = this->min.load(); + auto max = this->max.load(); value = juce::jlimit(min, max, value); // normalize value to range [0, 1] - return (value - min) / (max - min); - } + return (value - min) / (max - min); + } float getUnnormalisedValue(float value) const { - value = juce::jlimit(0.0f, 1.0f, value); + value = juce::jlimit(0.0f, 1.0f, value); auto min = this->min.load(); auto max = this->max.load(); return min + value * (max - min); - } + } float getValue() const override { return getNormalisedValue(value.load()); } float getValueUnnormalised() const { - return value.load(); - } + return value.load(); + } void setValue(float newValue) override { value = getUnnormalisedValue(newValue); - } + } void setValueUnnormalised(float newValue) { - value = newValue; - } + value = newValue; + } void setUnnormalisedValueNotifyingHost(float newValue) { - setValueNotifyingHost(getNormalisedValue(newValue)); - } + setValueNotifyingHost(getNormalisedValue(newValue)); + } float getDefaultValue() const override { - return 0.0f; - } + return 0.0f; + } int getNumSteps() const override { - return (max.load() - min.load()) / step.load(); - } + return (max.load() - min.load()) / step.load(); + } bool isDiscrete() const override { - return false; - } + return false; + } bool isBoolean() const override { - return false; - } + return false; + } bool isOrientationInverted() const override { - return false; - } + return false; + } juce::String getText(float value, int maximumStringLength) const override { auto string = juce::String(getUnnormalisedValue(value), 3); @@ -97,22 +84,148 @@ public: } float getValueForText(const juce::String& text) const override { - return getNormalisedValue(text.getFloatValue()); - } + return getNormalisedValue(text.getFloatValue()); + } bool isAutomatable() const override { - return true; - } + return true; + } bool isMetaParameter() const override { - return false; - } + return false; + } juce::AudioProcessorParameter::Category getCategory() const override { - return juce::AudioProcessorParameter::genericParameter; - } + return juce::AudioProcessorParameter::genericParameter; + } private: // value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range std::atomic value = 0.0; + juce::String label; +}; + +class IntParameter : public juce::AudioProcessorParameter { +public: + juce::String name; + juce::String id; + + std::atomic min = 0; + std::atomic max = 10; + + IntParameter(juce::String name, juce::String id, int value, int min, int max) : name(name), id(id), value(value), min(min), max(max) {} + + juce::String getName(int maximumStringLength) const override { + return name.substring(0, maximumStringLength); + } + + juce::String getLabel() const override { + return juce::String(); + } + + // returns value in range [0, 1] + float getNormalisedValue(float value) const { + // clip value to valid range + auto min = this->min.load(); + auto max = this->max.load(); + value = juce::jlimit(min, max, (int) value); + // normalize value to range [0, 1] + return (value - min) / (max - min); + } + + float getUnnormalisedValue(float value) const { + value = juce::jlimit(0.0f, 1.0f, value); + auto min = this->min.load(); + auto max = this->max.load(); + return min + value * (max - min); + } + + float getValue() const override { + return getNormalisedValue(value.load()); + } + + float getValueUnnormalised() const { + return value.load(); + } + + void setValue(float newValue) override { + value = getUnnormalisedValue(newValue); + } + + void setValueUnnormalised(float newValue) { + value = newValue; + } + + void setUnnormalisedValueNotifyingHost(float newValue) { + setValueNotifyingHost(getNormalisedValue(newValue)); + } + + float getDefaultValue() const override { + return 0; + } + + int getNumSteps() const override { + return max.load() - min.load() + 1; + } + + bool isDiscrete() const override { + return true; + } + + bool isBoolean() const override { + return false; + } + + bool isOrientationInverted() const override { + return false; + } + + juce::String getText(float value, int maximumStringLength) const override { + auto string = juce::String((int) getUnnormalisedValue(value)); + return string.substring(0, maximumStringLength); + } + + float getValueForText(const juce::String& text) const override { + return getNormalisedValue(text.getIntValue()); + } + + bool isAutomatable() const override { + return true; + } + + bool isMetaParameter() const override { + return false; + } + + juce::AudioProcessorParameter::Category getCategory() const override { + return juce::AudioProcessorParameter::genericParameter; + } + +private: + // value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range + std::atomic value = 0; +}; + +enum class LfoType : int { + Static = 1, + Sine = 2, + Square = 3, + Seesaw = 4, + Triangle = 5, + Sawtooth = 6, + ReverseSawtooth = 7, + Noise = 8 +}; + +class EffectParameter : public FloatParameter { +public: + std::atomic smoothValueChange = true; + IntParameter* lfo = new IntParameter(name + " LFO", id + "Lfo", 1, 1, 8); + FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", id + "LfoRate", 1.0f, 0.0f, 100.0f, 0.1f, "Hz"); + + std::vector getParameters() { + return { this, lfo, lfoRate }; + } + + EffectParameter(juce::String name, juce::String id, float value, float min, float max, float step = 0.001, bool smoothValueChange = true) : FloatParameter(name, id, value, min, max, step), smoothValueChange(smoothValueChange) {} }; \ No newline at end of file diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp index 7e80a28..cad5e23 100644 --- a/Source/components/EffectComponent.cpp +++ b/Source/components/EffectComponent.cpp @@ -42,11 +42,11 @@ void EffectComponent::setupComponent() { bool enabled = effect.enabled == nullptr || effect.enabled->getValue(); selected.setToggleState(enabled, juce::dontSendNotification); - lfo.setSelectedId(static_cast(parameter->lfoType.load()), juce::dontSendNotification); + lfo.setSelectedId(parameter->lfo->getValueUnnormalised(), juce::dontSendNotification); lfo.onChange = [this]() { if (lfo.getSelectedId() != 0) { - effect.parameters[index]->lfoType = static_cast(lfo.getSelectedId()); + effect.parameters[index]->lfo->setUnnormalisedValueNotifyingHost(lfo.getSelectedId()); } };