422 lines
19 KiB
C++
422 lines
19 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file contains the basic framework code for a JUCE plugin editor.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "PluginProcessor.h"
|
|
#include "PluginEditor.h"
|
|
|
|
//==============================================================================
|
|
FluteSynthAudioProcessorEditor::FluteSynthAudioProcessorEditor (FluteSynthAudioProcessor& p)
|
|
: AudioProcessorEditor (&p), audioProcessor (p)
|
|
{
|
|
auto& apvts = audioProcessor.getAPVTS();
|
|
|
|
// Preset selector setup
|
|
auto presetNames = audioProcessor.getEmbeddedPresetNames();
|
|
if (presetNames.isEmpty())
|
|
presetNames.add ("Preset 1");
|
|
presetSelector.addItemList (presetNames, 1);
|
|
presetSelector.onChange = [this]()
|
|
{
|
|
audioProcessor.selectEmbeddedPreset (presetSelector.getSelectedItemIndex());
|
|
};
|
|
presetSelector.setSelectedItemIndex (juce::jlimit (0, presetSelector.getNumItems() - 1,
|
|
audioProcessor.getActiveEmbeddedPresetIndex()),
|
|
juce::dontSendNotification);
|
|
presetSelector.setLookAndFeel (&comboLook);
|
|
|
|
// Preset label styling
|
|
presetLabel.setJustificationType (juce::Justification::centred);
|
|
presetLabel.setColour (juce::Label::textColourId, juce::Colour::fromRGB (160, 155, 145));
|
|
presetLabel.setFont (juce::Font (13.0f, juce::Font::plain));
|
|
|
|
// Temperament selector setup
|
|
temperamentSelector.addItemList (
|
|
{ "Preset", "12-TET", "Werckmeister", "Kirnberger", "Meantone", "Pythagorean" }, 1);
|
|
temperamentSelector.setLookAndFeel (&comboLook);
|
|
|
|
// Temperament label styling
|
|
temperamentLabel.setJustificationType (juce::Justification::centred);
|
|
temperamentLabel.setColour (juce::Label::textColourId, juce::Colour::fromRGB (160, 155, 145));
|
|
temperamentLabel.setFont (juce::Font (13.0f, juce::Font::plain));
|
|
|
|
// Velocity Curve selector setup
|
|
velocityCurveSelector.addItemList (
|
|
{ "Light", "Normal", "Heavy", "Fixed" }, 1);
|
|
velocityCurveSelector.setLookAndFeel (&comboLook);
|
|
|
|
// Velocity Curve label styling
|
|
velocityCurveLabel.setJustificationType (juce::Justification::centred);
|
|
velocityCurveLabel.setColour (juce::Label::textColourId, juce::Colour::fromRGB (160, 155, 145));
|
|
velocityCurveLabel.setFont (juce::Font (13.0f, juce::Font::plain));
|
|
|
|
// Toggle buttons
|
|
soundboardEnable.setLookAndFeel (&toggleLook);
|
|
postRoomEnable.setLookAndFeel (&toggleLook);
|
|
|
|
// Slider setup helper
|
|
auto prepSlider = [] (juce::Slider& s, juce::String suffix)
|
|
{
|
|
s.setSliderStyle (juce::Slider::LinearVertical);
|
|
s.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 120, 18);
|
|
s.setTextValueSuffix (suffix);
|
|
};
|
|
|
|
prepSlider (soundboardMix, "");
|
|
prepSlider (soundboardT60, " s");
|
|
prepSlider (soundboardDamp, "");
|
|
prepSlider (postRoomMix, "");
|
|
prepSlider (stringSustain, " s");
|
|
prepSlider (hammerHardness, "");
|
|
prepSlider (pm2GainDb, " dB");
|
|
prepSlider (outputLpfCutoff, " Hz");
|
|
prepSlider (masterVolume, "");
|
|
|
|
soundboardMix.setLookAndFeel (&sliderLook);
|
|
soundboardT60.setLookAndFeel (&sliderLook);
|
|
soundboardDamp.setLookAndFeel (&sliderLook);
|
|
postRoomMix.setLookAndFeel (&sliderLook);
|
|
stringSustain.setLookAndFeel (&sliderLook);
|
|
hammerHardness.setLookAndFeel (&sliderLook);
|
|
pm2GainDb.setLookAndFeel (&sliderLook);
|
|
outputLpfCutoff.setLookAndFeel (&sliderLook);
|
|
masterVolume.setLookAndFeel (&sliderLook);
|
|
|
|
// Parameter attachments
|
|
soundboardEnableAttachment = std::make_unique<ButtonAttachment> (apvts, ParamIDs::soundboardEnable, soundboardEnable);
|
|
postRoomEnableAttachment = std::make_unique<ButtonAttachment> (apvts, ParamIDs::postRoomEnable, postRoomEnable);
|
|
soundboardMixAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::soundboardMix, soundboardMix);
|
|
soundboardT60Attachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::soundboardT60, soundboardT60);
|
|
soundboardDampAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::soundboardDamp, soundboardDamp);
|
|
postRoomMixAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::postRoomMix, postRoomMix);
|
|
stringSustainAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::decay, stringSustain);
|
|
hammerHardnessAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::feltStiffness, hammerHardness);
|
|
pm2GainAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::pm2GainDb, pm2GainDb);
|
|
outputLpfAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::outputLpfCutoff, outputLpfCutoff);
|
|
masterVolumeAttachment = std::make_unique<SliderAttachment> (apvts, ParamIDs::masterVolume, masterVolume);
|
|
temperamentAttachment = std::make_unique<ComboBoxAttachment> (apvts, ParamIDs::temperament, temperamentSelector);
|
|
velocityCurveAttachment = std::make_unique<ComboBoxAttachment> (apvts, ParamIDs::velocityCurve, velocityCurveSelector);
|
|
|
|
syncControlsFromParams();
|
|
startTimerHz (30);
|
|
|
|
// Add components
|
|
addAndMakeVisible (soundboardEnable);
|
|
addAndMakeVisible (postRoomEnable);
|
|
resetButton.setVisible (false);
|
|
addAndMakeVisible (presetSelector);
|
|
addAndMakeVisible (presetLabel);
|
|
addAndMakeVisible (temperamentSelector);
|
|
addAndMakeVisible (temperamentLabel);
|
|
addAndMakeVisible (velocityCurveSelector);
|
|
addAndMakeVisible (velocityCurveLabel);
|
|
addAndMakeVisible (soundboardMix);
|
|
addAndMakeVisible (soundboardDamp);
|
|
addAndMakeVisible (postRoomMix);
|
|
addAndMakeVisible (stringSustain);
|
|
addAndMakeVisible (hammerHardness);
|
|
addAndMakeVisible (pm2GainDb);
|
|
addAndMakeVisible (outputLpfCutoff);
|
|
addAndMakeVisible (masterVolume);
|
|
|
|
// Slider label styling
|
|
auto styleLabel = [] (juce::Label& label)
|
|
{
|
|
label.setJustificationType (juce::Justification::centred);
|
|
label.setColour (juce::Label::textColourId, juce::Colour::fromRGB (160, 155, 145));
|
|
label.setFont (juce::Font (13.0f, juce::Font::plain));
|
|
};
|
|
|
|
styleLabel (soundboardMixLabel);
|
|
styleLabel (soundboardDampLabel);
|
|
styleLabel (postRoomMixLabel);
|
|
styleLabel (stringSustainLabel);
|
|
styleLabel (hammerHardnessLabel);
|
|
styleLabel (pm2GainLabel);
|
|
styleLabel (outputLpfLabel);
|
|
styleLabel (masterVolumeLabel);
|
|
|
|
addAndMakeVisible (soundboardMixLabel);
|
|
addAndMakeVisible (soundboardDampLabel);
|
|
addAndMakeVisible (postRoomMixLabel);
|
|
addAndMakeVisible (stringSustainLabel);
|
|
addAndMakeVisible (hammerHardnessLabel);
|
|
addAndMakeVisible (pm2GainLabel);
|
|
addAndMakeVisible (outputLpfLabel);
|
|
addAndMakeVisible (masterVolumeLabel);
|
|
|
|
resetButton.onClick = [this]()
|
|
{
|
|
audioProcessor.resetToEmbeddedPreset();
|
|
};
|
|
|
|
setSize (1040, 520);
|
|
}
|
|
|
|
FluteSynthAudioProcessorEditor::~FluteSynthAudioProcessorEditor()
|
|
{
|
|
soundboardMix.setLookAndFeel (nullptr);
|
|
soundboardT60.setLookAndFeel (nullptr);
|
|
soundboardDamp.setLookAndFeel (nullptr);
|
|
postRoomMix.setLookAndFeel (nullptr);
|
|
stringSustain.setLookAndFeel (nullptr);
|
|
hammerHardness.setLookAndFeel (nullptr);
|
|
pm2GainDb.setLookAndFeel (nullptr);
|
|
outputLpfCutoff.setLookAndFeel (nullptr);
|
|
masterVolume.setLookAndFeel (nullptr);
|
|
presetSelector.setLookAndFeel (nullptr);
|
|
temperamentSelector.setLookAndFeel (nullptr);
|
|
soundboardEnable.setLookAndFeel (nullptr);
|
|
postRoomEnable.setLookAndFeel (nullptr);
|
|
velocityCurveSelector.setLookAndFeel (nullptr);
|
|
}
|
|
|
|
void FluteSynthAudioProcessorEditor::syncControlsFromParams()
|
|
{
|
|
auto& apvts = audioProcessor.getAPVTS();
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::soundboardEnable))
|
|
soundboardEnable.setToggleState (v->load() >= 0.5f, juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::postRoomEnable))
|
|
postRoomEnable.setToggleState (v->load() >= 0.5f, juce::dontSendNotification);
|
|
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::soundboardMix))
|
|
soundboardMix.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::soundboardDamp))
|
|
soundboardDamp.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::postRoomMix))
|
|
postRoomMix.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::decay))
|
|
stringSustain.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::feltStiffness))
|
|
hammerHardness.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::pm2GainDb))
|
|
pm2GainDb.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::outputLpfCutoff))
|
|
outputLpfCutoff.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::masterVolume))
|
|
masterVolume.setValue (v->load(), juce::dontSendNotification);
|
|
if (auto* v = apvts.getRawParameterValue (ParamIDs::velocityCurve))
|
|
velocityCurveSelector.setSelectedItemIndex ((int) std::round (v->load()),
|
|
juce::dontSendNotification);
|
|
if (presetSelector.getNumItems() > 0)
|
|
{
|
|
const int idx = juce::jlimit (0, presetSelector.getNumItems() - 1,
|
|
audioProcessor.getActiveEmbeddedPresetIndex());
|
|
presetSelector.setSelectedItemIndex (idx, juce::dontSendNotification);
|
|
}
|
|
}
|
|
|
|
void FluteSynthAudioProcessorEditor::timerCallback()
|
|
{
|
|
if (audioProcessor.consumePendingPresetUiSync())
|
|
syncControlsFromParams();
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
void FluteSynthAudioProcessorEditor::paint (juce::Graphics& g)
|
|
{
|
|
auto bounds = getLocalBounds().toFloat();
|
|
|
|
// Deep piano-black lacquer background - darker for more contrast
|
|
auto deepBlack = juce::Colour::fromRGB (2, 2, 3);
|
|
g.fillAll (deepBlack);
|
|
|
|
// Outer frame with subtle gradient
|
|
auto frameArea = bounds.reduced (4.0f);
|
|
juce::ColourGradient frameGradient (juce::Colour::fromRGB (14, 14, 18),
|
|
frameArea.getX(), frameArea.getY(),
|
|
juce::Colour::fromRGB (4, 4, 6),
|
|
frameArea.getRight(), frameArea.getBottom(),
|
|
false);
|
|
g.setGradientFill (frameGradient);
|
|
g.fillRoundedRectangle (frameArea, 12.0f);
|
|
|
|
// Inner content area - very dark
|
|
auto contentArea = frameArea.reduced (6.0f);
|
|
juce::ColourGradient contentGradient (juce::Colour::fromRGB (6, 6, 8),
|
|
contentArea.getCentreX(), contentArea.getY(),
|
|
juce::Colour::fromRGB (2, 2, 3),
|
|
contentArea.getCentreX(), contentArea.getBottom(),
|
|
false);
|
|
g.setGradientFill (contentGradient);
|
|
g.fillRoundedRectangle (contentArea, 8.0f);
|
|
|
|
// High-contrast gloss highlight - brighter and more defined
|
|
juce::Path glossPath;
|
|
glossPath.addRoundedRectangle (contentArea, 8.0f);
|
|
|
|
juce::ColourGradient gloss;
|
|
auto glossCenterX = contentArea.getX() + contentArea.getWidth() * 0.30f;
|
|
gloss.point1 = { glossCenterX - contentArea.getWidth() * 0.22f, contentArea.getY() };
|
|
gloss.point2 = { glossCenterX + contentArea.getWidth() * 0.32f, contentArea.getBottom() };
|
|
gloss.addColour (0.0, juce::Colours::transparentWhite);
|
|
gloss.addColour (0.26, juce::Colours::transparentWhite);
|
|
gloss.addColour (0.40, juce::Colours::white.withAlpha (0.22f)); // Brighter
|
|
gloss.addColour (0.48, juce::Colours::white.withAlpha (0.28f)); // Peak brightness - increased
|
|
gloss.addColour (0.56, juce::Colours::white.withAlpha (0.18f));
|
|
gloss.addColour (0.70, juce::Colours::transparentWhite);
|
|
gloss.addColour (1.0, juce::Colours::transparentWhite);
|
|
g.setGradientFill (gloss);
|
|
g.fillPath (glossPath);
|
|
|
|
// Additional vertical sheen for lacquer depth - brighter
|
|
juce::ColourGradient sheen (juce::Colours::white.withAlpha (0.16f),
|
|
contentArea.getCentreX() - contentArea.getWidth() * 0.12f,
|
|
contentArea.getY(),
|
|
juce::Colours::transparentWhite,
|
|
contentArea.getCentreX() - contentArea.getWidth() * 0.12f,
|
|
contentArea.getY() + contentArea.getHeight() * 0.45f,
|
|
false);
|
|
g.setGradientFill (sheen);
|
|
g.fillRoundedRectangle (contentArea.withHeight (contentArea.getHeight() * 0.35f), 8.0f);
|
|
|
|
// Subtle top edge highlight
|
|
g.setColour (juce::Colour::fromRGB (50, 50, 55));
|
|
g.drawHorizontalLine ((int) (contentArea.getY() + 1), contentArea.getX() + 20, contentArea.getRight() - 20);
|
|
|
|
// Decorative brass-like accent line below title
|
|
auto accentY = 58.0f;
|
|
juce::ColourGradient accentGradient (juce::Colours::transparentBlack,
|
|
contentArea.getX(), accentY,
|
|
juce::Colours::transparentBlack,
|
|
contentArea.getRight(), accentY,
|
|
false);
|
|
accentGradient.addColour (0.15, juce::Colour::fromRGB (80, 72, 55).withAlpha (0.0f));
|
|
accentGradient.addColour (0.35, juce::Colour::fromRGB (120, 108, 80).withAlpha (0.5f));
|
|
accentGradient.addColour (0.5, juce::Colour::fromRGB (140, 128, 95).withAlpha (0.6f));
|
|
accentGradient.addColour (0.65, juce::Colour::fromRGB (120, 108, 80).withAlpha (0.5f));
|
|
accentGradient.addColour (0.85, juce::Colour::fromRGB (80, 72, 55).withAlpha (0.0f));
|
|
g.setGradientFill (accentGradient);
|
|
g.fillRect (contentArea.getX(), accentY, contentArea.getWidth(), 1.5f);
|
|
|
|
// Section divider line above sliders
|
|
auto dividerY = 175.0f;
|
|
g.setColour (juce::Colour::fromRGB (30, 30, 35));
|
|
g.drawHorizontalLine ((int) dividerY, contentArea.getX() + 30, contentArea.getRight() - 30);
|
|
g.setColour (juce::Colour::fromRGB (12, 12, 15));
|
|
g.drawHorizontalLine ((int) dividerY + 1, contentArea.getX() + 30, contentArea.getRight() - 30);
|
|
|
|
// Title with elegant styling
|
|
g.setColour (juce::Colour::fromRGB (220, 215, 200));
|
|
juce::Font titleFont (juce::Font::getDefaultSerifFontName(), 30.0f, juce::Font::italic);
|
|
g.setFont (titleFont);
|
|
|
|
// Title shadow
|
|
g.setColour (juce::Colours::black.withAlpha (0.6f));
|
|
g.drawText ("Samedi Dimanche MusPiano",
|
|
juce::Rectangle<int> (1, 16, getWidth(), 40),
|
|
juce::Justification::centredTop);
|
|
|
|
// Title main
|
|
g.setColour (juce::Colour::fromRGB (230, 225, 210));
|
|
g.drawText ("Samedi Dimanche MusPiano",
|
|
juce::Rectangle<int> (0, 15, getWidth(), 40),
|
|
juce::Justification::centredTop);
|
|
|
|
// Stronger vignette corners for depth
|
|
auto vignetteSize = 180.0f;
|
|
juce::ColourGradient vignetteTL (juce::Colours::black.withAlpha (0.4f),
|
|
contentArea.getX(), contentArea.getY(),
|
|
juce::Colours::transparentBlack,
|
|
contentArea.getX() + vignetteSize, contentArea.getY() + vignetteSize,
|
|
true);
|
|
g.setGradientFill (vignetteTL);
|
|
g.fillRect (contentArea.getX(), contentArea.getY(), vignetteSize, vignetteSize);
|
|
|
|
juce::ColourGradient vignetteBR (juce::Colours::black.withAlpha (0.45f),
|
|
contentArea.getRight(), contentArea.getBottom(),
|
|
juce::Colours::transparentBlack,
|
|
contentArea.getRight() - vignetteSize, contentArea.getBottom() - vignetteSize,
|
|
true);
|
|
g.setGradientFill (vignetteBR);
|
|
g.fillRect (contentArea.getRight() - vignetteSize, contentArea.getBottom() - vignetteSize, vignetteSize, vignetteSize);
|
|
|
|
// Inner frame border
|
|
g.setColour (juce::Colour::fromRGB (25, 25, 30));
|
|
g.drawRoundedRectangle (contentArea.reduced (0.5f), 8.0f, 1.0f);
|
|
|
|
// Outer frame border with subtle brass tint
|
|
g.setColour (juce::Colour::fromRGB (40, 38, 34));
|
|
g.drawRoundedRectangle (frameArea.reduced (0.5f), 12.0f, 1.5f);
|
|
}
|
|
|
|
void FluteSynthAudioProcessorEditor::resized()
|
|
{
|
|
auto area = getLocalBounds().reduced (20);
|
|
|
|
// Title area - increased space
|
|
area.removeFromTop (55);
|
|
|
|
// Control row - dropdowns and toggles (moved up with more spacing)
|
|
auto controlRow = area.removeFromTop (90);
|
|
controlRow.removeFromTop (8); // Add top padding
|
|
|
|
// Preset dropdown
|
|
auto presetArea = controlRow.removeFromLeft (200);
|
|
presetLabel.setBounds (presetArea.removeFromTop (22));
|
|
presetSelector.setBounds (presetArea.removeFromTop (36).reduced (4, 2));
|
|
|
|
controlRow.removeFromLeft (20); // Spacing between dropdowns
|
|
|
|
// Temperament dropdown
|
|
auto temperArea = controlRow.removeFromLeft (200);
|
|
temperamentLabel.setBounds (temperArea.removeFromTop (22));
|
|
temperamentSelector.setBounds (temperArea.removeFromTop (36).reduced (4, 2));
|
|
|
|
controlRow.removeFromLeft (20); // Spacing between dropdowns
|
|
|
|
// Velocity curve dropdown
|
|
auto velocityArea = controlRow.removeFromLeft (200);
|
|
velocityCurveLabel.setBounds (velocityArea.removeFromTop (22));
|
|
velocityCurveSelector.setBounds (velocityArea.removeFromTop (36).reduced (4, 2));
|
|
|
|
controlRow.removeFromLeft (20); // Spacing before toggles
|
|
|
|
// Toggle buttons - vertically stacked for cleaner look
|
|
auto toggleArea = controlRow.removeFromLeft (150);
|
|
toggleArea.removeFromTop (4);
|
|
const int toggleHeight = 24;
|
|
const int toggleGap = 4;
|
|
soundboardEnable.setBounds (toggleArea.removeFromTop (toggleHeight));
|
|
toggleArea.removeFromTop (toggleGap);
|
|
postRoomEnable.setBounds (toggleArea.removeFromTop (toggleHeight));
|
|
|
|
resetButton.setBounds (0, 0, 0, 0);
|
|
|
|
// Spacing before sliders
|
|
area.removeFromTop (35);
|
|
|
|
// Slider section with labels above - smaller sliders with more spacing
|
|
auto sliderSection = area.removeFromTop (210);
|
|
const int numSliders = 8;
|
|
const int sliderWidth = sliderSection.getWidth() / numSliders;
|
|
const int labelHeight = 34;
|
|
const int sliderPadding = 16;
|
|
|
|
auto layoutSlider = [&] (juce::Slider& slider, juce::Label& label, int index)
|
|
{
|
|
juce::ignoreUnused (index);
|
|
auto sliderArea = sliderSection.removeFromLeft (sliderWidth);
|
|
sliderArea = sliderArea.reduced (sliderPadding, 0);
|
|
|
|
auto labelArea = sliderArea.removeFromTop (labelHeight);
|
|
label.setBounds (labelArea);
|
|
|
|
slider.setBounds (sliderArea.reduced (10, 8)); // More padding around sliders
|
|
};
|
|
|
|
layoutSlider (soundboardMix, soundboardMixLabel, 0);
|
|
layoutSlider (soundboardDamp, soundboardDampLabel, 1);
|
|
layoutSlider (postRoomMix, postRoomMixLabel, 2);
|
|
layoutSlider (stringSustain, stringSustainLabel, 3);
|
|
layoutSlider (hammerHardness, hammerHardnessLabel, 4);
|
|
layoutSlider (pm2GainDb, pm2GainLabel, 5);
|
|
layoutSlider (outputLpfCutoff, outputLpfLabel, 6);
|
|
layoutSlider (masterVolume, masterVolumeLabel, 7);
|
|
}
|