Add change detection to Display, WaveformDisplay, FilterDisplay, EnvelopeDisplay, and LFODisplay so repaint() is only called when values actually change. Gate updateVisuals() by currentTab so only the visible tab's displays are updated each timer tick. Similarly gate updateModList/updateMacroList to their respective tabs. Manage dynamically created controls via ownedControls unique_ptr vector instead of raw new leaks. Null-check parameter lookups before creating attachments. Hold engine control lock when reading matrix/macros/LFO shape data. Fix envelope display curve directions (attack rising from bottom, decay falling toward sustain, release falling toward bottom). Add tooltip to FilterDisplay noting the approximation. Replace ToggleButton raw param pointer with ParameterAttachment for proper gesture handling. Sync preset and scale combos to processor state.
135 lines
4.7 KiB
C++
135 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include <JuceHeader.h>
|
|
#include "PluginProcessor.h"
|
|
#include "GUI/SerumLookAndFeel.h"
|
|
#include "GUI/Knob.h"
|
|
#include "GUI/Slider.h"
|
|
#include "GUI/ToggleButton.h"
|
|
#include "GUI/Display.h"
|
|
#include "GUI/Panel.h"
|
|
#include "GUI/WaveformDisplay.h"
|
|
#include "GUI/FilterDisplay.h"
|
|
#include "GUI/EnvelopeDisplay.h"
|
|
#include "GUI/LFODisplay.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
// ===========================================================================
|
|
// SerumAlt editor: dark vector GUI with a tabbed layout, real-time
|
|
// visualisations, RAVE, macro controls and preset-scale (75%..200%) scaling.
|
|
// ===========================================================================
|
|
class PluginEditor : public juce::AudioProcessorEditor,
|
|
public juce::Timer
|
|
{
|
|
public:
|
|
explicit PluginEditor (SerumAltAudioProcessor& p);
|
|
~PluginEditor() override;
|
|
|
|
void paint (juce::Graphics&) override;
|
|
void resized() override;
|
|
void timerCallback() override;
|
|
bool keyPressed (const juce::KeyPress& key) override;
|
|
|
|
void setTab (int index);
|
|
|
|
private:
|
|
SerumAltAudioProcessor& processor;
|
|
SerumLookAndFeel laf;
|
|
|
|
static constexpr int kBaseW = 1120;
|
|
static constexpr int kBaseH = 740;
|
|
|
|
juce::Component root;
|
|
|
|
// --- tooltips ---
|
|
juce::TooltipWindow tooltipWindow;
|
|
|
|
// --- top bar ---
|
|
std::unique_ptr<juce::Drawable> logo;
|
|
juce::ComboBox presetCombo;
|
|
juce::ComboBox scaleCombo;
|
|
ToggleButton raveButton;
|
|
Knob masterKnob;
|
|
Display masterDisplay;
|
|
|
|
// --- tabs ---
|
|
juce::TextButton tabOsc, tabFilter, tabMod, tabFx, tabMacro;
|
|
juce::Component oscView, filterView, modView, fxView, macroView;
|
|
int currentTab = 0;
|
|
|
|
// --- OSC ---
|
|
Panel oscAPanel { "Oscillator A" }, oscBPanel { "Oscillator B" };
|
|
Panel subPanel { "Sub" }, noisePanel { "Noise" };
|
|
WaveformDisplay oscAWave, oscBWave;
|
|
std::vector<juce::Component*> oscAComponents, oscBComponents;
|
|
|
|
// --- FILTER ---
|
|
Panel filter1Panel { "Filter 1" }, filter2Panel { "Filter 2" }, routingPanel { "Routing" };
|
|
FilterDisplay filter1Display, filter2Display;
|
|
|
|
// --- MOD ---
|
|
std::array<Panel, kNumEnvelopes> envPanels { { Panel ("Env 1 (Amp)"), Panel ("Env 2 (Filter)"),
|
|
Panel ("Env 3"), Panel ("Env 4") } };
|
|
std::array<EnvelopeDisplay, kNumEnvelopes> envDisplays;
|
|
std::array<Panel, kNumLfos> lfoPanels { { Panel ("LFO 1"), Panel ("LFO 2"), Panel ("LFO 3"), Panel ("LFO 4") } };
|
|
std::array<LFODisplay, kNumLfos> lfoDisplays;
|
|
|
|
Panel matrixPanel { "Modulation Matrix" };
|
|
juce::ComboBox modSourceCombo, modTargetCombo;
|
|
Knob modDepthKnob { "Depth" };
|
|
ToggleButton modBipolarToggle;
|
|
juce::TextButton modAddButton, modRemoveButton, modClearButton;
|
|
juce::TextEditor modList;
|
|
|
|
// --- FX ---
|
|
std::array<Panel, kNumFxSlots> fxPanels;
|
|
std::array<juce::ComboBox*, kNumFxSlots> fxTypeCombos;
|
|
std::array<std::vector<Knob*>, kNumFxSlots> fxKnobs;
|
|
std::array<juce::TextButton*, kNumFxSlots> fxUp, fxDown;
|
|
|
|
// --- MACRO ---
|
|
std::array<Panel, kNumMacros> macroPanels;
|
|
std::array<Knob*, kNumMacros> macroKnobs;
|
|
Panel macroAssignPanel { "Macro Assignments" };
|
|
juce::ComboBox macroAssignTarget;
|
|
juce::ComboBox macroAssignIndex;
|
|
Knob macroDepthKnob { "Depth" };
|
|
juce::TextButton macroAssignButton, macroClearButton;
|
|
juce::TextEditor macroList;
|
|
|
|
std::vector<std::unique_ptr<juce::Component>> ownedControls;
|
|
|
|
// --- attachments ---
|
|
std::vector<std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment>> sliderAttachments;
|
|
std::vector<std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment>> comboAttachments;
|
|
|
|
int currentScaleIndex = -1;
|
|
|
|
// --- helpers ---
|
|
Knob* makeKnob (juce::Component* parent, const juce::String& name, const juce::String& paramId,
|
|
std::function<juce::String (float)> fmt = {}, const juce::String& tooltip = {});
|
|
juce::ComboBox* makeCombo (juce::Component* parent, const juce::String& paramId, const juce::StringArray& items,
|
|
const juce::String& tooltip = {});
|
|
ToggleButton* makeToggle (juce::Component* parent, const juce::String& label, const juce::String& paramId,
|
|
const juce::String& tooltip = {});
|
|
void cycleTab (int delta);
|
|
|
|
void buildTopBar();
|
|
void buildOscTab();
|
|
void buildFilterTab();
|
|
void buildModTab();
|
|
void buildFxTab();
|
|
void buildMacroTab();
|
|
void swapFxSlots (int a, int b);
|
|
void applyUiScale();
|
|
void updateVisuals();
|
|
void updateModList();
|
|
void updateMacroList();
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginEditor)
|
|
};
|
|
|
|
} // namespace serum
|