diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index 1304b720..e4a4b793 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -37,10 +37,6 @@ double Effect::getValue() { return getValue(0); } -std::vector Effect::getDetails() { - return details; -} - void Effect::setValue(int index, double value) { details[index].value = value; } diff --git a/Source/audio/Effect.h b/Source/audio/Effect.h index cb853037..07c7a303 100644 --- a/Source/audio/Effect.h +++ b/Source/audio/Effect.h @@ -14,7 +14,6 @@ public: void apply(); double getValue(int index); double getValue(); - std::vector getDetails(); void setValue(int index, double value); void setValue(double value); int getPrecedence(); @@ -22,8 +21,10 @@ public: juce::String getId(); juce::String getName(); -private: std::vector details; + +private: + std::vector smoothValues; double frequency = 1.0; int precedence = -1; diff --git a/Source/audio/EffectApplication.h b/Source/audio/EffectApplication.h index 46b8fc93..cb594485 100644 --- a/Source/audio/EffectApplication.h +++ b/Source/audio/EffectApplication.h @@ -5,11 +5,25 @@ struct EffectDetails { juce::String name; juce::String id; - double value = 0.0; - double min = 0.0; - double max = 1.0; - double step = 0.001; - bool smoothValueChange = true; + 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) {} + + // COPY CONSTRUCTOR SHOULD ONLY BE USED BEFORE + // THE OBJECT IS USED IN MULTIPLE THREADS + EffectDetails(const EffectDetails& other) { + name = other.name; + id = other.id; + value.store(other.value.load()); + min.store(other.min.load()); + max.store(other.max.load()); + step.store(other.step.load()); + smoothValueChange.store(other.smoothValueChange.load()); + } }; class EffectApplication { diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp index 513f4753..563da91b 100644 --- a/Source/components/EffectComponent.cpp +++ b/Source/components/EffectComponent.cpp @@ -1,16 +1,14 @@ #include "EffectComponent.h" -EffectComponent::EffectComponent(EffectDetails details) : details(details) { +EffectComponent::EffectComponent(Effect& effect, int index) : effect(effect), index(index) { componentSetup(); - slider.setRange(details.min, details.max, details.step); - slider.setValue(details.value, juce::dontSendNotification); } -EffectComponent::EffectComponent(EffectDetails details, bool checkboxVisible) : EffectComponent(details) { - setCheckboxVisible(checkboxVisible); +EffectComponent::EffectComponent(Effect& effect, int index, bool checkboxVisible) : EffectComponent(effect, index) { + setCheckboxVisible(checkboxVisible); } -EffectComponent::EffectComponent(Effect& effect) : EffectComponent(effect.getDetails()[0]) {} +EffectComponent::EffectComponent(Effect& effect) : EffectComponent(effect, 0) {} EffectComponent::EffectComponent(Effect& effect, bool checkboxVisible) : EffectComponent(effect) { setCheckboxVisible(checkboxVisible); @@ -20,10 +18,32 @@ void EffectComponent::componentSetup() { addAndMakeVisible(slider); addAndMakeVisible(selected); + EffectDetails details = effect.details[index]; + + slider.setRange(details.min, details.max, details.step); + slider.setValue(details.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.onValueChange = [this]() { + effect.details[index].min = min.textBox.getValue(); + slider.setRange(effect.details[index].min, effect.details[index].max, effect.details[index].step); + }; + + max.textBox.onValueChange = [this]() { + effect.details[index].max = max.textBox.getValue(); + slider.setRange(effect.details[index].min, effect.details[index].max, effect.details[index].step); + }; + + popupLabel.setText(details.name + " Settings", juce::dontSendNotification); + popupLabel.setJustificationType(juce::Justification::centred); + popupLabel.setFont(juce::Font(14.0f, juce::Font::bold)); } @@ -50,7 +70,19 @@ void EffectComponent::resized() { void EffectComponent::paint(juce::Graphics& g) { g.fillAll(juce::Colours::black); g.setColour(juce::Colours::white); - g.drawText(details.name, textBounds, juce::Justification::left); + g.drawText(effect.details[index].name, textBounds, juce::Justification::left); +} + +void EffectComponent::mouseDown(const juce::MouseEvent& event) { + juce::PopupMenu menu; + + menu.addCustomItem(1, popupLabel, 200, 30, false); + menu.addCustomItem(2, min, 160, 40, false); + menu.addCustomItem(3, max, 160, 40, false); + + menu.showMenuAsync(juce::PopupMenu::Options(), [this](int result) { + + }); } void EffectComponent::setComponent(std::shared_ptr component) { diff --git a/Source/components/EffectComponent.h b/Source/components/EffectComponent.h index d2e3866c..7d42f9d2 100644 --- a/Source/components/EffectComponent.h +++ b/Source/components/EffectComponent.h @@ -2,24 +2,27 @@ #include #include "../PluginProcessor.h" #include "../audio/Effect.h" +#include "LabelledTextBox.h" class EffectComponent : public juce::Component { public: - EffectComponent(EffectDetails details); - EffectComponent(EffectDetails details, bool checkboxVisible); + EffectComponent(Effect& effect, int index); + EffectComponent(Effect& effect, int index, bool checkboxVisible); EffectComponent(Effect& effect); EffectComponent(Effect& effect, bool checkboxVisible); ~EffectComponent(); void resized() override; void paint(juce::Graphics& g) override; + void mouseDown(const juce::MouseEvent& event) override; void setCheckboxVisible(bool visible); void setComponent(std::shared_ptr component); juce::Slider slider; - EffectDetails details; + Effect& effect; + int index; juce::ToggleButton selected; private: @@ -28,5 +31,9 @@ private: juce::Rectangle textBounds; std::shared_ptr component; + juce::Label popupLabel; + LabelledTextBox min{"Min"}; + LabelledTextBox max{"Max"}; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectComponent) }; diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp index 4f1a9e8c..e82f0bb6 100644 --- a/Source/components/EffectsListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -1,40 +1,40 @@ #include "EffectsListComponent.h" EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr effect) : DraggableListBoxItem(lb, data, rn), effect(effect) { - auto details = effect->getDetails(); + auto details = effect->details; for (int i = 0; i < details.size(); i++) { - std::shared_ptr effectComponent = std::make_shared(details[i], i == 0); + 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(details[i].value, juce::dontSendNotification); effectComponent->slider.onValueChange = [this, i, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { this->effect->setValue(i, effectComponent->slider.getValue()); } - }; + }; - if (i == 0) { - bool isSelected = false; + if (i == 0) { + bool isSelected = false; - { - juce::SpinLock::ScopedLockType lock(data.audioProcessor.effectsLock); - // check if effect is in audioProcessor enabled effects - for (auto processorEffect : data.audioProcessor.enabledEffects) { - if (processorEffect->getId() == effect->getId()) { - isSelected = true; - break; + { + juce::SpinLock::ScopedLockType lock(data.audioProcessor.effectsLock); + // check if effect is in audioProcessor enabled effects + for (auto processorEffect : data.audioProcessor.enabledEffects) { + if (processorEffect->getId() == effect->getId()) { + isSelected = true; + break; + } } } - } - effectComponent->selected.setToggleState(isSelected, juce::dontSendNotification); + effectComponent->selected.setToggleState(isSelected, juce::dontSendNotification); effectComponent->selected.onClick = [this, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { auto data = (AudioEffectListBoxItemData&)modelData; juce::SpinLock::ScopedLockType lock(data.audioProcessor.effectsLock); data.setSelected(rowNum, effectComponent->selected.getToggleState()); } - }; - } + }; + } listModel.addComponent(effectComponent); } @@ -81,7 +81,7 @@ void EffectsListComponent::resized() { int EffectsListBoxModel::getRowHeight(int row) { auto data = (AudioEffectListBoxItemData&)modelData; - return data.getEffect(row)->getDetails().size() * 30; + return data.getEffect(row)->details.size() * 30; } bool EffectsListBoxModel::hasVariableHeightRows() const { diff --git a/Source/components/LabelledTextBox.h b/Source/components/LabelledTextBox.h new file mode 100644 index 00000000..2bb6ffb9 --- /dev/null +++ b/Source/components/LabelledTextBox.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include "SliderTextBox.h" + +class LabelledTextBox : public juce::Component { + public: + LabelledTextBox(juce::String text) { + addAndMakeVisible(label); + addAndMakeVisible(textBox); + label.setText(text, juce::dontSendNotification); + label.setJustificationType(juce::Justification::centredLeft); + label.setFont(juce::Font(14.0f)); + } + + ~LabelledTextBox() override {} + + void resized() override { + auto bounds = getLocalBounds(); + textBox.setBounds(bounds.removeFromRight(90)); + label.setBounds(bounds); + } + + juce::Label label; + SliderTextBox textBox; +}; diff --git a/Source/components/SliderTextBox.h b/Source/components/SliderTextBox.h new file mode 100644 index 00000000..fc142550 --- /dev/null +++ b/Source/components/SliderTextBox.h @@ -0,0 +1,20 @@ +#pragma once +#include + +class SliderTextBox : public juce::Slider { + public: + SliderTextBox() { + double RANGE = 999999; + double step = 0.01; + setRange(-RANGE, RANGE, step); + setTextBoxStyle(juce::Slider::TextBoxAbove, false, 90, getTextBoxHeight()); + setSliderStyle(juce::Slider::SliderStyle::IncDecButtons); + setIncDecButtonsMode(juce::Slider::IncDecButtonMode::incDecButtonsDraggable_AutoDirection); + setMouseDragSensitivity(2 * RANGE / step); + setSliderSnapsToMousePosition(false); + setColour(juce::Slider::trackColourId, juce::Colours::transparentBlack); + setSize(60, 20); + } + + ~SliderTextBox() override {} +}; diff --git a/osci-render.jucer b/osci-render.jucer index 5c35d785..5cafaa16 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -97,10 +97,13 @@ file="Source/components/EffectsListComponent.cpp"/> + +