Upload files to "/"

This commit is contained in:
ed
2026-01-10 10:54:35 +00:00
commit fcfe75f59d
3 changed files with 1123 additions and 0 deletions

389
PluginEditor.h Normal file
View File

@@ -0,0 +1,389 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin editor.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "PluginProcessor.h"
//==============================================================================
/**
*/
class FluteSynthAudioProcessorEditor : public juce::AudioProcessorEditor,
private juce::Timer
{
public:
FluteSynthAudioProcessorEditor (FluteSynthAudioProcessor&);
~FluteSynthAudioProcessorEditor() override;
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
FluteSynthAudioProcessor& audioProcessor;
juce::ComboBox presetSelector;
juce::Label presetLabel { {}, "Preset" };
juce::ComboBox temperamentSelector;
juce::Label temperamentLabel { {}, "Temperament" };
juce::ComboBox velocityCurveSelector;
juce::Label velocityCurveLabel { {}, "Velocity Curve" };
juce::ToggleButton soundboardEnable { "Soundboard" };
juce::ToggleButton postRoomEnable { "Post Reverb" };
juce::TextButton resetButton { "Reset to preset" };
juce::Slider soundboardMix;
juce::Slider soundboardT60;
juce::Slider soundboardDamp;
juce::Slider postRoomMix;
juce::Slider stringSustain;
juce::Slider hammerHardness;
juce::Slider pm2GainDb;
juce::Slider outputLpfCutoff;
juce::Slider masterVolume;
juce::Label soundboardMixLabel { {}, "Soundboard\nMix" };
juce::Label soundboardT60Label { {}, "Reverb Decay" };
juce::Label soundboardDampLabel{ {}, "Damp" };
juce::Label postRoomMixLabel { {}, "Post Reverb" };
juce::Label stringSustainLabel { {}, "String\nSustain" };
juce::Label hammerHardnessLabel { {}, "Hammer\nHardness" };
juce::Label pm2GainLabel { {}, "PM2 Gain" };
juce::Label outputLpfLabel { {}, "Brightness" };
juce::Label masterVolumeLabel { {}, "Master" };
using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment;
using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
std::unique_ptr<ButtonAttachment> soundboardEnableAttachment;
std::unique_ptr<ButtonAttachment> postRoomEnableAttachment;
std::unique_ptr<SliderAttachment> soundboardMixAttachment;
std::unique_ptr<SliderAttachment> soundboardT60Attachment;
std::unique_ptr<SliderAttachment> soundboardDampAttachment;
std::unique_ptr<SliderAttachment> postRoomMixAttachment;
std::unique_ptr<SliderAttachment> stringSustainAttachment;
std::unique_ptr<SliderAttachment> hammerHardnessAttachment;
std::unique_ptr<SliderAttachment> pm2GainAttachment;
std::unique_ptr<SliderAttachment> outputLpfAttachment;
std::unique_ptr<SliderAttachment> masterVolumeAttachment;
std::unique_ptr<ComboBoxAttachment> temperamentAttachment;
std::unique_ptr<ComboBoxAttachment> velocityCurveAttachment;
//==============================================================================
// Custom LookAndFeel for elegant dropdown menus
struct ElegantComboBoxLookAndFeel : juce::LookAndFeel_V4
{
ElegantComboBoxLookAndFeel()
{
// Set popup menu colors
setColour (juce::PopupMenu::backgroundColourId, juce::Colour::fromRGB (18, 20, 24));
setColour (juce::PopupMenu::textColourId, juce::Colour::fromRGB (220, 220, 225));
setColour (juce::PopupMenu::highlightedBackgroundColourId, juce::Colour::fromRGB (60, 70, 85));
setColour (juce::PopupMenu::highlightedTextColourId, juce::Colours::white);
}
void drawComboBox (juce::Graphics& g, int width, int height, bool isButtonDown,
int buttonX, int buttonY, int buttonW, int buttonH,
juce::ComboBox& box) override
{
juce::ignoreUnused (buttonX, buttonY, buttonW, buttonH);
auto bounds = juce::Rectangle<float> (0, 0, (float) width, (float) height);
auto cornerSize = 3.0f; // Reduced from 6.0f
// Outer glow/shadow effect
g.setColour (juce::Colours::black.withAlpha (0.4f));
g.fillRoundedRectangle (bounds.translated (0, 2).reduced (1), cornerSize);
// Main background gradient - dark metallic
juce::ColourGradient bgGradient (juce::Colour::fromRGB (38, 42, 48),
bounds.getCentreX(), bounds.getY(),
juce::Colour::fromRGB (22, 24, 28),
bounds.getCentreX(), bounds.getBottom(),
false);
g.setGradientFill (bgGradient);
g.fillRoundedRectangle (bounds.reduced (1), cornerSize);
// Subtle inner highlight at top
juce::ColourGradient innerHighlight (juce::Colours::white.withAlpha (0.08f),
bounds.getCentreX(), bounds.getY() + 1,
juce::Colours::transparentWhite,
bounds.getCentreX(), bounds.getY() + height * 0.4f,
false);
g.setGradientFill (innerHighlight);
g.fillRoundedRectangle (bounds.reduced (2), cornerSize - 1);
// Border - subtle golden/brass tint for piano aesthetic
auto borderColour = isButtonDown ? juce::Colour::fromRGB (140, 130, 100)
: juce::Colour::fromRGB (80, 75, 65);
if (box.hasKeyboardFocus (false))
borderColour = juce::Colour::fromRGB (160, 150, 120);
g.setColour (borderColour);
g.drawRoundedRectangle (bounds.reduced (1.5f), cornerSize, 1.0f);
// Arrow indicator
auto arrowZone = bounds.removeFromRight ((float) height).reduced (8);
juce::Path arrow;
auto arrowH = arrowZone.getHeight() * 0.3f;
auto arrowW = arrowZone.getWidth() * 0.5f;
arrow.addTriangle (arrowZone.getCentreX() - arrowW * 0.5f, arrowZone.getCentreY() - arrowH * 0.3f,
arrowZone.getCentreX() + arrowW * 0.5f, arrowZone.getCentreY() - arrowH * 0.3f,
arrowZone.getCentreX(), arrowZone.getCentreY() + arrowH * 0.5f);
g.setColour (juce::Colour::fromRGB (180, 175, 165));
g.fillPath (arrow);
}
void drawPopupMenuBackground (juce::Graphics& g, int width, int height) override
{
auto bounds = juce::Rectangle<float> (0, 0, (float) width, (float) height);
// Shadow
g.setColour (juce::Colours::black.withAlpha (0.5f));
g.fillRoundedRectangle (bounds.translated (2, 3), 3.0f); // Reduced from 6.0f
// Main background
juce::ColourGradient bgGradient (juce::Colour::fromRGB (28, 32, 38),
bounds.getCentreX(), bounds.getY(),
juce::Colour::fromRGB (18, 20, 24),
bounds.getCentreX(), bounds.getBottom(),
false);
g.setGradientFill (bgGradient);
g.fillRoundedRectangle (bounds, 3.0f); // Reduced from 6.0f
// Border
g.setColour (juce::Colour::fromRGB (60, 58, 52));
g.drawRoundedRectangle (bounds.reduced (0.5f), 3.0f, 1.0f); // Reduced from 6.0f
}
void drawPopupMenuItem (juce::Graphics& g, const juce::Rectangle<int>& area,
bool isSeparator, bool isActive, bool isHighlighted,
bool isTicked, bool hasSubMenu,
const juce::String& text, const juce::String& shortcutKeyText,
const juce::Drawable* icon, const juce::Colour* textColour) override
{
juce::ignoreUnused (shortcutKeyText, icon, textColour, hasSubMenu);
if (isSeparator)
{
auto r = area.reduced (5, 0).toFloat();
r.removeFromTop ((float) r.getHeight() * 0.5f - 0.5f);
g.setColour (juce::Colour::fromRGB (50, 52, 58));
g.fillRect (r.removeFromTop (1.0f));
return;
}
auto r = area.reduced (2);
if (isHighlighted && isActive)
{
// Highlighted background with subtle gradient
juce::ColourGradient hlGradient (juce::Colour::fromRGB (55, 65, 80),
(float) r.getCentreX(), (float) r.getY(),
juce::Colour::fromRGB (45, 52, 65),
(float) r.getCentreX(), (float) r.getBottom(),
false);
g.setGradientFill (hlGradient);
g.fillRoundedRectangle (r.toFloat(), 4.0f);
}
auto textColourToUse = isHighlighted ? juce::Colours::white
: juce::Colour::fromRGB (200, 200, 205);
if (! isActive)
textColourToUse = textColourToUse.withAlpha (0.4f);
g.setColour (textColourToUse);
g.setFont (getPopupMenuFont().withHeight (15.0f));
auto textArea = r.reduced (12, 0);
if (isTicked)
{
// Checkmark for selected item
auto tickArea = textArea.removeFromLeft (20);
juce::Path tick;
tick.startNewSubPath ((float) tickArea.getX() + 4, (float) tickArea.getCentreY());
tick.lineTo ((float) tickArea.getX() + 8, (float) tickArea.getCentreY() + 4);
tick.lineTo ((float) tickArea.getX() + 14, (float) tickArea.getCentreY() - 4);
g.setColour (juce::Colour::fromRGB (180, 170, 140));
g.strokePath (tick, juce::PathStrokeType (2.0f));
}
g.drawFittedText (text, textArea, juce::Justification::centredLeft, 1);
}
juce::Font getComboBoxFont (juce::ComboBox&) override
{
return juce::Font (15.0f);
}
void positionComboBoxText (juce::ComboBox& box, juce::Label& label) override
{
label.setBounds (12, 0, box.getWidth() - 30, box.getHeight());
label.setFont (getComboBoxFont (box));
label.setColour (juce::Label::textColourId, juce::Colour::fromRGB (220, 218, 212));
}
};
//==============================================================================
// Custom LookAndFeel for toggle buttons
struct ElegantToggleLookAndFeel : juce::LookAndFeel_V4
{
void drawToggleButton (juce::Graphics& g, juce::ToggleButton& button,
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override
{
juce::ignoreUnused (shouldDrawButtonAsDown);
auto bounds = button.getLocalBounds().toFloat();
auto tickBounds = bounds.removeFromLeft (bounds.getHeight()).reduced (4);
// Checkbox background
juce::ColourGradient bgGradient (juce::Colour::fromRGB (35, 38, 44),
tickBounds.getCentreX(), tickBounds.getY(),
juce::Colour::fromRGB (20, 22, 26),
tickBounds.getCentreX(), tickBounds.getBottom(),
false);
g.setGradientFill (bgGradient);
g.fillRoundedRectangle (tickBounds, 4.0f);
// Border
auto borderColour = shouldDrawButtonAsHighlighted ? juce::Colour::fromRGB (100, 95, 80)
: juce::Colour::fromRGB (70, 68, 62);
g.setColour (borderColour);
g.drawRoundedRectangle (tickBounds.reduced (0.5f), 4.0f, 1.0f);
if (button.getToggleState())
{
// Filled state with warm glow
auto innerBounds = tickBounds.reduced (3);
juce::ColourGradient fillGradient (juce::Colour::fromRGB (160, 150, 120),
innerBounds.getCentreX(), innerBounds.getY(),
juce::Colour::fromRGB (120, 110, 85),
innerBounds.getCentreX(), innerBounds.getBottom(),
false);
g.setGradientFill (fillGradient);
g.fillRoundedRectangle (innerBounds, 2.0f);
// Checkmark
juce::Path tick;
auto cx = tickBounds.getCentreX();
auto cy = tickBounds.getCentreY();
tick.startNewSubPath (cx - 4, cy);
tick.lineTo (cx - 1, cy + 3);
tick.lineTo (cx + 5, cy - 4);
g.setColour (juce::Colour::fromRGB (30, 28, 24));
g.strokePath (tick, juce::PathStrokeType (2.2f, juce::PathStrokeType::curved));
}
// Label text
g.setColour (juce::Colour::fromRGB (210, 208, 200));
g.setFont (14.5f);
g.drawText (button.getButtonText(), bounds.reduced (4, 0),
juce::Justification::centredLeft, true);
}
};
//==============================================================================
// Custom LookAndFeel for sliders - compact design
struct DarkRectSliderLookAndFeel : juce::LookAndFeel_V4
{
void drawLinearSlider (juce::Graphics& g, int x, int y, int width, int height,
float sliderPos, float min, float max, const juce::Slider::SliderStyle style,
juce::Slider& slider) override
{
juce::ignoreUnused (min, max, style, slider);
// Track - narrower
auto trackWidth = width * 0.11f; // Further reduced
auto track = juce::Rectangle<float> ((float) x + (width - trackWidth) * 0.5f,
(float) y + 4.0f,
trackWidth,
(float) height - 8.0f);
// Track shadow
g.setColour (juce::Colours::black.withAlpha (0.3f));
g.fillRoundedRectangle (track.translated (1, 1), 2.0f);
// Track gradient
juce::ColourGradient trackGradient (juce::Colour::fromRGB (15, 15, 18),
track.getCentreX(), track.getY(),
juce::Colour::fromRGB (8, 8, 10),
track.getCentreX(), track.getBottom(),
false);
g.setGradientFill (trackGradient);
g.fillRoundedRectangle (track, 2.0f);
// Track border
g.setColour (juce::Colour::fromRGB (45, 45, 50));
g.drawRoundedRectangle (track, 2.0f, 1.0f);
// Fill from bottom to thumb position
auto fillTrack = track;
fillTrack.setTop (sliderPos);
juce::ColourGradient fillGradient (juce::Colour::fromRGB (80, 75, 60).withAlpha (0.4f),
fillTrack.getCentreX(), fillTrack.getY(),
juce::Colour::fromRGB (60, 55, 45).withAlpha (0.2f),
fillTrack.getCentreX(), fillTrack.getBottom(),
false);
g.setGradientFill (fillGradient);
g.fillRoundedRectangle (fillTrack, 2.0f);
// Thumb - smaller
float thumbW = (float) width * 0.50f;
float thumbH = 9.0f; // Reduced from 11.0f
float thumbX = (float) x + (width - thumbW) * 0.5f;
float thumbY = sliderPos - thumbH * 0.5f;
juce::Rectangle<float> thumb (thumbX, thumbY, thumbW, thumbH);
// Thumb shadow
g.setColour (juce::Colours::black.withAlpha (0.4f));
g.fillRoundedRectangle (thumb.translated (0, 1.5f), 2.0f);
// Thumb gradient
juce::ColourGradient thumbGradient (juce::Colour::fromRGB (130, 125, 115),
thumb.getCentreX(), thumb.getY(),
juce::Colour::fromRGB (70, 68, 62),
thumb.getCentreX(), thumb.getBottom(),
false);
g.setGradientFill (thumbGradient);
g.fillRoundedRectangle (thumb, 2.0f);
// Thumb highlight
auto highlightRect = thumb.reduced (1.5f).withHeight (thumb.getHeight() * 0.35f);
g.setColour (juce::Colours::white.withAlpha (0.12f));
g.fillRoundedRectangle (highlightRect, 1.0f);
// Thumb border
g.setColour (juce::Colour::fromRGB (25, 25, 28));
g.drawRoundedRectangle (thumb, 2.0f, 1.0f);
// Center notch on thumb
g.setColour (juce::Colour::fromRGB (40, 40, 44));
g.fillRect (thumb.getCentreX() - 5.0f, thumb.getCentreY() - 0.5f, 10.0f, 1.0f);
}
juce::Label* createSliderTextBox (juce::Slider& slider) override
{
auto* label = LookAndFeel_V4::createSliderTextBox (slider);
label->setColour (juce::Label::textColourId, juce::Colour::fromRGB (180, 178, 170));
label->setColour (juce::Label::backgroundColourId, juce::Colour::fromRGB (20, 22, 26));
label->setColour (juce::Label::outlineColourId, juce::Colour::fromRGB (50, 50, 55));
return label;
}
};
ElegantComboBoxLookAndFeel comboLook;
ElegantToggleLookAndFeel toggleLook;
DarkRectSliderLookAndFeel sliderLook;
void syncControlsFromParams();
void timerCallback() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluteSynthAudioProcessorEditor)
};