#include "EffectComponent.h" #include "../LookAndFeel.h" EffectComponent::EffectComponent(Effect& effect, int index) : effect(effect), index(index) { addAndMakeVisible(slider); addChildComponent(lfoSlider); addAndMakeVisible(lfo); addAndMakeVisible(label); addAndMakeVisible(rangeButton); sidechainEnabled = effect.parameters[index]->sidechain != nullptr; if (sidechainEnabled) { sidechainButton = std::make_unique(effect.parameters[index]->name, BinaryData::microphone_svg, juce::Colours::white, juce::Colours::red, effect.parameters[index]->sidechain); sidechainButton->setTooltip("When enabled, the volume of the input audio controls the value of the slider, acting like a sidechain effect."); addAndMakeVisible(*sidechainButton); } slider.setSliderStyle(juce::Slider::LinearHorizontal); slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, slider.getTextBoxHeight()); if (effect.parameters[index]->step == 1.0) { slider.setNumDecimalPlacesToDisplay(0); } else { slider.setNumDecimalPlacesToDisplay(4); } lfoSlider.setSliderStyle(juce::Slider::LinearHorizontal); lfoSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, lfoSlider.getTextBoxHeight()); lfoSlider.setTextValueSuffix("Hz"); lfoSlider.setColour(sliderThumbOutlineColourId, juce::Colour(0xff00ff00)); lfoSlider.setNumDecimalPlacesToDisplay(3); label.setFont(juce::Font(14.0f)); lfo.addItem("Static", static_cast(LfoType::Static)); lfo.addItem("Sine", static_cast(LfoType::Sine)); lfo.addItem("Square", static_cast(LfoType::Square)); lfo.addItem("Seesaw", static_cast(LfoType::Seesaw)); lfo.addItem("Triangle", static_cast(LfoType::Triangle)); lfo.addItem("Sawtooth", static_cast(LfoType::Sawtooth)); lfo.addItem("Reverse Sawtooth", static_cast(LfoType::ReverseSawtooth)); lfo.addItem("Noise", static_cast(LfoType::Noise)); rangeButton.setTooltip("Click to change the range of the slider."); rangeButton.onClick = [this] { auto range = std::make_unique(this); range->setSize(200, 110); auto& myBox = juce::CallOutBox::launchAsynchronously(std::move(range), rangeButton.getScreenBounds(), nullptr); }; effect.addListener(index, this); setupComponent(); } EffectComponent::EffectComponent(Effect& effect) : EffectComponent(effect, 0) {} void EffectComponent::setSliderValueIfChanged(FloatParameter* parameter, juce::Slider& slider) { juce::String newSliderValue = juce::String(parameter->getValueUnnormalised(), 3); juce::String oldSliderValue = juce::String((float) slider.getValue(), 3); // only set the slider value if the parameter value is different so that we prefer the more // precise slider value. if (newSliderValue != oldSliderValue) { slider.setValue(parameter->getValueUnnormalised(), juce::dontSendNotification); } } void EffectComponent::setupComponent() { EffectParameter* parameter = effect.parameters[index]; setEnabled(effect.enabled == nullptr || effect.enabled->getBoolValue()); setTooltip(parameter->description); label.setText(parameter->name, juce::dontSendNotification); label.setInterceptsMouseClicks(false, false); slider.setRange(parameter->min, parameter->max, parameter->step); setSliderValueIfChanged(parameter, slider); slider.setDoubleClickReturnValue(true, parameter->defaultValue); lfoEnabled = parameter->lfo != nullptr && parameter->lfoRate != nullptr; if (lfoEnabled) { lfo.setSelectedId(parameter->lfo->getValueUnnormalised(), juce::dontSendNotification); lfo.onChange = [this]() { if (lfo.getSelectedId() != 0) { effect.parameters[index]->lfo->setUnnormalisedValueNotifyingHost(lfo.getSelectedId()); if (lfo.getSelectedId() == static_cast(LfoType::Static)) { lfoSlider.setVisible(false); slider.setVisible(true); } else { lfoSlider.setVisible(true); slider.setVisible(false); } } }; lfoSlider.setRange(parameter->lfoRate->min, parameter->lfoRate->max, parameter->lfoRate->step); setSliderValueIfChanged(parameter->lfoRate, lfoSlider); lfoSlider.setValue(parameter->lfoRate->getValueUnnormalised(), juce::dontSendNotification); lfoSlider.setSkewFactorFromMidPoint(parameter->lfoRate->min + 0.1 * (parameter->lfoRate->max - parameter->lfoRate->min)); lfoSlider.setDoubleClickReturnValue(true, 1.0); if (lfo.getSelectedId() == static_cast(LfoType::Static)) { lfoSlider.setVisible(false); slider.setVisible(true); } else { lfoSlider.setVisible(true); slider.setVisible(false); } lfoSlider.onValueChange = [this]() { effect.parameters[index]->lfoRate->setUnnormalisedValueNotifyingHost(lfoSlider.getValue()); }; } if (sidechainEnabled) { sidechainButton->onClick = [this] { effect.parameters[index]->sidechain->setBoolValueNotifyingHost(!effect.parameters[index]->sidechain->getBoolValue()); }; } if (sidechainEnabled && effect.parameters[index]->sidechain->getBoolValue()) { slider.setEnabled(false); slider.setColour(sliderThumbOutlineColourId, juce::Colour(0xffff0000)); slider.setTooltip("Sidechain effect applied - click the microphone icon to disable this."); } else { slider.setEnabled(true); slider.setColour(sliderThumbOutlineColourId, findColour(sliderThumbOutlineColourId)); slider.setTooltip(""); } } EffectComponent::~EffectComponent() { effect.removeListener(index, this); } void EffectComponent::resized() { auto bounds = getLocalBounds(); auto componentBounds = bounds.removeFromRight(25); if (component != nullptr) { component->setBounds(componentBounds); } if (sidechainEnabled) { sidechainButton->setBounds(bounds.removeFromRight(20)); } bool drawingSmall = bounds.getWidth() < 3.5 * TEXT_WIDTH; if (lfoEnabled) { lfo.setBounds(bounds.removeFromRight(drawingSmall ? 70 : 100).reduced(0, 5)); } if (rangeButton.isVisible()) { rangeButton.setBounds(bounds.removeFromRight(20)); } bounds.removeFromLeft(5); label.setBounds(bounds.removeFromLeft(drawingSmall ? SMALL_TEXT_WIDTH : TEXT_WIDTH)); slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, drawingSmall ? SMALL_TEXT_BOX_WIDTH : TEXT_BOX_WIDTH, slider.getTextBoxHeight()); lfoSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, drawingSmall ? SMALL_TEXT_BOX_WIDTH : TEXT_BOX_WIDTH, lfoSlider.getTextBoxHeight()); slider.setBounds(bounds); lfoSlider.setBounds(bounds); } void EffectComponent::paint(juce::Graphics& g) { auto bounds = getLocalBounds(); auto length = effect.parameters.size(); auto isEnd = index == length - 1; auto isStart = index == 0; g.setColour(findColour(effectComponentBackgroundColourId, true)); juce::Path path; path.addRoundedRectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), OscirenderLookAndFeel::RECT_RADIUS, OscirenderLookAndFeel::RECT_RADIUS, false, isStart, false, isEnd); g.fillPath(path); } void EffectComponent::parameterValueChanged(int parameterIndex, float newValue) { triggerAsyncUpdate(); } void EffectComponent::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {} void EffectComponent::handleAsyncUpdate() { setupComponent(); getParentComponent()->repaint(); } void EffectComponent::setRangeEnabled(bool enabled) { rangeButton.setVisible(enabled); } void EffectComponent::setComponent(std::shared_ptr component) { this->component = component; addAndMakeVisible(component.get()); } void EffectComponent::setSliderOnValueChange() { slider.onValueChange = [this] { effect.setValue(index, slider.getValue()); }; }