From 707715bc2e3ad9ee64c7a555dbdfa51bbeec08f1 Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 17 Jul 2023 17:16:18 +0100 Subject: [PATCH 1/2] Refactor EffectDetails into EffectParameter --- Source/PluginProcessor.cpp | 20 +++---- Source/PluginProcessor.h | 26 ++++----- Source/audio/Effect.cpp | 26 ++++----- Source/audio/Effect.h | 10 ++-- Source/audio/EffectApplication.h | 65 +++++++++++++++++++++- Source/components/EffectComponent.cpp | 26 ++++----- Source/components/EffectsListComponent.cpp | 8 +-- 7 files changed, 120 insertions(+), 61 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index a25bb0a..5932171 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -37,39 +37,39 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() allEffects.push_back(std::make_shared( std::make_shared(), - std::vector(1, { "Bit Crush", "bitCrush", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Bit Crush", "bitCrush", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(), - std::vector(1, { "Bulge", "bulge", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Bulge", "bulge", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(), - std::vector(1, { "2D Rotate Speed", "rotateSpeed", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "2D Rotate Speed", "rotateSpeed", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(), - std::vector(1, { "Vector cancelling", "vectorCancelling", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Vector cancelling", "vectorCancelling", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(true), - std::vector(1, { "Vertical shift", "verticalDistort", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Vertical shift", "verticalDistort", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(false), - std::vector(1, { "Horizontal shift", "horizontalDistort", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Horizontal shift", "horizontalDistort", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( std::make_shared(), - std::vector(1, { "Smoothing", "smoothing", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Smoothing", "smoothing", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( wobbleEffect, - std::vector(1, { "Wobble", "wobble", 0.0, 0.0, 1.0, 0.001, true }) + std::vector(1, { "Wobble", "wobble", 0.0, 0.0, 1.0 }) )); allEffects.push_back(std::make_shared( delayEffect, - std::vector{{ "Delay Decay", "delayDecay", 0.0, 0.0, 1.0, 0.001, true }, { "Delay Length", "delayEchoLength", 0.5, 0.0, 1.0, 0.001, true }} + std::vector{{ "Delay Decay", "delayDecay", 0.0, 0.0, 1.0 }, { "Delay Length", "delayEchoLength", 0.5, 0.0, 1.0 }} )); allEffects.push_back(traceMax); allEffects.push_back(traceMin); @@ -189,7 +189,7 @@ void OscirenderAudioProcessor::addLuaSlider() { luaEffects.push_back(std::make_shared( std::make_shared(sliderName, *this), - std::vector(1, { "Lua " + sliderName, "lua" + sliderName, 0.0, 0.0, 1.0, 0.001, false }) + std::vector(1, { "Lua " + sliderName, "lua" + sliderName, 0.0, 0.0, 1.0, 0.001, false }) )); } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 8bde440..a9f5753 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -79,21 +79,21 @@ public: [this](int index, Vector2 input, const std::vector& values, double sampleRate) { frequency = values[0]; return input; - }, std::vector(1, { "Frequency", "frequency", 440.0, 0.0, 12000.0, 0.1, true }) + }, std::vector(1, { "Frequency", "frequency", 440.0, 0.0, 12000.0, 0.1 }) ); std::shared_ptr volumeEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { volume = values[0]; return input; - }, std::vector(1, { "Volume", "volume", 1.0, 0.0, 3.0, 0.001, true }) + }, std::vector(1, { "Volume", "volume", 1.0, 0.0, 3.0 }) ); std::shared_ptr thresholdEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { threshold = values[0]; return input; - }, std::vector(1, { "Threshold", "threshold", 3.0, 0.0, 3.0, 0.001, true }) + }, std::vector(1, { "Threshold", "threshold", 3.0, 0.0, 3.0 }) ); std::shared_ptr focalLength = std::make_shared( @@ -104,7 +104,7 @@ public: camera->setFocalLength(values[0]); } return input; - }, std::vector(1, { "Focal length", "focalLength", 1.0, 0.0, 2.0, 0.001, true }) + }, std::vector(1, { "Focal length", "focalLength", 1.0, 0.0, 2.0 }) ); std::shared_ptr rotateX = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -114,7 +114,7 @@ public: obj->setBaseRotationX(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Rotate x", "rotateX", 1.0, -1.0, 1.0, 0.001, true }) + }, std::vector(1, { "Rotate x", "rotateX", 1.0, -1.0, 1.0 }) ); std::shared_ptr rotateY = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -124,7 +124,7 @@ public: obj->setBaseRotationY(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Rotate y", "rotateY", 1.0, -1.0, 1.0, 0.001, true }) + }, std::vector(1, { "Rotate y", "rotateY", 1.0, -1.0, 1.0 }) ); std::shared_ptr rotateZ = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -134,7 +134,7 @@ public: obj->setBaseRotationZ(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Rotate z", "rotateZ", 0.0, -1.0, 1.0, 0.001, true }) + }, std::vector(1, { "Rotate z", "rotateZ", 0.0, -1.0, 1.0 }) ); std::shared_ptr currentRotateX = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -144,7 +144,7 @@ public: obj->setCurrentRotationX(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Current Rotate x", "currentRotateX", 0.0, 0.0, 1.0, 0.001, false }) + }, std::vector(1, { "Current Rotate x", "currentRotateX", 0.0, 0.0, 1.0, 0.001, false }) ); std::shared_ptr currentRotateY = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -154,7 +154,7 @@ public: obj->setCurrentRotationY(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Current Rotate y", "currentRotateY", 0.0, 0.0, 1.0, 0.001, false }) + }, std::vector(1, { "Current Rotate y", "currentRotateY", 0.0, 0.0, 1.0, 0.001, false }) ); std::shared_ptr currentRotateZ = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -164,7 +164,7 @@ public: obj->setCurrentRotationZ(values[0] * std::numbers::pi); } return input; - }, std::vector(1, { "Current Rotate z", "currentRotateZ", 0.0, 0.0, 1.0, 0.001, false }) + }, std::vector(1, { "Current Rotate z", "currentRotateZ", 0.0, 0.0, 1.0, 0.001, false }) ); std::shared_ptr rotateSpeed = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { @@ -174,7 +174,7 @@ public: obj->setRotationSpeed(values[0]); } return input; - }, std::vector(1, { "Rotate speed", "rotateSpeed", 0.0, -1.0, 1.0, 0.001, true }) + }, std::vector(1, { "Rotate speed", "rotateSpeed", 0.0, -1.0, 1.0 }) ); std::atomic fixedRotateX = false; std::atomic fixedRotateY = false; @@ -236,14 +236,14 @@ private: traceMaxValue = values[0]; traceMaxEnabled = true; return input; - }, std::vector(1, { "Trace max", "traceMax", 1.0, 0.0, 1.0, 0.001, true }) + }, std::vector(1, { "Trace max", "traceMax", 1.0, 0.0, 1.0 }) ); std::shared_ptr traceMin = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { traceMinValue = values[0]; traceMinEnabled = true; return input; - }, std::vector(1, { "Trace min", "traceMin", 0.0, 0.0, 1.0, 0.001, true }) + }, std::vector(1, { "Trace min", "traceMin", 0.0, 0.0, 1.0 }) ); const double MIN_TRACE = 0.005; double traceMaxValue = traceMax->getValue(); diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index e4a4b79..4331284 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -1,21 +1,21 @@ #include "Effect.h" -Effect::Effect(std::shared_ptr effectApplication, std::vector details) : effectApplication(effectApplication), details(details) { - smoothValues = std::vector(details.size(), 0.0); +Effect::Effect(std::shared_ptr effectApplication, std::vector parameters) : effectApplication(effectApplication), parameters(parameters) { + smoothValues = std::vector(parameters.size(), 0.0); } -Effect::Effect(std::shared_ptr effectApplication, EffectDetails details) : Effect(effectApplication, std::vector{details}) {} +Effect::Effect(std::shared_ptr effectApplication, EffectParameter parameter) : Effect(effectApplication, std::vector{parameter}) {} -Effect::Effect(std::function&, double)> application, std::vector details) : application(application), details(details) { - smoothValues = std::vector(details.size(), 0.0); +Effect::Effect(std::function&, double)> application, std::vector parameters) : application(application), parameters(parameters) { + smoothValues = std::vector(parameters.size(), 0.0); } -Effect::Effect(std::function&, double)> application, EffectDetails details) : Effect(application, std::vector{details}) {} +Effect::Effect(std::function&, double)> application, EffectParameter parameter) : Effect(application, std::vector{parameter}) {} Vector2 Effect::apply(int index, Vector2 input) { - for (int i = 0; i < details.size(); i++) { - double weight = details[i].smoothValueChange ? 0.0005 : 1.0; - smoothValues[i] = (1.0 - weight) * smoothValues[i] + weight * details[i].value; + for (int i = 0; i < parameters.size(); i++) { + double weight = parameters[i].smoothValueChange ? 0.0005 : 1.0; + smoothValues[i] = (1.0 - weight) * smoothValues[i] + weight * parameters[i].value; } if (application) { return application(index, input, smoothValues, sampleRate); @@ -30,7 +30,7 @@ void Effect::apply() { } double Effect::getValue(int index) { - return details[index].value; + return parameters[index].value; } double Effect::getValue() { @@ -38,7 +38,7 @@ double Effect::getValue() { } void Effect::setValue(int index, double value) { - details[index].value = value; + parameters[index].value = value; } void Effect::setValue(double value) { @@ -54,9 +54,9 @@ void Effect::setPrecedence(int precedence) { } juce::String Effect::getId() { - return details[0].id; + return parameters[0].id; } juce::String Effect::getName() { - return details[0].name; + return parameters[0].name; } diff --git a/Source/audio/Effect.h b/Source/audio/Effect.h index 07c7a30..94ec7cd 100644 --- a/Source/audio/Effect.h +++ b/Source/audio/Effect.h @@ -5,10 +5,10 @@ class Effect { public: - Effect(std::shared_ptr effectApplication, std::vector details); - Effect(std::shared_ptr effectApplication, EffectDetails details); - Effect(std::function&, double)> application, std::vector details); - Effect(std::function&, double)> application, EffectDetails details); + Effect(std::shared_ptr effectApplication, std::vector parameters); + Effect(std::shared_ptr effectApplication, EffectParameter parameter); + Effect(std::function&, double)> application, std::vector parameters); + Effect(std::function&, double)> application, EffectParameter parameter); Vector2 apply(int index, Vector2 input); void apply(); @@ -21,7 +21,7 @@ public: juce::String getId(); juce::String getName(); - std::vector details; + std::vector parameters; private: diff --git a/Source/audio/EffectApplication.h b/Source/audio/EffectApplication.h index cb59448..1ba785c 100644 --- a/Source/audio/EffectApplication.h +++ b/Source/audio/EffectApplication.h @@ -2,20 +2,22 @@ #include "../shape/Vector2.h" #include -struct EffectDetails { +class EffectParameter : public juce::AudioProcessorParameter { +public: juce::String name; juce::String id; + // 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; std::atomic min = 0.0; std::atomic max = 1.0; std::atomic step = 0.001; std::atomic smoothValueChange = true; - EffectDetails(juce::String name, juce::String id, double value, double min, double max, double step, bool smoothValueChange) : name(name), id(id), value(value), min(min), max(max), step(step), smoothValueChange(smoothValueChange) {} + EffectParameter(juce::String name, juce::String id, double value, double min, double max, double step = 0.001, bool smoothValueChange = true) : name(name), id(id), value(value), min(min), max(max), step(step), smoothValueChange(smoothValueChange) {} // COPY CONSTRUCTOR SHOULD ONLY BE USED BEFORE // THE OBJECT IS USED IN MULTIPLE THREADS - EffectDetails(const EffectDetails& other) { + EffectParameter(const EffectParameter& other) { name = other.name; id = other.id; value.store(other.value.load()); @@ -24,6 +26,63 @@ struct EffectDetails { step.store(other.step.load()); smoothValueChange.store(other.smoothValueChange.load()); } + + juce::String getName(int maximumStringLength) const override { + return name.substring(0, maximumStringLength); + } + + juce::String getLabel() const override { + return juce::String(); + } + + float getValue() const override { + return value.load(); + } + + void setValue(float newValue) override { + value.store(newValue); + } + + float getDefaultValue() const override { + return 0.0f; + } + + int getNumSteps() const override { + return (max.load() - min.load()) / step.load(); + } + + bool isDiscrete() const override { + return false; + } + + 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(value, 3); + return string.substring(0, maximumStringLength); + } + + float getValueForText(const juce::String& text) const override { + return text.getFloatValue(); + } + + bool isAutomatable() const override { + return true; + } + + bool isMetaParameter() const override { + return false; + } + + juce::AudioProcessorParameter::Category getCategory() const override { + return juce::AudioProcessorParameter::genericParameter; + } }; class EffectApplication { diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp index 6efd3c4..a0ed3c0 100644 --- a/Source/components/EffectComponent.cpp +++ b/Source/components/EffectComponent.cpp @@ -18,42 +18,42 @@ void EffectComponent::componentSetup() { addAndMakeVisible(slider); addAndMakeVisible(selected); - EffectDetails details = effect.details[index]; + EffectParameter parameter = effect.parameters[index]; - slider.setRange(details.min, details.max, details.step); - slider.setValue(details.value, juce::dontSendNotification); + slider.setRange(parameter.min, parameter.max, parameter.step); + slider.setValue(parameter.value, juce::dontSendNotification); slider.setSliderStyle(juce::Slider::LinearHorizontal); slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); selected.setToggleState(false, juce::dontSendNotification); - min.textBox.setValue(details.min, juce::dontSendNotification); - max.textBox.setValue(details.max, juce::dontSendNotification); + min.textBox.setValue(parameter.min, juce::dontSendNotification); + max.textBox.setValue(parameter.max, juce::dontSendNotification); min.textBox.onValueChange = [this]() { double minValue = min.textBox.getValue(); double maxValue = max.textBox.getValue(); if (minValue >= maxValue) { - minValue = maxValue - effect.details[index].step; + minValue = maxValue - effect.parameters[index].step; min.textBox.setValue(minValue, juce::dontSendNotification); } - effect.details[index].min = minValue; - slider.setRange(effect.details[index].min, effect.details[index].max, effect.details[index].step); + effect.parameters[index].min = minValue; + slider.setRange(effect.parameters[index].min, effect.parameters[index].max, effect.parameters[index].step); }; max.textBox.onValueChange = [this]() { double minValue = min.textBox.getValue(); double maxValue = max.textBox.getValue(); if (maxValue <= minValue) { - maxValue = minValue + effect.details[index].step; + maxValue = minValue + effect.parameters[index].step; max.textBox.setValue(maxValue, juce::dontSendNotification); } - effect.details[index].max = maxValue; - slider.setRange(effect.details[index].min, effect.details[index].max, effect.details[index].step); + effect.parameters[index].max = maxValue; + slider.setRange(effect.parameters[index].min, effect.parameters[index].max, effect.parameters[index].step); }; - popupLabel.setText(details.name + " Settings", juce::dontSendNotification); + popupLabel.setText(parameter.name + " Settings", juce::dontSendNotification); popupLabel.setJustificationType(juce::Justification::centred); popupLabel.setFont(juce::Font(14.0f, juce::Font::bold)); } @@ -82,7 +82,7 @@ void EffectComponent::resized() { void EffectComponent::paint(juce::Graphics& g) { g.fillAll(juce::Colours::black); g.setColour(juce::Colours::white); - g.drawText(effect.details[index].name, textBounds, juce::Justification::left); + g.drawText(effect.parameters[index].name, textBounds, juce::Justification::left); } void EffectComponent::mouseDown(const juce::MouseEvent& event) { diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp index 9d6acdf..b4bc5e2 100644 --- a/Source/components/EffectsListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -1,12 +1,12 @@ #include "EffectsListComponent.h" EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr effect) : DraggableListBoxItem(lb, data, rn), effect(effect) { - auto details = effect->details; - for (int i = 0; i < details.size(); i++) { + auto parameters = effect->parameters; + for (int i = 0; i < parameters.size(); i++) { std::shared_ptr effectComponent = std::make_shared(*effect, i, i == 0); // using weak_ptr to avoid circular reference and memory leak std::weak_ptr weakEffectComponent = effectComponent; - effectComponent->slider.setValue(details[i].value, juce::dontSendNotification); + effectComponent->slider.setValue(parameters[i].value, juce::dontSendNotification); effectComponent->slider.onValueChange = [this, i, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { this->effect->setValue(i, effectComponent->slider.getValue()); @@ -81,7 +81,7 @@ void EffectsListComponent::resized() { int EffectsListBoxModel::getRowHeight(int row) { auto data = (AudioEffectListBoxItemData&)modelData; - return data.getEffect(row)->details.size() * 30; + return data.getEffect(row)->parameters.size() * 30; } bool EffectsListBoxModel::hasVariableHeightRows() const { From de3ab79052d54f6ea06223d826c000b4c5f759d7 Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 17 Jul 2023 20:09:13 +0100 Subject: [PATCH 2/2] Parameters are now controllable from DAW --- Source/PluginProcessor.cpp | 10 +++++ Source/audio/Effect.cpp | 6 +-- Source/audio/EffectApplication.h | 52 +++++++++++++++++----- Source/components/EffectComponent.cpp | 4 +- Source/components/EffectsListComponent.cpp | 2 +- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 5932171..15bae11 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -86,6 +86,16 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() for (int i = 0; i < 5; i++) { addLuaSlider(); } + + auto effects = allEffects; + effects.insert(effects.end(), permanentEffects.begin(), permanentEffects.end()); + effects.insert(effects.end(), luaEffects.begin(), luaEffects.end()); + + for (auto effect : effects) { + for (auto& parameter : effect->parameters) { + addParameter(¶meter); + } + } } OscirenderAudioProcessor::~OscirenderAudioProcessor() {} diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index 4331284..7b31729 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -15,7 +15,7 @@ Effect::Effect(std::function&, d Vector2 Effect::apply(int index, Vector2 input) { for (int i = 0; i < parameters.size(); i++) { double weight = parameters[i].smoothValueChange ? 0.0005 : 1.0; - smoothValues[i] = (1.0 - weight) * smoothValues[i] + weight * parameters[i].value; + smoothValues[i] = (1.0 - weight) * smoothValues[i] + weight * parameters[i].getValueUnnormalised(); } if (application) { return application(index, input, smoothValues, sampleRate); @@ -30,7 +30,7 @@ void Effect::apply() { } double Effect::getValue(int index) { - return parameters[index].value; + return parameters[index].getValueUnnormalised(); } double Effect::getValue() { @@ -38,7 +38,7 @@ double Effect::getValue() { } void Effect::setValue(int index, double value) { - parameters[index].value = value; + parameters[index].setUnnormalisedValueNotifyingHost(value); } void Effect::setValue(double value) { diff --git a/Source/audio/EffectApplication.h b/Source/audio/EffectApplication.h index 1ba785c..5060c2d 100644 --- a/Source/audio/EffectApplication.h +++ b/Source/audio/EffectApplication.h @@ -6,14 +6,13 @@ class EffectParameter : public juce::AudioProcessorParameter { public: juce::String name; juce::String id; - // 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; - std::atomic min = 0.0; - std::atomic max = 1.0; - std::atomic step = 0.001; + + std::atomic min = 0.0; + std::atomic max = 1.0; + std::atomic step = 0.001; std::atomic smoothValueChange = true; - EffectParameter(juce::String name, juce::String id, double value, double min, double max, double step = 0.001, bool smoothValueChange = true) : name(name), id(id), value(value), min(min), max(max), step(step), smoothValueChange(smoothValueChange) {} + 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) {} // COPY CONSTRUCTOR SHOULD ONLY BE USED BEFORE // THE OBJECT IS USED IN MULTIPLE THREADS @@ -34,13 +33,42 @@ public: 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, 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 value.load(); + return getNormalisedValue(value.load()); } + float getValueUnnormalised() const { + return value.load(); + } + void setValue(float newValue) override { - value.store(newValue); + value = getUnnormalisedValue(newValue); + } + + void setValueUnnormalised(float newValue) { + value = newValue; + } + + void setUnnormalisedValueNotifyingHost(float newValue) { + setValueNotifyingHost(getNormalisedValue(newValue)); } float getDefaultValue() const override { @@ -64,12 +92,12 @@ public: } juce::String getText(float value, int maximumStringLength) const override { - auto string = juce::String(value, 3); + auto string = juce::String(getUnnormalisedValue(value), 3); return string.substring(0, maximumStringLength); } float getValueForText(const juce::String& text) const override { - return text.getFloatValue(); + return getNormalisedValue(text.getFloatValue()); } bool isAutomatable() const override { @@ -83,6 +111,10 @@ public: 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.0; }; class EffectApplication { diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp index a0ed3c0..d8932c2 100644 --- a/Source/components/EffectComponent.cpp +++ b/Source/components/EffectComponent.cpp @@ -18,10 +18,10 @@ void EffectComponent::componentSetup() { addAndMakeVisible(slider); addAndMakeVisible(selected); - EffectParameter parameter = effect.parameters[index]; + EffectParameter& parameter = effect.parameters[index]; slider.setRange(parameter.min, parameter.max, parameter.step); - slider.setValue(parameter.value, juce::dontSendNotification); + slider.setValue(parameter.getValueUnnormalised(), juce::dontSendNotification); slider.setSliderStyle(juce::Slider::LinearHorizontal); slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp index b4bc5e2..f474083 100644 --- a/Source/components/EffectsListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -6,7 +6,7 @@ EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectList std::shared_ptr effectComponent = std::make_shared(*effect, i, i == 0); // using weak_ptr to avoid circular reference and memory leak std::weak_ptr weakEffectComponent = effectComponent; - effectComponent->slider.setValue(parameters[i].value, juce::dontSendNotification); + effectComponent->slider.setValue(parameters[i].getValueUnnormalised(), juce::dontSendNotification); effectComponent->slider.onValueChange = [this, i, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { this->effect->setValue(i, effectComponent->slider.getValue());