Dual-stage compressor (optical + VCA) with output transformer saturation. - Port-Hamiltonian T4B opto-cell model with implicit trapezoidal integration - Feed-forward VCA with 7 ratios, 6 attack/release presets, Dual release mode - 3 transformer types (Nickel/Iron/Steel) with 4x oversampled waveshaping - Analog-style needle VU meters, horizontal GR meters - Sidechain HPF, stereo link, independent section bypass - Full state save/restore, CI/CD for Windows/macOS/Linux
130 sor
5.1 KiB
C++
130 sor
5.1 KiB
C++
#include "DiscretePanel.h"
|
|
|
|
DiscretePanel::DiscretePanel()
|
|
{
|
|
titleLabel.setText ("DISCRETE", juce::dontSendNotification);
|
|
titleLabel.setJustificationType (juce::Justification::centred);
|
|
titleLabel.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textPrimary);
|
|
addAndMakeVisible (titleLabel);
|
|
|
|
auto setupKnob = [this] (juce::Slider& knob, juce::Label& label, const juce::String& name,
|
|
double min, double max, double def, const juce::String& suffix)
|
|
{
|
|
knob.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
|
|
knob.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 60, 14);
|
|
knob.setRange (min, max);
|
|
knob.setValue (def);
|
|
knob.setTextValueSuffix (suffix);
|
|
knob.getProperties().set (InstaShadowLookAndFeel::knobTypeProperty, "orange");
|
|
addAndMakeVisible (knob);
|
|
|
|
label.setText (name, juce::dontSendNotification);
|
|
label.setJustificationType (juce::Justification::centred);
|
|
label.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textSecondary);
|
|
addAndMakeVisible (label);
|
|
};
|
|
|
|
setupKnob (thresholdKnob, threshLabel, "Threshold", -40.0, 0.0, -20.0, " dB");
|
|
setupKnob (gainKnob, gainLabel, "Gain", 0.0, 20.0, 0.0, " dB");
|
|
|
|
ratioBox.addItem ("1.2:1", 1);
|
|
ratioBox.addItem ("2:1", 2);
|
|
ratioBox.addItem ("3:1", 3);
|
|
ratioBox.addItem ("4:1", 4);
|
|
ratioBox.addItem ("6:1", 5);
|
|
ratioBox.addItem ("10:1", 6);
|
|
ratioBox.addItem ("Flood", 7);
|
|
ratioBox.setSelectedId (2);
|
|
addAndMakeVisible (ratioBox);
|
|
ratioLabel.setText ("Ratio", juce::dontSendNotification);
|
|
ratioLabel.setJustificationType (juce::Justification::centred);
|
|
ratioLabel.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textSecondary);
|
|
addAndMakeVisible (ratioLabel);
|
|
|
|
attackBox.addItem ("0.1 ms", 1);
|
|
attackBox.addItem ("0.5 ms", 2);
|
|
attackBox.addItem ("1 ms", 3);
|
|
attackBox.addItem ("5 ms", 4);
|
|
attackBox.addItem ("10 ms", 5);
|
|
attackBox.addItem ("30 ms", 6);
|
|
attackBox.setSelectedId (3);
|
|
addAndMakeVisible (attackBox);
|
|
attackLabel.setText ("Attack", juce::dontSendNotification);
|
|
attackLabel.setJustificationType (juce::Justification::centred);
|
|
attackLabel.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textSecondary);
|
|
addAndMakeVisible (attackLabel);
|
|
|
|
releaseBox.addItem ("100 ms", 1);
|
|
releaseBox.addItem ("250 ms", 2);
|
|
releaseBox.addItem ("500 ms", 3);
|
|
releaseBox.addItem ("800 ms", 4);
|
|
releaseBox.addItem ("1.2 s", 5);
|
|
releaseBox.addItem ("Dual", 6);
|
|
releaseBox.setSelectedId (3);
|
|
addAndMakeVisible (releaseBox);
|
|
releaseLabel.setText ("Release", juce::dontSendNotification);
|
|
releaseLabel.setJustificationType (juce::Justification::centred);
|
|
releaseLabel.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textSecondary);
|
|
addAndMakeVisible (releaseLabel);
|
|
|
|
bypassToggle.setButtonText ("");
|
|
addAndMakeVisible (bypassToggle);
|
|
bypassLabel.setText ("Bypass", juce::dontSendNotification);
|
|
bypassLabel.setJustificationType (juce::Justification::centred);
|
|
bypassLabel.setColour (juce::Label::textColourId, InstaShadowLookAndFeel::textSecondary);
|
|
addAndMakeVisible (bypassLabel);
|
|
}
|
|
|
|
void DiscretePanel::resized()
|
|
{
|
|
auto bounds = getLocalBounds().reduced (4);
|
|
titleLabel.setBounds (bounds.removeFromTop (18));
|
|
|
|
int availH = bounds.getHeight() - 22; // reserve for bypass
|
|
|
|
// Top: 2 knobs stacked vertically (~55%)
|
|
int knobSectionH = (int) (availH * 0.55f);
|
|
int singleKnobH = knobSectionH / 2;
|
|
|
|
auto k1 = bounds.removeFromTop (singleKnobH);
|
|
thresholdKnob.setBounds (k1.withTrimmedBottom (14).reduced (4, 0));
|
|
threshLabel.setBounds (k1.getX(), k1.getBottom() - 14, k1.getWidth(), 14);
|
|
|
|
auto k2 = bounds.removeFromTop (singleKnobH);
|
|
gainKnob.setBounds (k2.withTrimmedBottom (14).reduced (4, 0));
|
|
gainLabel.setBounds (k2.getX(), k2.getBottom() - 14, k2.getWidth(), 14);
|
|
|
|
// Gap between knobs and combos
|
|
bounds.removeFromTop (6);
|
|
|
|
// Middle: 3 combos stacked (~30%)
|
|
int comboSectionH = (int) (availH * 0.30f);
|
|
int comboH = comboSectionH / 3;
|
|
|
|
auto r1 = bounds.removeFromTop (comboH);
|
|
ratioLabel.setBounds (r1.removeFromLeft (r1.getWidth() / 3));
|
|
ratioBox.setBounds (r1.reduced (2, 1));
|
|
|
|
auto r2 = bounds.removeFromTop (comboH);
|
|
attackLabel.setBounds (r2.removeFromLeft (r2.getWidth() / 3));
|
|
attackBox.setBounds (r2.reduced (2, 1));
|
|
|
|
auto r3 = bounds.removeFromTop (comboH);
|
|
releaseLabel.setBounds (r3.removeFromLeft (r3.getWidth() / 3));
|
|
releaseBox.setBounds (r3.reduced (2, 1));
|
|
|
|
// Bottom: bypass
|
|
auto bpRow = bounds;
|
|
bypassLabel.setBounds (bpRow.removeFromLeft (bpRow.getWidth() / 2));
|
|
bypassToggle.setBounds (bpRow.reduced (4, 2));
|
|
}
|
|
|
|
void DiscretePanel::paint (juce::Graphics& g)
|
|
{
|
|
auto bounds = getLocalBounds().toFloat();
|
|
g.setColour (InstaShadowLookAndFeel::bgMedium.withAlpha (0.5f));
|
|
g.fillRoundedRectangle (bounds, 4.0f);
|
|
g.setColour (InstaShadowLookAndFeel::bgLight.withAlpha (0.3f));
|
|
g.drawRoundedRectangle (bounds, 4.0f, 1.0f);
|
|
}
|