FIR quality selector now offers 6 choices: 512 (~6ms), 1024 (~12ms), 2048 (~23ms), 4096 (~46ms), 8192 (~93ms), 16384 (~186ms) Default changed to 2048 (~23ms) for lower latency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
248 sor
8.7 KiB
C++
248 sor
8.7 KiB
C++
#include "PluginEditor.h"
|
|
|
|
InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p)
|
|
: AudioProcessorEditor (p), processor (p)
|
|
{
|
|
setLookAndFeel (&customLookAndFeel);
|
|
juce::LookAndFeel::setDefaultLookAndFeel (&customLookAndFeel);
|
|
|
|
// Title
|
|
titleLabel.setFont (customLookAndFeel.getBoldFont (26.0f));
|
|
titleLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::accent);
|
|
addAndMakeVisible (titleLabel);
|
|
|
|
versionLabel.setFont (customLookAndFeel.getRegularFont (13.0f));
|
|
versionLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary);
|
|
versionLabel.setJustificationType (juce::Justification::centredRight);
|
|
addAndMakeVisible (versionLabel);
|
|
|
|
// Bypass
|
|
bypassToggle.setToggleState (processor.bypassed.load(), juce::dontSendNotification);
|
|
addAndMakeVisible (bypassToggle);
|
|
bypassLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
|
|
bypassLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary);
|
|
addAndMakeVisible (bypassLabel);
|
|
|
|
// New Band button
|
|
newBandButton.onClick = [this]
|
|
{
|
|
if (processor.getNumBands() < InstaLPEQProcessor::maxBands)
|
|
{
|
|
processor.addBand (1000.0f, 0.0f);
|
|
syncDisplayFromProcessor();
|
|
curveDisplay.setSelectedBand (processor.getNumBands() - 1);
|
|
}
|
|
};
|
|
addAndMakeVisible (newBandButton);
|
|
|
|
// Quality selector (FIR latency)
|
|
qualitySelector.addItem ("512 (~6ms)", 1);
|
|
qualitySelector.addItem ("1024 (~12ms)", 2);
|
|
qualitySelector.addItem ("2048 (~23ms)", 3);
|
|
qualitySelector.addItem ("4096 (~46ms)", 4);
|
|
qualitySelector.addItem ("8192 (~93ms)", 5);
|
|
qualitySelector.addItem ("16384 (~186ms)", 6);
|
|
qualitySelector.setSelectedId (3, juce::dontSendNotification); // default 2048
|
|
qualitySelector.onChange = [this]
|
|
{
|
|
int sel = qualitySelector.getSelectedId();
|
|
int order = sel + 8; // 1->9, 2->10, 3->11, 4->12, 5->13, 6->14
|
|
processor.setQuality (order);
|
|
};
|
|
addAndMakeVisible (qualitySelector);
|
|
qualityLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
|
|
qualityLabel.setJustificationType (juce::Justification::centredRight);
|
|
addAndMakeVisible (qualityLabel);
|
|
|
|
// EQ curve
|
|
curveDisplay.setListener (this);
|
|
addAndMakeVisible (curveDisplay);
|
|
|
|
// Node panel
|
|
nodePanel.setListener (this);
|
|
addAndMakeVisible (nodePanel);
|
|
|
|
// Master gain
|
|
masterGainSlider.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
|
|
masterGainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 60, 16);
|
|
masterGainSlider.setRange (-24.0, 24.0, 0.1);
|
|
masterGainSlider.setValue (0.0);
|
|
masterGainSlider.setTextValueSuffix (" dB");
|
|
addAndMakeVisible (masterGainSlider);
|
|
masterGainLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
|
|
masterGainLabel.setJustificationType (juce::Justification::centred);
|
|
addAndMakeVisible (masterGainLabel);
|
|
|
|
// Sizing
|
|
constrainer.setMinimumSize (700, 450);
|
|
constrainer.setMaximumSize (1920, 1080);
|
|
setConstrainer (&constrainer);
|
|
setResizable (true, true);
|
|
setSize (900, 650);
|
|
|
|
startTimerHz (30);
|
|
syncDisplayFromProcessor();
|
|
}
|
|
|
|
InstaLPEQEditor::~InstaLPEQEditor()
|
|
{
|
|
setLookAndFeel (nullptr);
|
|
}
|
|
|
|
void InstaLPEQEditor::paint (juce::Graphics& g)
|
|
{
|
|
auto bounds = getLocalBounds().toFloat();
|
|
|
|
// Background gradient
|
|
juce::ColourGradient bgGrad (InstaLPEQLookAndFeel::bgDark, 0, 0,
|
|
InstaLPEQLookAndFeel::bgDark.darker (0.3f), 0, bounds.getBottom(), false);
|
|
g.setGradientFill (bgGrad);
|
|
g.fillAll();
|
|
|
|
// Noise texture
|
|
customLookAndFeel.drawBackgroundTexture (g, getLocalBounds());
|
|
|
|
// Top header bar
|
|
float scale = (float) getHeight() / 650.0f;
|
|
int headerH = (int) std::max (28.0f, 36.0f * scale);
|
|
g.setColour (InstaLPEQLookAndFeel::bgDark.darker (0.4f));
|
|
g.fillRect (0, 0, getWidth(), headerH);
|
|
|
|
g.setColour (InstaLPEQLookAndFeel::bgLight.withAlpha (0.3f));
|
|
g.drawHorizontalLine (headerH, 0.0f, (float) getWidth());
|
|
}
|
|
|
|
void InstaLPEQEditor::resized()
|
|
{
|
|
auto bounds = getLocalBounds();
|
|
float scale = (float) getHeight() / 650.0f;
|
|
|
|
// Top bar
|
|
int headerH = (int) std::max (28.0f, 36.0f * scale);
|
|
auto header = bounds.removeFromTop (headerH);
|
|
int pad = (int) (8 * scale);
|
|
header.reduce (pad, 2);
|
|
|
|
titleLabel.setFont (customLookAndFeel.getBoldFont (std::max (18.0f, 26.0f * scale)));
|
|
titleLabel.setBounds (header.removeFromLeft (200));
|
|
|
|
versionLabel.setFont (customLookAndFeel.getRegularFont (std::max (10.0f, 13.0f * scale)));
|
|
versionLabel.setBounds (header.removeFromRight (60));
|
|
|
|
auto bypassArea = header.removeFromRight (80);
|
|
bypassLabel.setBounds (bypassArea.removeFromLeft (50));
|
|
bypassToggle.setBounds (bypassArea);
|
|
|
|
newBandButton.setBounds (header.removeFromRight ((int) (90 * scale)).reduced (2));
|
|
|
|
// Bottom master row
|
|
int masterH = (int) std::max (50.0f, 65.0f * scale);
|
|
auto masterArea = bounds.removeFromBottom (masterH).reduced (pad, 2);
|
|
|
|
masterGainLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
|
|
auto labelArea = masterArea.removeFromLeft (60);
|
|
masterGainLabel.setBounds (labelArea);
|
|
masterGainSlider.setBounds (masterArea.removeFromLeft (masterH));
|
|
|
|
// Quality selector on the right side of master row
|
|
qualityLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
|
|
auto qLabelArea = masterArea.removeFromRight (30);
|
|
qualityLabel.setBounds (qLabelArea);
|
|
qualitySelector.setBounds (masterArea.removeFromRight ((int) (130 * scale)).reduced (2, (masterH - 24) / 2));
|
|
|
|
// Node parameter panel (15% of remaining height)
|
|
int nodePanelH = (int) (bounds.getHeight() * 0.18f);
|
|
auto nodePanelArea = bounds.removeFromBottom (nodePanelH).reduced (pad, 2);
|
|
nodePanel.setBounds (nodePanelArea);
|
|
|
|
// EQ curve display (rest)
|
|
auto curveArea = bounds.reduced (pad, 2);
|
|
curveDisplay.setBounds (curveArea);
|
|
}
|
|
|
|
void InstaLPEQEditor::timerCallback()
|
|
{
|
|
// Sync bypass
|
|
processor.bypassed.store (bypassToggle.getToggleState());
|
|
processor.masterGainDb.store ((float) masterGainSlider.getValue());
|
|
|
|
// Update display with latest magnitude response
|
|
auto magDb = processor.getFIREngine().getMagnitudeResponseDb();
|
|
if (! magDb.empty())
|
|
{
|
|
curveDisplay.setMagnitudeResponse (magDb, processor.getCurrentSampleRate(),
|
|
processor.getFIREngine().getFIRLength());
|
|
}
|
|
|
|
// Sync bands from processor (in case of state restore)
|
|
auto currentBands = processor.getBands();
|
|
curveDisplay.setBands (currentBands);
|
|
|
|
// Update node panel if selected
|
|
int sel = curveDisplay.getSelectedBandIndex();
|
|
if (sel >= 0 && sel < (int) currentBands.size())
|
|
nodePanel.setSelectedBand (sel, ¤tBands[sel]);
|
|
}
|
|
|
|
// ============================================================
|
|
// EQCurveDisplay::Listener
|
|
// ============================================================
|
|
|
|
void InstaLPEQEditor::bandAdded (int /*index*/, float freq, float gainDb)
|
|
{
|
|
processor.addBand (freq, gainDb);
|
|
syncDisplayFromProcessor();
|
|
curveDisplay.setSelectedBand (processor.getNumBands() - 1);
|
|
}
|
|
|
|
void InstaLPEQEditor::bandRemoved (int index)
|
|
{
|
|
processor.removeBand (index);
|
|
curveDisplay.setSelectedBand (-1);
|
|
syncDisplayFromProcessor();
|
|
}
|
|
|
|
void InstaLPEQEditor::bandChanged (int index, const EQBand& band)
|
|
{
|
|
processor.setBand (index, band);
|
|
// Don't call syncDisplayFromProcessor here to avoid flicker during drag
|
|
auto currentBands = processor.getBands();
|
|
curveDisplay.setBands (currentBands);
|
|
|
|
if (index == nodePanel.getSelectedIndex() && index < (int) currentBands.size())
|
|
nodePanel.setSelectedBand (index, ¤tBands[index]);
|
|
}
|
|
|
|
void InstaLPEQEditor::selectedBandChanged (int index)
|
|
{
|
|
auto currentBands = processor.getBands();
|
|
if (index >= 0 && index < (int) currentBands.size())
|
|
nodePanel.setSelectedBand (index, ¤tBands[index]);
|
|
else
|
|
nodePanel.setSelectedBand (-1, nullptr);
|
|
}
|
|
|
|
// ============================================================
|
|
// NodeParameterPanel::Listener
|
|
// ============================================================
|
|
|
|
void InstaLPEQEditor::nodeParameterChanged (int bandIndex, const EQBand& band)
|
|
{
|
|
processor.setBand (bandIndex, band);
|
|
syncDisplayFromProcessor();
|
|
}
|
|
|
|
void InstaLPEQEditor::nodeDeleteRequested (int bandIndex)
|
|
{
|
|
processor.removeBand (bandIndex);
|
|
curveDisplay.setSelectedBand (-1);
|
|
nodePanel.setSelectedBand (-1, nullptr);
|
|
syncDisplayFromProcessor();
|
|
}
|
|
|
|
void InstaLPEQEditor::syncDisplayFromProcessor()
|
|
{
|
|
auto currentBands = processor.getBands();
|
|
curveDisplay.setBands (currentBands);
|
|
}
|