Per-pad FX chain, animated toggles, GR meter, simplified master panel

- FX moved from master bus to per-pad processing:
  each pad has its own Filter, Distortion, EQ, Compressor, Reverb
  via DrumPad::applyPadFx() with temp buffer rendering
- FxPanel now edits the selected pad's FX parameters
- Animated toggle switches with smooth lerp transition and glow
- Per-pad compressor GR meter connected to FxPanel display
- Master panel simplified: Volume/Tune/Pan + Limiter toggle + VU meter
- Master bus chain: Vol/Pan → Output Limiter (0dB brickwall) → VU
- Pointer glow reduced to half intensity (4 layers, narrower spread)
- Smooth 8-layer arc glow with exponential opacity falloff

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hariel1985
2026-03-23 06:42:47 +01:00
szülő 20b9fe2674
commit a0e83fa0a4
11 fájl változott, egészen pontosan 500 új sor hozzáadva és 210 régi sor törölve

Fájl megtekintése

@@ -17,6 +17,11 @@ FxPanel::FxPanel()
setupKnob (distMixSlider, distMixLabel, "Mix", 0.0, 1.0, 0.0, 0.01);
setupKnob (reverbSizeSlider, reverbSizeLabel, "Size", 0.0, 1.0, 0.3, 0.01);
setupKnob (reverbDecaySlider, reverbDecayLabel, "Decay", 0.0, 1.0, 0.5, 0.01);
setupToggle (compToggle);
setupToggle (eqToggle);
setupToggle (distToggle);
setupToggle (reverbToggle);
}
void FxPanel::setupKnob (juce::Slider& s, juce::Label& l, const juce::String& name,
@@ -44,6 +49,63 @@ void FxPanel::setupTitle (juce::Label& l, const juce::String& text)
addAndMakeVisible (l);
}
void FxPanel::setupToggle (juce::ToggleButton& t)
{
t.setToggleState (false, juce::dontSendNotification);
t.setButtonText ("");
addAndMakeVisible (t);
}
void FxPanel::setPad (DrumPad* pad)
{
currentPad = pad;
syncFromPad();
}
void FxPanel::syncFromPad()
{
if (currentPad == nullptr) return;
compThreshSlider.setValue (currentPad->fxCompThreshold, juce::dontSendNotification);
compRatioSlider.setValue (currentPad->fxCompRatio, juce::dontSendNotification);
compToggle.setToggleState (currentPad->fxCompEnabled, juce::dontSendNotification);
eqLoSlider.setValue (currentPad->fxEqLo, juce::dontSendNotification);
eqMidSlider.setValue (currentPad->fxEqMid, juce::dontSendNotification);
eqHiSlider.setValue (currentPad->fxEqHi, juce::dontSendNotification);
eqToggle.setToggleState (currentPad->fxEqEnabled, juce::dontSendNotification);
distDriveSlider.setValue (currentPad->fxDistDrive, juce::dontSendNotification);
distMixSlider.setValue (currentPad->fxDistMix, juce::dontSendNotification);
distToggle.setToggleState (currentPad->fxDistEnabled, juce::dontSendNotification);
reverbSizeSlider.setValue (currentPad->fxReverbSize, juce::dontSendNotification);
reverbDecaySlider.setValue (currentPad->fxReverbDecay, juce::dontSendNotification);
reverbToggle.setToggleState (currentPad->fxReverbEnabled, juce::dontSendNotification);
}
void FxPanel::syncToPad()
{
if (currentPad == nullptr) return;
currentPad->fxCompThreshold = (float) compThreshSlider.getValue();
currentPad->fxCompRatio = (float) compRatioSlider.getValue();
currentPad->fxCompEnabled = compToggle.getToggleState();
currentPad->fxEqLo = (float) eqLoSlider.getValue();
currentPad->fxEqMid = (float) eqMidSlider.getValue();
currentPad->fxEqHi = (float) eqHiSlider.getValue();
currentPad->fxEqEnabled = eqToggle.getToggleState();
currentPad->fxDistDrive = (float) distDriveSlider.getValue();
currentPad->fxDistMix = (float) distMixSlider.getValue();
currentPad->fxDistEnabled = distToggle.getToggleState();
currentPad->fxReverbSize = (float) reverbSizeSlider.getValue();
currentPad->fxReverbDecay = (float) reverbDecaySlider.getValue();
currentPad->fxReverbEnabled = reverbToggle.getToggleState();
}
void FxPanel::paint (juce::Graphics& g)
{
auto bounds = getLocalBounds().toFloat();
@@ -108,10 +170,18 @@ void FxPanel::resized()
distTitle.setFont (juce::FontOptions (titleSize, juce::Font::bold));
reverbTitle.setFont (juce::FontOptions (titleSize, juce::Font::bold));
int toggleW = std::max (28, (int) (36 * scale));
int toggleH = std::max (16, (int) (20 * scale));
int sectionTitleH = std::max (titleH, toggleH + 4);
auto layoutSection = [&] (juce::Rectangle<int> secArea, juce::Label& title,
juce::ToggleButton& toggle,
juce::Slider* sliders[], juce::Label* labels[], int count)
{
title.setBounds (secArea.removeFromTop (titleH).reduced (4, 0));
auto titleRow = secArea.removeFromTop (sectionTitleH).reduced (2, 0);
auto toggleArea = titleRow.removeFromLeft (toggleW + 4);
toggle.setBounds (toggleArea.withSizeKeepingCentre (toggleW, toggleH));
title.setBounds (titleRow);
int kw = secArea.getWidth() / count;
for (int i = 0; i < count; ++i)
{
@@ -122,12 +192,15 @@ void FxPanel::resized()
}
};
// Top-left: Compressor
// Top-left: Compressor (with GR meter area on the right)
{
auto sec = area.removeFromTop (rowH).removeFromLeft (halfW).reduced (4, 2);
auto compFullArea = area.removeFromTop (rowH).removeFromLeft (halfW).reduced (4, 2);
int grMeterW = std::max (10, (int) (14 * scale));
compGrArea = compFullArea.removeFromRight (grMeterW);
auto sec = compFullArea;
juce::Slider* s[] = { &compThreshSlider, &compRatioSlider };
juce::Label* l[] = { &compThreshLabel, &compRatioLabel };
layoutSection (sec, compTitle, s, l, 2);
layoutSection (sec, compTitle, compToggle, s, l, 2);
}
// Top-right: EQ (need to recalculate since we consumed area)
@@ -139,7 +212,7 @@ void FxPanel::resized()
{
juce::Slider* s[] = { &eqLoSlider, &eqMidSlider, &eqHiSlider };
juce::Label* l[] = { &eqLoLabel, &eqMidLabel, &eqHiLabel };
layoutSection (rightTop, eqTitle, s, l, 3);
layoutSection (rightTop, eqTitle, eqToggle, s, l, 3);
}
// Bottom-left: Distortion
@@ -149,7 +222,7 @@ void FxPanel::resized()
auto sec = bottomArea.removeFromLeft (halfW).reduced (4, 2);
juce::Slider* s[] = { &distDriveSlider, &distMixSlider };
juce::Label* l[] = { &distDriveLabel, &distMixLabel };
layoutSection (sec, distTitle, s, l, 2);
layoutSection (sec, distTitle, distToggle, s, l, 2);
}
// Bottom-right: Reverb
@@ -157,6 +230,48 @@ void FxPanel::resized()
auto sec = bottomArea.reduced (4, 2);
juce::Slider* s[] = { &reverbSizeSlider, &reverbDecaySlider };
juce::Label* l[] = { &reverbSizeLabel, &reverbDecayLabel };
layoutSection (sec, reverbTitle, s, l, 2);
layoutSection (sec, reverbTitle, reverbToggle, s, l, 2);
}
}
void FxPanel::paintOverChildren (juce::Graphics& g)
{
// Draw compressor GR meter
if (compGrArea.isEmpty()) return;
auto bar = compGrArea.toFloat().reduced (2, 4);
// Background
g.setColour (juce::Colour (0xff111122));
g.fillRoundedRectangle (bar, 2.0f);
// GR bar (grows downward from top, since GR is negative)
float grNorm = juce::jlimit (0.0f, 1.0f, std::abs (compGrDb) / 30.0f); // 30dB range
float barH = bar.getHeight() * grNorm;
if (barH > 0.5f)
{
auto filled = bar.removeFromTop (barH);
// Colour: green for light GR, orange for medium, red for heavy
juce::Colour grColour;
if (grNorm < 0.3f)
grColour = juce::Colour (0xff00cc44);
else if (grNorm < 0.6f)
grColour = juce::Colour (0xffccaa00);
else
grColour = juce::Colour (0xffff4422);
g.setColour (grColour);
g.fillRoundedRectangle (filled, 2.0f);
// Glow
g.setColour (grColour.withAlpha (0.2f));
g.fillRoundedRectangle (filled.expanded (2, 0), 3.0f);
}
// "GR" label
g.setColour (InstaDrumsLookAndFeel::textSecondary.withAlpha (0.6f));
g.setFont (juce::FontOptions (8.0f));
g.drawText ("GR", compGrArea.toFloat(), juce::Justification::centredBottom);
}