Initial release — InstaShadow mastering compressor v1.0

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
This commit is contained in:
hariel1985
2026-03-27 16:03:24 +01:00
commit a587a43ff9
33 fájl változott, egészen pontosan 2417 új sor hozzáadva és 0 régi sor törölve

129
Source/DiscretePanel.cpp Normal file
Fájl megtekintése

@@ -0,0 +1,129 @@
#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);
}