CrystalizerEQ v1.0.0
This commit is contained in:
parent
f270e4613e
commit
e239d3e955
@ -179,7 +179,6 @@ namespace AXIOM {
|
||||
float ratio = 1.125f;
|
||||
|
||||
float sizeFor(int step) const noexcept {
|
||||
// step = 0 => basePx, step = 1 => base*ratio, step = -1 => base/ratio, ...
|
||||
return basePx * std::pow(ratio, static_cast<float>(step));
|
||||
}
|
||||
};
|
||||
@ -556,7 +555,6 @@ namespace AXIOM {
|
||||
float bypassOpacity = 1.0f;
|
||||
|
||||
if (svgXml != nullptr) {
|
||||
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
|
||||
auto icon = juce::Drawable::createFromSVG(*svgXml);
|
||||
if (icon != nullptr) {
|
||||
juce::Colour colour;
|
||||
@ -570,8 +568,6 @@ namespace AXIOM {
|
||||
: Colours::ACCENTCOLOUR;
|
||||
}
|
||||
|
||||
|
||||
// Icon zeichnen
|
||||
icon->replaceColour(juce::Colours::black, colour);
|
||||
icon->drawWithin(g, iconBounds,
|
||||
juce::RectanglePlacement::centred, bypassOpacity);
|
||||
@ -594,7 +590,6 @@ namespace AXIOM {
|
||||
bool shouldDrawButtonAsDown) override {
|
||||
bool isHovered = button.isMouseOverOrDragging();
|
||||
bool isToggled = button.getToggleState();
|
||||
bool isEnabled = button.isEnabled();
|
||||
auto bounds = button.getLocalBounds().toFloat();
|
||||
auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 1.5f;
|
||||
auto hoverFactor = isHovered ? 1.05f : 1.0f;
|
||||
@ -602,8 +597,6 @@ namespace AXIOM {
|
||||
auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
|
||||
.withCentre(bounds.getCentre());
|
||||
|
||||
float bypassOpacity = 1.0f;
|
||||
|
||||
if (presetMenuButton != nullptr) {
|
||||
auto menuIcon = juce::Drawable::createFromSVG(*presetMenuButton);
|
||||
juce::Colour colour;
|
||||
@ -615,8 +608,6 @@ namespace AXIOM {
|
||||
: Colours::BACKGROUNDCOLOUR;
|
||||
}
|
||||
|
||||
|
||||
// Icon zeichnen
|
||||
menuIcon->replaceColour(juce::Colours::black, colour);
|
||||
menuIcon->drawWithin(g, iconBounds,
|
||||
juce::RectanglePlacement::centred, 1.0f);
|
||||
@ -641,7 +632,6 @@ namespace AXIOM {
|
||||
bool shouldDrawButtonAsHighlighted,
|
||||
bool shouldDrawButtonAsDown) override {
|
||||
const bool isHovered = button.isMouseOverOrDragging();
|
||||
const bool isToggled = button.getToggleState();
|
||||
auto bounds = button.getLocalBounds().toFloat();
|
||||
auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 2 * 0.95f;
|
||||
|
||||
@ -693,7 +683,6 @@ namespace AXIOM {
|
||||
|
||||
if (lowBypassIcon != nullptr && lowCutIcon != nullptr && lowBellIcon != nullptr && lowShelfIcon !=
|
||||
nullptr) {
|
||||
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
|
||||
auto bypassIcon = juce::Drawable::createFromSVG(*lowBypassIcon);
|
||||
auto cutIcon = juce::Drawable::createFromSVG(*lowCutIcon);
|
||||
auto bellIcon = juce::Drawable::createFromSVG(*lowBellIcon);
|
||||
@ -712,8 +701,6 @@ namespace AXIOM {
|
||||
: Colours::SURFACECOLOUR;
|
||||
}
|
||||
|
||||
|
||||
// Icon zeichnen
|
||||
if (button.getName() == "LowBypass") {
|
||||
bypassIcon->replaceColour(juce::Colours::black, colour);
|
||||
bypassIcon->drawWithin(g, iconBounds,
|
||||
@ -769,7 +756,6 @@ namespace AXIOM {
|
||||
|
||||
if (highBypassIcon != nullptr && highCutIcon != nullptr && highBellIcon != nullptr && highShelfIcon
|
||||
!= nullptr) {
|
||||
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
|
||||
auto bypassIcon = juce::Drawable::createFromSVG(*highBypassIcon);
|
||||
auto cutIcon = juce::Drawable::createFromSVG(*highCutIcon);
|
||||
auto bellIcon = juce::Drawable::createFromSVG(*highBellIcon);
|
||||
@ -788,8 +774,6 @@ namespace AXIOM {
|
||||
: Colours::SURFACECOLOUR;
|
||||
}
|
||||
|
||||
|
||||
// Icon zeichnen
|
||||
if (button.getName() == "HighBypass") {
|
||||
bypassIcon->replaceColour(juce::Colours::black, colour);
|
||||
bypassIcon->drawWithin(g, iconBounds,
|
||||
@ -862,17 +846,14 @@ namespace AXIOM {
|
||||
void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height,
|
||||
float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
|
||||
juce::Slider &slider) override {
|
||||
// Basis-Implementation - wird in Subklassen überschrieben
|
||||
auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
|
||||
auto centreX = x + width * 0.5f;
|
||||
auto centreY = y + height * 0.5f;
|
||||
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
|
||||
|
||||
// Outer ring
|
||||
g.setColour(Colours::BACKGROUNDCOLOUR);
|
||||
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
|
||||
|
||||
// Indicator
|
||||
juce::Path p;
|
||||
auto pointerLength = radius * 0.6f;
|
||||
auto pointerThickness = 2.0f;
|
||||
@ -911,7 +892,6 @@ namespace AXIOM {
|
||||
auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle;
|
||||
const float arcPathWidth = 3.0f;
|
||||
|
||||
// Background
|
||||
g.setColour(Colours::BACKGROUNDCOLOUR);
|
||||
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
|
||||
|
||||
@ -919,7 +899,6 @@ namespace AXIOM {
|
||||
g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth);
|
||||
|
||||
|
||||
// Arc für Gain-Anzeige
|
||||
juce::Path valueArc;
|
||||
valueArc.addCentredArc(centreX, centreY, radius, radius,
|
||||
0.0f, mid, angle, true);
|
||||
@ -927,7 +906,6 @@ namespace AXIOM {
|
||||
g.setColour(accentCol);
|
||||
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
|
||||
|
||||
// Pointer
|
||||
juce::Path p;
|
||||
p.addRectangle(-arcPathWidth / 2, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
|
||||
radius * 0.5f * hoverFactor);
|
||||
@ -961,7 +939,6 @@ namespace AXIOM {
|
||||
}
|
||||
|
||||
|
||||
// Einfacher Ring
|
||||
g.setColour(surfaceCol);
|
||||
g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f);
|
||||
|
||||
@ -971,7 +948,7 @@ namespace AXIOM {
|
||||
|
||||
g.setColour(accentCol);
|
||||
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
|
||||
// Dünner Pointer
|
||||
|
||||
juce::Path p;
|
||||
p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
|
||||
radius * 0.4f * hoverFactor);
|
||||
@ -990,7 +967,6 @@ namespace AXIOM {
|
||||
juce::Slider::SliderLayout layout;
|
||||
auto bounds = slider.getLocalBounds();
|
||||
|
||||
// TextBox in der Mitte (wie bei BaseSliderLookAndFeel)
|
||||
layout.textBoxBounds = juce::Rectangle<int>(
|
||||
bounds.getCentreX() - bounds.getWidth() / 2,
|
||||
bounds.getCentreY() - bounds.getHeight() / 2,
|
||||
@ -998,8 +974,6 @@ namespace AXIOM {
|
||||
bounds.getHeight()
|
||||
);
|
||||
|
||||
// HIER IST DER TRICK: Knob-Bereich kleiner als Slider-Bounds!
|
||||
// So bleibt Platz für die Labels außerhalb
|
||||
layout.sliderBounds = bounds.reduced(labelPadding);
|
||||
|
||||
return layout;
|
||||
@ -1008,21 +982,17 @@ namespace AXIOM {
|
||||
void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height,
|
||||
float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
|
||||
juce::Slider &slider) override {
|
||||
// Basis-Implementation - wird in Subklassen überschrieben
|
||||
auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
|
||||
auto centreX = x + width * 0.5f;
|
||||
auto centreY = y + height * 0.5f;
|
||||
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
|
||||
bool isEnabled = slider.isEnabled();
|
||||
bool isHovered = slider.isMouseOverOrDragging();
|
||||
|
||||
// Outer ring
|
||||
g.setColour(Colours::BACKGROUNDCOLOUR);
|
||||
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
|
||||
|
||||
juce::Colour accentCol = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;
|
||||
|
||||
// Indicator
|
||||
juce::Path p;
|
||||
auto pointerLength = radius * 0.6f;
|
||||
auto pointerThickness = 2.0f;
|
||||
@ -1033,7 +1003,6 @@ namespace AXIOM {
|
||||
g.fillPath(p);
|
||||
|
||||
|
||||
// Dann zeichne die Labels
|
||||
const auto bounds = juce::Rectangle<int>(x, y, width, height);
|
||||
const auto centre = bounds.getCentre().toFloat();
|
||||
const float textRadius = std::min(width, height) * 0.5f + 5.0f;
|
||||
@ -1044,7 +1013,6 @@ namespace AXIOM {
|
||||
auto value = slider.getValue();
|
||||
|
||||
for (int i = 0; i < labels.size(); ++i) {
|
||||
const double labelVal = labels[i].getDoubleValue(); // "12" -> 12.0 usw.
|
||||
const bool isMatch = value == i; // Toleranz: 0.5
|
||||
|
||||
juce::Colour colour;
|
||||
@ -1105,7 +1073,6 @@ namespace AXIOM {
|
||||
}
|
||||
|
||||
|
||||
// Einfacher Ring
|
||||
g.setColour(surfaceCol);
|
||||
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
|
||||
|
||||
@ -1118,7 +1085,7 @@ namespace AXIOM {
|
||||
|
||||
g.setColour(accentCol);
|
||||
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
|
||||
// Dünner Pointer
|
||||
|
||||
juce::Path p;
|
||||
p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
|
||||
radius * 0.4f * hoverFactor);
|
||||
@ -1168,9 +1135,6 @@ namespace AXIOM {
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
///
|
||||
/// @param mode Spacing mode -- e.g. Spacing::SizeMode::XS
|
||||
/// @return Gap size in px as int
|
||||
static int gap(Spacing::SizeMode mode) {
|
||||
return juce::roundToInt(Spacing::scale.space(mode));
|
||||
}
|
||||
@ -1327,10 +1291,8 @@ namespace AXIOM {
|
||||
bool shouldDrawButtonAsDown) override {
|
||||
auto bounds = button.getLocalBounds();
|
||||
|
||||
// Text holen
|
||||
auto text = button.getButtonText();
|
||||
|
||||
// Farbe wählen
|
||||
auto isHovered = button.isMouseOver();
|
||||
bool isEnabled = button.isEnabled();
|
||||
juce::Colour colour = isHovered ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR;
|
||||
@ -1340,11 +1302,9 @@ namespace AXIOM {
|
||||
|
||||
g.setColour(colour);
|
||||
|
||||
// Font einstellen
|
||||
auto font = Typography::getFont(Typography::Style::Button, 1.0f);
|
||||
g.setFont(font);
|
||||
|
||||
// Text zeichnen
|
||||
g.drawFittedText(text,
|
||||
bounds.reduced(4),
|
||||
juce::Justification::centred,
|
||||
@ -1373,7 +1333,7 @@ namespace AXIOM {
|
||||
std::function<void()> onMouseDown;
|
||||
|
||||
void mouseDown(const juce::MouseEvent &event) override {
|
||||
juce::TextEditor::mouseDown(event); // Wichtig: Original-Verhalten beibehalten
|
||||
juce::TextEditor::mouseDown(event);
|
||||
if (onMouseDown)
|
||||
onMouseDown();
|
||||
}
|
||||
@ -1383,7 +1343,6 @@ namespace AXIOM {
|
||||
public:
|
||||
void fillTextEditorBackground(juce::Graphics &g, int width, int height,
|
||||
juce::TextEditor &textEditor) override {
|
||||
// Hintergrundfarbe
|
||||
if (textEditor.isEnabled()) {
|
||||
g.setColour(Colours::SURFACECOLOUR);
|
||||
} else {
|
||||
@ -1404,7 +1363,6 @@ namespace AXIOM {
|
||||
|
||||
void drawTextEditorOutline(juce::Graphics &g, int width, int height,
|
||||
juce::TextEditor &textEditor) override {
|
||||
// Rahmen
|
||||
auto bounds = juce::Rectangle<float>(0, 0, width, height);
|
||||
auto radius = Shape::getRadius(Shape::RadiusMode::S, bounds);
|
||||
auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin);
|
||||
@ -1414,13 +1372,10 @@ namespace AXIOM {
|
||||
if (!textEditor.isEnabled()) {
|
||||
outlineColour = Colours::FOREGROUNDBYPASS.withAlpha(0.3f);
|
||||
} else if (textEditor.hasKeyboardFocus(true)) {
|
||||
// Fokussiert - Accent-Farbe
|
||||
outlineColour = Colours::ACCENTCOLOUR;
|
||||
} else if (textEditor.isMouseOver()) {
|
||||
// Hover
|
||||
outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f);
|
||||
} else {
|
||||
// Normal
|
||||
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f);
|
||||
}
|
||||
|
||||
@ -1440,153 +1395,136 @@ namespace AXIOM {
|
||||
};
|
||||
|
||||
class PresetComboBoxLookAndFeel : public juce::LookAndFeel_V4,
|
||||
private juce::ComponentListener
|
||||
{
|
||||
public:
|
||||
PresetComboBoxLookAndFeel() = default;
|
||||
private juce::ComponentListener {
|
||||
public:
|
||||
PresetComboBoxLookAndFeel() = default;
|
||||
|
||||
~PresetComboBoxLookAndFeel() override
|
||||
{
|
||||
}
|
||||
~PresetComboBoxLookAndFeel() override {
|
||||
}
|
||||
|
||||
void getIdealPopupMenuItemSize (const juce::String& text, bool /*isSeparator*/,
|
||||
int /*standardMenuItemHeight*/, int& idealWidth,
|
||||
int& idealHeight) override
|
||||
{
|
||||
idealHeight = 50;
|
||||
void getIdealPopupMenuItemSize(const juce::String &text, bool isSeparator,
|
||||
int standardMenuItemHeight, int &idealWidth,
|
||||
int &idealHeight) override {
|
||||
idealHeight = 50;
|
||||
|
||||
auto font = Typography::getFont (Typography::Style::Body, 1.0f);
|
||||
idealWidth = font.getStringWidth (text);
|
||||
}
|
||||
auto font = Typography::getFont(Typography::Style::Body, 1.0f);
|
||||
idealWidth = font.getStringWidth(text);
|
||||
}
|
||||
|
||||
void drawPopupMenuUpDownArrow(juce::Graphics& g, int width, int height,
|
||||
bool isScrollUpArrow) override
|
||||
{
|
||||
g.setColour(Colours::ACCENTCOLOUR);
|
||||
void drawPopupMenuUpDownArrow(juce::Graphics &g, int width, int height,
|
||||
bool isScrollUpArrow) override {
|
||||
g.setColour(Colours::ACCENTCOLOUR);
|
||||
|
||||
auto arrowZone = juce::Rectangle<float>(0.0f, 0.0f, (float)width, (float)height);
|
||||
auto arrowWidth = arrowZone.getWidth() * 0.1f;
|
||||
auto arrowHeight = arrowZone.getHeight() * 0.1f;
|
||||
auto arrowZone = juce::Rectangle<float>(0.0f, 0.0f, (float) width, (float) height);
|
||||
auto arrowWidth = arrowZone.getWidth() * 0.1f;
|
||||
auto arrowHeight = arrowZone.getHeight() * 0.1f;
|
||||
|
||||
juce::Path arrow;
|
||||
arrow.startNewSubPath(arrowZone.getCentreX() - arrowWidth * 0.5f,
|
||||
arrowZone.getCentreY() + (isScrollUpArrow ? arrowHeight * 0.5f : -arrowHeight * 0.5f));
|
||||
arrow.lineTo(arrowZone.getCentreX(),
|
||||
arrowZone.getCentreY() + (isScrollUpArrow ? -arrowHeight * 0.5f : arrowHeight * 0.5f));
|
||||
arrow.lineTo(arrowZone.getCentreX() + arrowWidth * 0.5f,
|
||||
arrowZone.getCentreY() + (isScrollUpArrow ? arrowHeight * 0.5f : -arrowHeight * 0.5f));
|
||||
juce::Path arrow;
|
||||
arrow.startNewSubPath(arrowZone.getCentreX() - arrowWidth * 0.5f,
|
||||
arrowZone.getCentreY() + (isScrollUpArrow
|
||||
? arrowHeight * 0.5f
|
||||
: -arrowHeight * 0.5f));
|
||||
arrow.lineTo(arrowZone.getCentreX(),
|
||||
arrowZone.getCentreY() + (isScrollUpArrow ? -arrowHeight * 0.5f : arrowHeight * 0.5f));
|
||||
arrow.lineTo(arrowZone.getCentreX() + arrowWidth * 0.5f,
|
||||
arrowZone.getCentreY() + (isScrollUpArrow ? arrowHeight * 0.5f : -arrowHeight * 0.5f));
|
||||
|
||||
g.strokePath(arrow, juce::PathStrokeType(1.5f));
|
||||
}
|
||||
g.strokePath(arrow, juce::PathStrokeType(1.5f));
|
||||
}
|
||||
|
||||
void drawPopupMenuBackground(juce::Graphics& g, int width, int height) override
|
||||
{
|
||||
g.fillAll(Colours::SURFACECOLOUR);
|
||||
void drawPopupMenuBackground(juce::Graphics &g, int width, int height) override {
|
||||
g.fillAll(Colours::SURFACECOLOUR);
|
||||
}
|
||||
|
||||
}
|
||||
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 *textColourToUse) override {
|
||||
juce::Colour backGroundColour = isHighlighted ? Colours::SURFACEHOVER : Colours::SURFACECOLOUR;
|
||||
|
||||
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* textColourToUse) override {
|
||||
g.fillAll(backGroundColour);
|
||||
|
||||
juce::Colour backGroundColour = isHighlighted ? Colours::SURFACEHOVER : Colours::SURFACECOLOUR;
|
||||
g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
|
||||
if (isTicked) {
|
||||
juce::Colour textColour = isHighlighted ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR;
|
||||
g.setColour(textColour);
|
||||
} else {
|
||||
juce::Colour textColour = isHighlighted ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR;
|
||||
g.setColour(textColour);
|
||||
}
|
||||
g.drawFittedText(text, area.reduced(10), juce::Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
g.fillAll(backGroundColour);
|
||||
void drawComboBox(juce::Graphics &g, int width, int height, bool isButtonDown,
|
||||
int buttonX, int buttonY, int buttonW, int buttonH,
|
||||
juce::ComboBox &box) override {
|
||||
auto bounds = juce::Rectangle<float>(0, 0, (float) width, (float) height);
|
||||
auto cornerSize = Shape::getRadius(Shape::RadiusMode::S, bounds);
|
||||
|
||||
g.setFont (Typography::getFont (Typography::Style::Mono, 1.0f));
|
||||
if (isTicked) {
|
||||
juce::Colour textColour = isHighlighted ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR;
|
||||
g.setColour (textColour);
|
||||
} else {
|
||||
juce::Colour textColour = isHighlighted ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR;
|
||||
g.setColour (textColour);
|
||||
}
|
||||
g.drawFittedText (text, area.reduced (10), juce::Justification::centredLeft, 1);
|
||||
}
|
||||
bool isHovered = box.isMouseOver();
|
||||
bool isEnabled = box.isEnabled();
|
||||
|
||||
void drawComboBox(juce::Graphics& g, int width, int height, bool isButtonDown,
|
||||
int buttonX, int buttonY, int buttonW, int buttonH,
|
||||
juce::ComboBox& box) override
|
||||
{
|
||||
auto bounds = juce::Rectangle<float>(0, 0, (float)width, (float)height);
|
||||
auto cornerSize = Shape::getRadius(Shape::RadiusMode::S, bounds);
|
||||
juce::Colour bgColour;
|
||||
if (!isEnabled) {
|
||||
bgColour = Colours::SURFACEBYPASS;
|
||||
} else if (isButtonDown) {
|
||||
bgColour = Colours::SURFACEHOVER;
|
||||
} else if (isHovered) {
|
||||
bgColour = Colours::SURFACECOLOUR.brighter(0.1f);
|
||||
} else {
|
||||
bgColour = Colours::SURFACECOLOUR;
|
||||
}
|
||||
|
||||
bool isHovered = box.isMouseOver();
|
||||
bool isEnabled = box.isEnabled();
|
||||
g.setColour(bgColour);
|
||||
g.fillRoundedRectangle(bounds, cornerSize);
|
||||
|
||||
juce::Colour bgColour;
|
||||
if (!isEnabled) {
|
||||
bgColour = Colours::SURFACEBYPASS;
|
||||
} else if (isButtonDown) {
|
||||
bgColour = Colours::SURFACEHOVER;
|
||||
} else if (isHovered) {
|
||||
bgColour = Colours::SURFACECOLOUR.brighter(0.1f);
|
||||
} else {
|
||||
bgColour = Colours::SURFACECOLOUR;
|
||||
}
|
||||
juce::Colour outlineColour;
|
||||
if (!isEnabled) {
|
||||
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.2f);
|
||||
} else if (isButtonDown) {
|
||||
outlineColour = Colours::ACCENTCOLOUR;
|
||||
} else if (isHovered) {
|
||||
outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f);
|
||||
} else {
|
||||
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f);
|
||||
}
|
||||
|
||||
g.setColour(bgColour);
|
||||
g.fillRoundedRectangle(bounds, cornerSize);
|
||||
auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin);
|
||||
g.setColour(outlineColour);
|
||||
g.drawRoundedRectangle(bounds.reduced(strokeWidth * 0.5f), cornerSize, strokeWidth);
|
||||
|
||||
juce::Colour outlineColour;
|
||||
if (!isEnabled) {
|
||||
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.2f);
|
||||
} else if (isButtonDown) {
|
||||
outlineColour = Colours::ACCENTCOLOUR;
|
||||
} else if (isHovered) {
|
||||
outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f);
|
||||
} else {
|
||||
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f);
|
||||
}
|
||||
auto arrowZone = juce::Rectangle<float>((float) buttonX, (float) buttonY,
|
||||
(float) buttonW, (float) buttonH);
|
||||
|
||||
auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin);
|
||||
g.setColour(outlineColour);
|
||||
g.drawRoundedRectangle(bounds.reduced(strokeWidth * 0.5f), cornerSize, strokeWidth);
|
||||
juce::Colour arrowColour = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;
|
||||
if (isHovered && isEnabled) {
|
||||
arrowColour = Colours::ACCENTHOVER;
|
||||
}
|
||||
|
||||
auto arrowZone = juce::Rectangle<float>((float)buttonX, (float)buttonY,
|
||||
(float)buttonW, (float)buttonH);
|
||||
g.setColour(arrowColour);
|
||||
|
||||
juce::Colour arrowColour = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;
|
||||
if (isHovered && isEnabled) {
|
||||
arrowColour = Colours::ACCENTHOVER;
|
||||
}
|
||||
juce::Path arrow;
|
||||
auto arrowW = arrowZone.getWidth() * 0.3f;
|
||||
auto arrowH = arrowZone.getHeight() * 0.15f;
|
||||
auto centreX = arrowZone.getCentreX();
|
||||
auto centreY = arrowZone.getCentreY();
|
||||
|
||||
g.setColour(arrowColour);
|
||||
arrow.startNewSubPath(centreX - arrowW * 0.5f, centreY - arrowH * 0.5f);
|
||||
arrow.lineTo(centreX, centreY + arrowH * 0.5f);
|
||||
arrow.lineTo(centreX + arrowW * 0.5f, centreY - arrowH * 0.5f);
|
||||
|
||||
juce::Path arrow;
|
||||
auto arrowW = arrowZone.getWidth() * 0.3f;
|
||||
auto arrowH = arrowZone.getHeight() * 0.15f;
|
||||
auto centreX = arrowZone.getCentreX();
|
||||
auto centreY = arrowZone.getCentreY();
|
||||
g.strokePath(arrow, juce::PathStrokeType(1.5f));
|
||||
}
|
||||
|
||||
arrow.startNewSubPath(centreX - arrowW * 0.5f, centreY - arrowH * 0.5f);
|
||||
arrow.lineTo(centreX, centreY + arrowH * 0.5f);
|
||||
arrow.lineTo(centreX + arrowW * 0.5f, centreY - arrowH * 0.5f);
|
||||
|
||||
g.strokePath(arrow, juce::PathStrokeType(1.5f));
|
||||
}
|
||||
|
||||
void positionComboBoxText(juce::ComboBox& box, juce::Label& label) override
|
||||
{
|
||||
label.setBounds(8, 0, box.getWidth() - 30, box.getHeight());
|
||||
label.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
|
||||
label.setJustificationType(juce::Justification::centredLeft);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
void positionComboBoxText(juce::ComboBox &box, juce::Label &label) override {
|
||||
label.setBounds(8, 0, box.getWidth() - 30, box.getHeight());
|
||||
label.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
|
||||
label.setJustificationType(juce::Justification::centredLeft);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(CrystalizerEQ VERSION 0.1.0)
|
||||
project(CrystalizerEQ VERSION 1.0.0)
|
||||
|
||||
# JUCE einbinden (lokaler Pfad zu JUCE, z. B. als Submodul oder manuell kopiert)
|
||||
add_subdirectory(juce) # <-- Pfad zu deinem JUCE-Ordner relativ zum Projekt
|
||||
|
||||
@ -1,11 +1,3 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "PluginProcessor.h"
|
||||
#include "PluginEditor.h"
|
||||
#include "AXIOMDesignSystem.h"
|
||||
@ -22,7 +14,6 @@ using Components = DesignSystem::Components;
|
||||
using SliderStyles = Components::SliderStyles;
|
||||
|
||||
|
||||
//region setupSliders
|
||||
void CrystalizerEQAudioProcessorEditor::setupSliders() {
|
||||
auto style = juce::Slider::RotaryHorizontalVerticalDrag;
|
||||
for (auto *s: sliders) {
|
||||
@ -57,11 +48,8 @@ void CrystalizerEQAudioProcessorEditor::setupSliders() {
|
||||
highBandSlopeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
|
||||
}
|
||||
|
||||
//endregion setupSliders
|
||||
|
||||
//region setupAttachments
|
||||
void CrystalizerEQAudioProcessorEditor::setupAttachments() {
|
||||
|
||||
lowBandFreqAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandFreq", lowBandFreqSlider);
|
||||
lowBandSlopeAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandSlope", lowBandSlopeSlider);
|
||||
lowBandGainAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandGain", lowBandGainSlider);
|
||||
@ -86,15 +74,18 @@ void CrystalizerEQAudioProcessorEditor::setupAttachments() {
|
||||
|
||||
inputAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "InputGain", inputSlider);
|
||||
outputAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "OutputGain", outputSlider);
|
||||
|
||||
peak1BypassAttach = std::make_unique<ButtonAttach>(audioProcessor.apvts, "Peak1Bypass", peak1BypassButton);
|
||||
peak2BypassAttach = std::make_unique<ButtonAttach>(audioProcessor.apvts, "Peak2Bypass", peak2BypassButton);
|
||||
peak3BypassAttach = std::make_unique<ButtonAttach>(audioProcessor.apvts, "Peak3Bypass", peak3BypassButton);
|
||||
crystalizeAttach = std::make_unique<ButtonAttach>(audioProcessor.apvts, "CrystalizeButton", crystalizeButton);
|
||||
masterBypassAttach = std::make_unique<ButtonAttach>(audioProcessor.apvts, "MasterBypass", masterBypassButton);
|
||||
}
|
||||
|
||||
//endregion setupAttachments
|
||||
|
||||
//region setupDisplayNames
|
||||
void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
|
||||
titleLabel.setText("Crystalizer", juce::dontSendNotification);
|
||||
|
||||
|
||||
lowBypass.setName("LowBypass");
|
||||
lowBell.setName("LowBell");
|
||||
lowCut.setName("LowCut");
|
||||
@ -130,38 +121,22 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
|
||||
inputSlider.setName("Input");
|
||||
outputSlider.setName("Output");
|
||||
|
||||
|
||||
//lowBandFreqSlider.setTextValueSuffix ("\nHz");
|
||||
//lowBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
|
||||
lowBandGainSlider.setTextValueSuffix("\ndB");
|
||||
//lowBandQSlider.setTextValueSuffix ("\nQ");
|
||||
|
||||
//peak1FreqSlider.setTextValueSuffix("\nHz");
|
||||
peak1GainSlider.setTextValueSuffix("\ndB");
|
||||
//peak1QSlider.setTextValueSuffix("\nQ");
|
||||
peak1BypassButton.setName("peak1Bypass");
|
||||
|
||||
//peak2FreqSlider.setTextValueSuffix("\nHz");
|
||||
peak2GainSlider.setTextValueSuffix("\ndB");
|
||||
//peak2QSlider.setTextValueSuffix("\nQ");
|
||||
peak2BypassButton.setName("peak2Bypass");
|
||||
|
||||
//peak3FreqSlider.setTextValueSuffix("\nHz");
|
||||
peak3GainSlider.setTextValueSuffix("\ndB");
|
||||
//peak3QSlider.setTextValueSuffix("\nQ");
|
||||
peak3BypassButton.setName("peak3Bypass");
|
||||
|
||||
|
||||
//highBandFreqSlider.setTextValueSuffix ("\nHz");
|
||||
//highBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
|
||||
highBandGainSlider.setTextValueSuffix("\ndB");
|
||||
// highBandQSlider.setTextValueSuffix ("\nQ");
|
||||
|
||||
inputSlider.setTextValueSuffix("\nIN");
|
||||
outputSlider.setTextValueSuffix("\nOUT");
|
||||
|
||||
|
||||
|
||||
crystalizeButton.setName("CrystalizeButton");
|
||||
|
||||
resetButton.setName("ResetButton");
|
||||
@ -176,9 +151,7 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
|
||||
deletePresetButton.setButtonText("Delete Preset");
|
||||
}
|
||||
|
||||
//endregion setupDisplayNames
|
||||
|
||||
//region setupToggleButtons
|
||||
void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
|
||||
bypassButtonLookAndFeel = std::make_unique<Components::BypassButtonLookAndFeel>();
|
||||
svgToggleButtonLookAndFeel = std::make_unique<Components::SvgToggleButtonLookAndFeel>();
|
||||
@ -212,40 +185,23 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
|
||||
&peak1BypassButton, &peak2BypassButton, &peak3BypassButton, &crystalizeButton, &masterBypassButton
|
||||
}) {
|
||||
b->onClick = [this, b]() {
|
||||
juce::String paramID; // nimm juce::String statt std::string
|
||||
juce::String mode;
|
||||
if (b == &peak1BypassButton) {
|
||||
paramID = "Peak1Bypass";
|
||||
mode = "Low-Mid";
|
||||
} else if (b == &peak2BypassButton) {
|
||||
paramID = "Peak2Bypass";
|
||||
mode = "Mid";
|
||||
} else if (b == &peak3BypassButton) {
|
||||
paramID = "Peak3Bypass";
|
||||
mode = "High-Mid";
|
||||
} else if (b == &crystalizeButton) {
|
||||
paramID = "CrystalizeButton";
|
||||
} else if (b == &masterBypassButton) {
|
||||
paramID = "MasterBypass";
|
||||
mode = "Master";
|
||||
}
|
||||
|
||||
if (auto *param = audioProcessor.apvts.getParameter(paramID)) {
|
||||
const bool isToggled = b->getToggleState();
|
||||
const float target = b->getToggleState() ? 1.0f : 0.0f;
|
||||
|
||||
const float target = isToggled ? 1.0f : 0.0f;
|
||||
|
||||
// Nur senden, wenn sich wirklich etwas ändert (Schwelle 0.5f)
|
||||
if ((param->getValue() >= 0.5f) != isToggled) {
|
||||
param->beginChangeGesture();
|
||||
param->setValueNotifyingHost(target);
|
||||
param->endChangeGesture();
|
||||
}
|
||||
if (mode == "Low-Mid") disableLowMidBand(target);
|
||||
else if (mode == "Mid") disableMidBand(target);
|
||||
else if (mode == "High-Mid") disableHighMidBand(target);
|
||||
else if (mode == "Master") disableEverything(target);
|
||||
}
|
||||
if (mode == "Low-Mid") disableLowMidBand(target);
|
||||
else if (mode == "Mid") disableMidBand(target);
|
||||
else if (mode == "High-Mid") disableHighMidBand(target);
|
||||
else if (mode == "Master") disableEverything(target);
|
||||
|
||||
if (b == &crystalizeButton) {
|
||||
isAnimatingCrystalize = true;
|
||||
@ -255,9 +211,7 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion setupToggleButtons
|
||||
|
||||
//region disableLowBand
|
||||
void CrystalizerEQAudioProcessorEditor::disableLowBand(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
lowBandFreqSlider.setEnabled(isToggled);
|
||||
@ -269,9 +223,8 @@ void CrystalizerEQAudioProcessorEditor::disableLowBand(const float target) {
|
||||
lowBandQSlider.setEnabled(isToggled);
|
||||
lowBandQLabel.setEnabled(isToggled);
|
||||
};
|
||||
//endregion disableLowBand
|
||||
|
||||
//region disableLowMidBand
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::disableLowMidBand(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
peak1GainSlider.setEnabled(isToggled);
|
||||
@ -282,9 +235,7 @@ void CrystalizerEQAudioProcessorEditor::disableLowMidBand(const float target) {
|
||||
peak1FreqLabel.setEnabled(isToggled);
|
||||
}
|
||||
|
||||
//endregion disableLowMidBand
|
||||
|
||||
//region disableMidBand
|
||||
void CrystalizerEQAudioProcessorEditor::disableMidBand(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
peak2GainSlider.setEnabled(isToggled);
|
||||
@ -295,9 +246,7 @@ void CrystalizerEQAudioProcessorEditor::disableMidBand(const float target) {
|
||||
peak2FreqLabel.setEnabled(isToggled);
|
||||
}
|
||||
|
||||
//endregion disableMidBand
|
||||
|
||||
//region disableHighMidBand
|
||||
void CrystalizerEQAudioProcessorEditor::disableHighMidBand(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
peak3GainSlider.setEnabled(isToggled);
|
||||
@ -308,9 +257,7 @@ void CrystalizerEQAudioProcessorEditor::disableHighMidBand(const float target) {
|
||||
peak3FreqLabel.setEnabled(isToggled);
|
||||
}
|
||||
|
||||
//endregion disableHighMidBand
|
||||
|
||||
//region disableHighBand
|
||||
void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
highBandFreqSlider.setEnabled(isToggled);
|
||||
@ -322,9 +269,8 @@ void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) {
|
||||
highBandQSlider.setEnabled(isToggled);
|
||||
highBandQLabel.setEnabled(isToggled);
|
||||
};
|
||||
//endregion disableHighBand
|
||||
|
||||
//region disableEverything
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) {
|
||||
bool isToggled = target <= 0.5f;
|
||||
|
||||
@ -372,9 +318,7 @@ void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion disableEverything
|
||||
|
||||
//region addComponentsToLayout
|
||||
void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
|
||||
addAndMakeVisible(headerBar);
|
||||
headerBar.addAndMakeVisible(titleLabel);
|
||||
@ -489,19 +433,14 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
|
||||
addAndMakeVisible(footerBar);
|
||||
}
|
||||
|
||||
//endregion addComponentsToLayout
|
||||
|
||||
//region setupLabels
|
||||
void CrystalizerEQAudioProcessorEditor::setupLabels() {
|
||||
auto setupLabel = [](juce::Label &label, const juce::String &text) {
|
||||
label.setText(text, juce::dontSendNotification);
|
||||
label.setJustificationType(juce::Justification::centred);
|
||||
label.setColour(juce::Label::textColourId, juce::Colours::black);
|
||||
label.setInterceptsMouseClicks(false, false); // Klicks gehen an den Slider
|
||||
};
|
||||
|
||||
//SetupLabels
|
||||
{
|
||||
}; {
|
||||
//LOW-BAND
|
||||
setupLabel(lowBandFreqLabel, "Low\nHz");
|
||||
setupLabel(lowBandSlopeLabel, "Slope");
|
||||
@ -543,13 +482,9 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() {
|
||||
|
||||
|
||||
setupLabel(presetBoxLabel, "Presets");
|
||||
|
||||
/*setupLabel(inputLabel, "IN");
|
||||
setupLabel(outputLabel, "OUT");*/
|
||||
}
|
||||
}
|
||||
|
||||
//endregion setupLabels
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() {
|
||||
Typography::applyToLabel(titleLabel, Typography::Style::Display, 1.f);
|
||||
@ -564,7 +499,7 @@ void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() {
|
||||
}
|
||||
}
|
||||
|
||||
//region setupSliderTextBoxes
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::setupSliderTextBoxes() {
|
||||
baseLookAndFeel = std::make_unique<AXIOM::DesignSystem::Components::BaseSliderLookAndFeel>();
|
||||
for (auto *s: sliders) {
|
||||
@ -572,9 +507,7 @@ void CrystalizerEQAudioProcessorEditor::setupSliderTextBoxes() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion setupSliderTextBoxes
|
||||
|
||||
//region handleLowBandModes
|
||||
void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
|
||||
for (int i = 0; i < lowBandModeButtons.size(); ++i) {
|
||||
lowBandModeButtons[i]->onClick = [this, i] {
|
||||
@ -589,7 +522,6 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
|
||||
lowBandModeButtons[j]->setToggleState(false, juce::dontSendNotification);
|
||||
}
|
||||
}
|
||||
// Aktuellen aktivieren
|
||||
lowBandBools[i] = true;
|
||||
param->setValueNotifyingHost(param->convertTo0to1((float) i));
|
||||
|
||||
@ -604,9 +536,7 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion handleLowBandModes
|
||||
|
||||
//region handleHighBandModes
|
||||
void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
|
||||
for (int i = 0; i < highBandModeButtons.size(); ++i) {
|
||||
highBandModeButtons[i]->onClick = [this, i] {
|
||||
@ -627,7 +557,6 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
|
||||
param->endChangeGesture();
|
||||
updateFrequencyRanges();
|
||||
}
|
||||
|
||||
} else if (highBandBools[i]) {
|
||||
highBandModeButtons[i]->setToggleState(true, juce::dontSendNotification);
|
||||
}
|
||||
@ -635,9 +564,7 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion handleHighBandModes
|
||||
|
||||
//region setupEventListeners
|
||||
void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
|
||||
presetMenuButton.onClick = [this]() {
|
||||
auto *menu = new DesignSystem::PresetMenu(&presetNameInput, &savePresetButton, &deletePresetButton);
|
||||
@ -657,10 +584,6 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
|
||||
nullptr
|
||||
);
|
||||
box.setLookAndFeel(presetMenuLookAndFeel.get());
|
||||
|
||||
/*juce::Timer::callAfterDelay(100, [this]() {
|
||||
presetNameInput.giveAwayKeyboardFocus();
|
||||
});*/
|
||||
};
|
||||
|
||||
savePresetButton.onClick = [this]() {
|
||||
@ -707,7 +630,7 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
|
||||
}
|
||||
const auto selectedPresetWithExt = selectedPreset + ".xml";
|
||||
audioProcessor.loadPreset(selectedPresetWithExt);
|
||||
updateModeButtons(); // ← NEU!
|
||||
updateModeButtons(); // ← NEU!
|
||||
updateFrequencyRanges();
|
||||
};
|
||||
|
||||
@ -737,9 +660,8 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
|
||||
};
|
||||
}
|
||||
|
||||
//endregion setupEventListeners
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
|
||||
// Low Band Modes aktualisieren
|
||||
int lowMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("LowBandModes")->load());
|
||||
|
||||
for (int i = 0; i < lowBandModeButtons.size(); ++i) {
|
||||
@ -747,7 +669,6 @@ void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
|
||||
lowBandModeButtons[i]->setToggleState(lowBandBools[i], juce::dontSendNotification);
|
||||
}
|
||||
|
||||
// High Band Modes aktualisieren
|
||||
int highMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("HighBandModes")->load());
|
||||
|
||||
for (int i = 0; i < highBandModeButtons.size(); ++i) {
|
||||
@ -755,7 +676,17 @@ void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
|
||||
highBandModeButtons[i]->setToggleState(highBandBools[i], juce::dontSendNotification);
|
||||
}
|
||||
}
|
||||
//region initPresetSystem
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::parameterChanged(const juce::String& parameterID, float newValue) {
|
||||
if (parameterID == "LowBandModes" || parameterID == "HighBandModes") {
|
||||
juce::MessageManager::callAsync([this]() {
|
||||
updateModeButtons();
|
||||
repaint();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::initPresetSystem() {
|
||||
auto presets = audioProcessor.getPresetNamesArray();
|
||||
presetBox.addItemList(presets, 1);
|
||||
@ -780,8 +711,6 @@ void CrystalizerEQAudioProcessorEditor::initPresetSystem() {
|
||||
presetBox.setLookAndFeel(presetComboBoxLookAndFeel.get());
|
||||
}
|
||||
|
||||
//endregion initPresetSystem
|
||||
|
||||
//==============================================================================
|
||||
CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &p)
|
||||
: AudioProcessorEditor(&p), audioProcessor(p), spectrumAnalyzer(p.audioFIFO, p) {
|
||||
@ -803,8 +732,10 @@ CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor(Crystalizer
|
||||
auto logoIcon = juce::XmlDocument::parse(BinaryData::logo_icon_svg);
|
||||
if (logoIcon != nullptr)
|
||||
logoDrawable = juce::Drawable::createFromSVG(*logoIcon);
|
||||
audioProcessor.apvts.addParameterListener("LowBandModes", this);
|
||||
audioProcessor.apvts.addParameterListener("HighBandModes", this);
|
||||
|
||||
|
||||
updateModeButtons();
|
||||
}
|
||||
|
||||
CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() {
|
||||
@ -821,7 +752,8 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() {
|
||||
presetNameInput.setLookAndFeel(nullptr);
|
||||
presetBox.setLookAndFeel(nullptr);
|
||||
|
||||
|
||||
audioProcessor.apvts.removeParameterListener("LowBandModes", this);
|
||||
audioProcessor.apvts.removeParameterListener("HighBandModes", this);
|
||||
for (auto *b: lowBandModeButtons) {
|
||||
b->setLookAndFeel(nullptr);
|
||||
}
|
||||
@ -847,7 +779,6 @@ float CrystalizerEQAudioProcessorEditor::getInterpolatedDb(float exactBin) {
|
||||
return y1 + fraction * (y2 - y1);
|
||||
}
|
||||
|
||||
//region paintAnalyzer
|
||||
void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
|
||||
analyzerRect = getLocalArea(&analyzerArea, analyzerArea.getLocalBounds());
|
||||
juce::Graphics::ScopedSaveState guard(g);
|
||||
@ -855,15 +786,15 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
|
||||
|
||||
auto r = analyzerRect.toFloat();
|
||||
|
||||
// Hintergrund
|
||||
// BACKGROUND
|
||||
g.setColour(juce::Colours::black);
|
||||
g.fillRect(r);
|
||||
|
||||
// Rahmen
|
||||
// BORDER
|
||||
g.setColour(juce::Colours::grey);
|
||||
g.drawRect(analyzerRect);
|
||||
|
||||
// === Spektrum zeichnen ===
|
||||
// SPECTRUM
|
||||
g.setColour(Colours::ACCENTCOLOUR);
|
||||
|
||||
const float analyzerWidth = analyzerRect.getWidth();
|
||||
@ -874,49 +805,36 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
|
||||
const float maxDb = spectrumAnalyzer.MAXDB;
|
||||
const float dbRange = maxDb - minDb; // = 96 dB
|
||||
|
||||
const float zeroDbY = analyzerBottom - (analyzerHeight * (0.0f - minDb) / dbRange); // Position von 0dB
|
||||
|
||||
|
||||
const float minFreq = spectrumAnalyzer.MINFREQ; // 20 Hz
|
||||
const float maxFreq = spectrumAnalyzer.MAXFREQ; // 20 kHz
|
||||
|
||||
float sampleRate = spectrumAnalyzer.sampleRate;
|
||||
float deltaF = spectrumAnalyzer.deltaF;
|
||||
|
||||
// Zeichne für jede X-Position
|
||||
juce::Path spectrumPath;
|
||||
bool pathStarted = false;
|
||||
|
||||
// Erhöhe die Auflösung - zeichne mehr Punkte als Pixel
|
||||
const int resolutionMultiplier = 4; // 4x mehr Punkte
|
||||
const int resolutionMultiplier = 4;
|
||||
const int numPoints = static_cast<int>(analyzerWidth) * resolutionMultiplier;
|
||||
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
// Berechne X-Position
|
||||
float x = (i / static_cast<float>(numPoints)) * analyzerWidth;
|
||||
|
||||
// Berechne Frequenz (logarithmisch)
|
||||
float normalizedX = i / static_cast<float>(numPoints);
|
||||
float logMin = std::log10(minFreq);
|
||||
float logMax = std::log10(maxFreq);
|
||||
float logFreq = logMin + normalizedX * (logMax - logMin);
|
||||
float freq = std::pow(10.0f, logFreq);
|
||||
|
||||
// Finde Bin und interpoliere
|
||||
float exactBin = freq / deltaF;
|
||||
|
||||
float dB = getInterpolatedDb(exactBin);
|
||||
|
||||
// Normalisiere
|
||||
float normalized = (dB - minDb) / dbRange;
|
||||
normalized = juce::jlimit(0.0f, 1.0f, normalized);
|
||||
|
||||
|
||||
// Berechne Y-Position
|
||||
float pixelHeight = normalized * analyzerHeight;
|
||||
float y = analyzerBottom - pixelHeight;
|
||||
|
||||
// Füge zum Path hinzu
|
||||
if (!pathStarted) {
|
||||
spectrumPath.startNewSubPath(x + analyzerRect.getX(), y);
|
||||
pathStarted = true;
|
||||
@ -925,7 +843,6 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
|
||||
}
|
||||
}
|
||||
|
||||
// Zeichne die Linie mit Anti-Aliasing
|
||||
if (pathStarted) {
|
||||
juce::PathStrokeType stroke(2.0f);
|
||||
stroke.setJointStyle(juce::PathStrokeType::curved); // Runde Ecken
|
||||
@ -939,13 +856,11 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
|
||||
g.fillPath(spectrumPath);
|
||||
}
|
||||
|
||||
|
||||
// Optional: Zeichne Frequenz-Grid-Linien
|
||||
drawFrequencyGrid(g, dbRange);
|
||||
}
|
||||
|
||||
|
||||
float CrystalizerEQAudioProcessorEditor::mapFrequencyToX(float freq, float minFreq, float maxFreq, float width) {
|
||||
// Logarithmische Skalierung: log(freq) zwischen log(minFreq) und log(maxFreq)
|
||||
float logMin = std::log10(minFreq);
|
||||
float logMax = std::log10(maxFreq);
|
||||
float logFreq = std::log10(freq);
|
||||
@ -954,6 +869,7 @@ float CrystalizerEQAudioProcessorEditor::mapFrequencyToX(float freq, float minFr
|
||||
return normalized * width;
|
||||
}
|
||||
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, const float dbRange) {
|
||||
g.setColour(Colours::FOREGROUNDCOLOUR.withAlpha(0.3f));
|
||||
|
||||
@ -973,11 +889,9 @@ void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, con
|
||||
for (int i = 0; i < gridDB.size(); ++i) {
|
||||
float db = gridDB[i];
|
||||
|
||||
// 1: dB normalisieren (wie bei der Kurve)
|
||||
float normalized = (db - minDB) / dbRange;
|
||||
normalized = juce::jlimit(0.0f, 1.0f, normalized);
|
||||
|
||||
// 2: Y-Position wie beim Spektrum berechnen
|
||||
float pixelHeight = normalized * analyzerHeight;
|
||||
float y = analyzerBottom - pixelHeight;
|
||||
|
||||
@ -997,7 +911,6 @@ void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, con
|
||||
static_cast<float>(analyzerRect.getY()),
|
||||
static_cast<float>(analyzerRect.getBottom()));
|
||||
|
||||
// Optional: Frequenz-Label zeichnen
|
||||
if (freq == 20) continue;
|
||||
g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
|
||||
juce::String label = (freq >= 1000) ? juce::String(freq / 1000) + "k" : juce::String(freq);
|
||||
@ -1006,7 +919,6 @@ void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, con
|
||||
}
|
||||
}
|
||||
|
||||
//endregion paintAnalyzer
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) {
|
||||
for (auto *b: lowBandModeButtons) {
|
||||
@ -1028,7 +940,7 @@ void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) {
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
|
||||
g.fillAll(Colours::BACKGROUNDCOLOUR);
|
||||
g.setColour(AXIOM::DesignSystem::Colours::FOREGROUNDCOLOUR);
|
||||
@ -1051,7 +963,6 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
|
||||
g.fillRect(hBX, hBY, hBW, mPY - hBPad);
|
||||
|
||||
|
||||
|
||||
g.setColour(Colours::SURFACECOLOUR);
|
||||
const auto pA = getLocalArea(&presetArea, presetArea.getLocalBounds());
|
||||
auto pAX = pA.getX();
|
||||
@ -1077,7 +988,6 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
|
||||
g.fillRect(fABorder);
|
||||
|
||||
fABorder = fABorder.withX(fABorder.getX() - 2 * fABorderWidth).withY(fABorder.getY() - 2 * fABorderWidth);
|
||||
//g.setColour(Colours::FOREGROUNDBYPASS);
|
||||
g.fillRect(fABorder);
|
||||
|
||||
g.setColour(Colours::BACKGROUNDBYPASS);
|
||||
@ -1095,10 +1005,9 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
|
||||
|
||||
logoDrawable->drawWithin(g, logoArea, juce::RectanglePlacement::xLeft, 1.0f);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
//region paintBorderLines
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) {
|
||||
g.setColour(DesignSystem::Colours::BACKGROUNDCOLOUR);
|
||||
auto prevRight = (float) lowFilterArea.getRight();
|
||||
@ -1140,12 +1049,10 @@ void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion paintBorderLines
|
||||
|
||||
//region setKnobVisibility
|
||||
void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
|
||||
int lowMode = (int) audioProcessor.apvts
|
||||
.getRawParameterValue("LowBandModes")->load(); // 0..3
|
||||
.getRawParameterValue("LowBandModes")->load();
|
||||
const bool masterIsToggled = masterBypassButton.getToggleState();
|
||||
|
||||
const float target = masterIsToggled ? 1.0f : 0.0f;
|
||||
@ -1166,7 +1073,7 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
|
||||
lowBandModeLabel.setEnabled(lowMode >= 1);
|
||||
|
||||
int highMode = (int) audioProcessor.apvts
|
||||
.getRawParameterValue("HighBandModes")->load(); // 0..3
|
||||
.getRawParameterValue("HighBandModes")->load();
|
||||
|
||||
if (masterIsToggled) {
|
||||
highMode = 0;
|
||||
@ -1184,56 +1091,38 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
|
||||
highBandModeLabel.setEnabled(highMode >= 1);
|
||||
}
|
||||
|
||||
//endregion setKnobVisibility
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::updateFrequencyRanges()
|
||||
{
|
||||
// Low Band Range anpassen
|
||||
int lowMode = (int)audioProcessor.apvts.getRawParameterValue("LowBandModes")->load();
|
||||
void CrystalizerEQAudioProcessorEditor::updateFrequencyRanges() {
|
||||
int lowMode = (int) audioProcessor.apvts.getRawParameterValue("LowBandModes")->load();
|
||||
|
||||
if (lowMode == 3) // Bell Mode
|
||||
{
|
||||
// Begrenzter Bereich für Low Bell
|
||||
if (lowMode == 3) {
|
||||
lowBandFreqSlider.setRange(20.0, 500.0, 1.0);
|
||||
|
||||
// Aktuellen Wert prüfen und ggf. anpassen
|
||||
float currentFreq = audioProcessor.apvts.getRawParameterValue("LowBandFreq")->load();
|
||||
if (currentFreq > 500.0f)
|
||||
{
|
||||
if (currentFreq > 500.0f) {
|
||||
lowBandFreqSlider.setValue(500.0, juce::sendNotificationSync);
|
||||
}
|
||||
}
|
||||
else if (lowMode >= 1) // Cut oder Shelf Mode
|
||||
{
|
||||
// Volle Bandbreite
|
||||
} else if (lowMode >= 1) {
|
||||
lowBandFreqSlider.setRange(20.0, 20000.0, 1.0);
|
||||
}
|
||||
|
||||
lowBandFreqSlider.repaint();
|
||||
// High Band Range anpassen
|
||||
int highMode = (int)audioProcessor.apvts.getRawParameterValue("HighBandModes")->load();
|
||||
int highMode = (int) audioProcessor.apvts.getRawParameterValue("HighBandModes")->load();
|
||||
|
||||
if (highMode == 3) // Bell Mode
|
||||
{
|
||||
// Begrenzter Bereich für High Bell
|
||||
if (highMode == 3) {
|
||||
highBandFreqSlider.setRange(8000.0, 20000.0, 1.0);
|
||||
|
||||
// Aktuellen Wert prüfen und ggf. anpassen
|
||||
float currentFreq = audioProcessor.apvts.getRawParameterValue("HighBandFreq")->load();
|
||||
if (currentFreq < 8000.0f)
|
||||
{
|
||||
if (currentFreq < 8000.0f) {
|
||||
highBandFreqSlider.setValue(8000.0, juce::sendNotificationSync);
|
||||
}
|
||||
}
|
||||
else if (highMode >= 1) // Cut oder Shelf Mode
|
||||
{
|
||||
// Volle Bandbreite
|
||||
} else if (highMode >= 1) {
|
||||
highBandFreqSlider.setRange(20.0, 20000.0, 1.0);
|
||||
}
|
||||
highBandFreqSlider.repaint();
|
||||
}
|
||||
|
||||
//region animateCrystalizeButton
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() {
|
||||
if (isAnimatingCrystalize) {
|
||||
const float step = 0.1f;
|
||||
@ -1260,9 +1149,7 @@ void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion animateCrystalizeButton
|
||||
|
||||
//region timerCallback
|
||||
void CrystalizerEQAudioProcessorEditor::timerCallback() {
|
||||
setKnobVisibility();
|
||||
spectrumAnalyzer.processSamples();
|
||||
@ -1279,7 +1166,6 @@ void CrystalizerEQAudioProcessorEditor::timerCallback() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion timerCallback
|
||||
|
||||
void CrystalizerEQAudioProcessorEditor::resized() {
|
||||
auto pluginArea = getLocalBounds();
|
||||
@ -1288,10 +1174,8 @@ void CrystalizerEQAudioProcessorEditor::resized() {
|
||||
setupHeader();
|
||||
setupBody();
|
||||
setupFooter();
|
||||
|
||||
}
|
||||
|
||||
//region resetAllCheckboxes
|
||||
void CrystalizerEQAudioProcessorEditor::resetAllCheckboxes() {
|
||||
const auto notify = juce::dontSendNotification;
|
||||
masterBypassButton.setToggleState(false, notify);
|
||||
@ -1311,9 +1195,7 @@ void CrystalizerEQAudioProcessorEditor::resetAllCheckboxes() {
|
||||
highShelf.setToggleState(true, notify);
|
||||
}
|
||||
|
||||
//endregion resetAllCheckboxes
|
||||
|
||||
//region scalePluginWindow
|
||||
void CrystalizerEQAudioProcessorEditor::scalePluginWindow(juce::Rectangle<int> area) {
|
||||
if (area.getWidth() < 520) Spacing::setUiScale(Spacing::uiScaleMode::XS);
|
||||
else if (area.getWidth() < 720) Spacing::setUiScale(Spacing::uiScaleMode::S);
|
||||
@ -1322,15 +1204,13 @@ void CrystalizerEQAudioProcessorEditor::scalePluginWindow(juce::Rectangle<int> a
|
||||
else Spacing::setUiScale(Spacing::uiScaleMode::XL);
|
||||
}
|
||||
|
||||
//endregion scalePluginWindow
|
||||
|
||||
//region setupMainGrid
|
||||
void CrystalizerEQAudioProcessorEditor::setupMainGrid(juce::Rectangle<int> area) {
|
||||
const float headerHeight = static_cast<float>(getHeight()) * 0.1f;
|
||||
const float footerHeight = static_cast<float>(getHeight()) * 0.1f;
|
||||
|
||||
Layout::GridSpec spec{
|
||||
/* cols */ {Layout::fr(1)}, // eine Spalte, füllt Breite
|
||||
/* cols */ {Layout::fr(1)},
|
||||
/* rows */ {Layout::pxTrack(headerHeight), Layout::fr(1), Layout::pxTrack(footerHeight)},
|
||||
// Header / Body / Footer
|
||||
/* colGap */ Spacing::SizeMode::S,
|
||||
@ -1340,9 +1220,7 @@ void CrystalizerEQAudioProcessorEditor::setupMainGrid(juce::Rectangle<int> area)
|
||||
Layout::grid(area, spec, {&headerBar, &mainPanel, &footerBar});
|
||||
}
|
||||
|
||||
//endregion setupMainGrid
|
||||
|
||||
//region setupHeader
|
||||
void CrystalizerEQAudioProcessorEditor::setupHeader() {
|
||||
const auto bounds = headerBar.getLocalBounds();
|
||||
|
||||
@ -1393,12 +1271,9 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() {
|
||||
/* pad */ Layout::padding(Spacing::SizeMode::S)
|
||||
};
|
||||
Layout::grid(presetAreaBounds, presetSpec, {
|
||||
// Label über beide Spalten (row1, col1..2)
|
||||
Layout::area(presetBoxLabel, 1, 2, 2, 3),
|
||||
// Box unten links (row2, col1)
|
||||
Layout::area(presetBox, 2, 2, 3, 3),
|
||||
Layout::area(resetButton, 2, 1, 2, 2),
|
||||
// Menütaste unten rechts (row2, col2)
|
||||
Layout::area(presetMenuButton, 2, 3, 3, 4)
|
||||
.withWidth(iconSize)
|
||||
.withHeight(iconSize)
|
||||
@ -1406,9 +1281,7 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupHeader
|
||||
|
||||
//region setupBody
|
||||
void CrystalizerEQAudioProcessorEditor::setupBody() {
|
||||
const auto bounds = mainPanel.getLocalBounds();
|
||||
|
||||
@ -1432,22 +1305,17 @@ void CrystalizerEQAudioProcessorEditor::setupBody() {
|
||||
Layout::area(analyzerArea, 1, 2, 2, 5)
|
||||
.withMargin(juce::GridItem::Margin(filterAreaMargin, 0, filterAreaMargin, 0)),
|
||||
Layout::area(crystalizeButton, 1, 5, 2, 6)
|
||||
.withWidth(crystalizeIconSize)
|
||||
.withHeight(crystalizeIconSize * 0.5f)
|
||||
.withAlignSelf(juce::GridItem::AlignSelf::center)
|
||||
.withJustifySelf(juce::GridItem::JustifySelf::center),
|
||||
.withWidth(crystalizeIconSize)
|
||||
.withHeight(crystalizeIconSize * 0.5f)
|
||||
.withAlignSelf(juce::GridItem::AlignSelf::center)
|
||||
.withJustifySelf(juce::GridItem::JustifySelf::center),
|
||||
Layout::area(filterArea, 2, 1, 3, 6)
|
||||
.withMargin(juce::GridItem::Margin(0, filterAreaMargin, 0, filterAreaMargin))
|
||||
|
||||
});
|
||||
|
||||
const auto analyzerAreaBounds = analyzerArea.getLocalBounds();
|
||||
const auto analyzerAreaWidth = static_cast<float>(analyzerArea.getWidth());
|
||||
const auto analyzerAreaHeight = static_cast<float>(analyzerArea.getHeight());
|
||||
|
||||
const auto filterAreaBounds = filterArea.getLocalBounds();
|
||||
const auto filterAreaWidth = static_cast<float>(filterArea.getWidth());
|
||||
const auto filterAreaHeight = static_cast<float>(filterArea.getHeight());
|
||||
const auto filterColWidth = filterAreaWidth * 0.2f;
|
||||
|
||||
|
||||
@ -1478,9 +1346,7 @@ void CrystalizerEQAudioProcessorEditor::setupBody() {
|
||||
setupHighBandLayout();
|
||||
}
|
||||
|
||||
//endregion setupBody
|
||||
|
||||
//region setupLowBandLayout
|
||||
void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
|
||||
const auto bounds = lowFilterArea.getLocalBounds();
|
||||
const auto areaWidth = static_cast<float>(bounds.getWidth());
|
||||
@ -1546,9 +1412,6 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
|
||||
});
|
||||
|
||||
const auto modeBoxBounds = lowBandModeBox.getLocalBounds();
|
||||
const auto modeBoxAreaWidth = static_cast<float>(modeBoxBounds.getWidth());
|
||||
const auto modeBoxAreaHeight = static_cast<float>(modeBoxBounds.getHeight());
|
||||
const auto modeBoxButtonColWidth = modeBoxAreaWidth / 4.0f;
|
||||
const auto slopeH = lowBandSlopeSlider.getY() - lowBandModeBox.getY() + lowBandModeLabel.getFont().getHeight() / 2;
|
||||
|
||||
Layout::GridSpec lowBandModeBoxSpec{
|
||||
@ -1569,9 +1432,7 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupLowBandLayout
|
||||
|
||||
//region setupLowMidBandLayout
|
||||
void CrystalizerEQAudioProcessorEditor::setupLowMidBandLayout() {
|
||||
const auto bounds = lowMidFilterArea.getLocalBounds();
|
||||
const auto areaWidth = static_cast<float>(bounds.getWidth());
|
||||
@ -1671,9 +1532,7 @@ void CrystalizerEQAudioProcessorEditor::setupMidBandLayout() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupMidBandLayout
|
||||
|
||||
//region setupHighMidBandLayout
|
||||
void CrystalizerEQAudioProcessorEditor::setupHighMidBandLayout() {
|
||||
const auto bounds = highMidFilterArea.getLocalBounds();
|
||||
const auto areaWidth = static_cast<float>(bounds.getWidth());
|
||||
@ -1721,9 +1580,7 @@ void CrystalizerEQAudioProcessorEditor::setupHighMidBandLayout() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupHighMidBandLayout
|
||||
|
||||
//region setupHighBandLayout
|
||||
void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
|
||||
const auto bounds = highFilterArea.getLocalBounds();
|
||||
const auto areaWidth = static_cast<float>(bounds.getWidth());
|
||||
@ -1788,9 +1645,6 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
|
||||
});
|
||||
|
||||
const auto modeBoxBounds = lowBandModeBox.getLocalBounds();
|
||||
const auto modeBoxAreaWidth = static_cast<float>(modeBoxBounds.getWidth());
|
||||
const auto modeBoxAreaHeight = static_cast<float>(modeBoxBounds.getHeight());
|
||||
const auto modeBoxButtonColWidth = modeBoxAreaWidth / 4.0f;
|
||||
const auto slopeH = highBandSlopeSlider.getY() - highBandModeBox.getY() + highBandModeLabel.getFont().getHeight() /
|
||||
2;
|
||||
|
||||
@ -1812,15 +1666,10 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupHighBandLayout
|
||||
|
||||
//region setupFooter
|
||||
void CrystalizerEQAudioProcessorEditor::setupFooter() {
|
||||
const auto bounds = footerBar.getLocalBounds();
|
||||
|
||||
const auto footerWidth = static_cast<float>(bounds.getWidth());
|
||||
const auto footerHeight = static_cast<float>(bounds.getHeight());
|
||||
|
||||
const auto refW = getReferenceCell()[0];
|
||||
const auto refH = getReferenceCell()[1];
|
||||
|
||||
@ -1836,9 +1685,6 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
|
||||
});
|
||||
|
||||
const auto globalControlAreaBounds = globalControlArea.getLocalBounds();
|
||||
const auto globalControlAreaWidth = static_cast<float>(globalControlArea.getWidth());
|
||||
const auto globalControlAreaHeight = static_cast<float>(globalControlArea.getHeight());
|
||||
const auto globalControlColWidth = globalControlAreaWidth / 3.0f;
|
||||
const auto sliderRadius = outputSlider.getWidth() / 2.0f;
|
||||
const auto sliderWidth = outputSlider.getWidth();
|
||||
|
||||
@ -1874,9 +1720,7 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
|
||||
});
|
||||
}
|
||||
|
||||
//endregion setupFooter
|
||||
|
||||
//region getReferenceCell
|
||||
juce::Array<float> CrystalizerEQAudioProcessorEditor::getReferenceCell() {
|
||||
const auto bounds = midFilterArea.getLocalBounds();
|
||||
const auto areaWidth = static_cast<float>(bounds.getWidth());
|
||||
@ -1890,18 +1734,14 @@ juce::Array<float> CrystalizerEQAudioProcessorEditor::getReferenceCell() {
|
||||
return refCell;
|
||||
}
|
||||
|
||||
//endregion getReferenceCell
|
||||
|
||||
//region processSamples
|
||||
void SpectrumAnalyzer::processSamples() {
|
||||
auto samples = audioFIFO.sendSamplesToEditor();
|
||||
|
||||
getFftFrame(samples);
|
||||
}
|
||||
|
||||
//endregion processSamples
|
||||
|
||||
//region getFftFrame
|
||||
void SpectrumAnalyzer::getFftFrame(juce::Array<float> &samples) {
|
||||
const int needed = FFTSIZE - fftFrame.size();
|
||||
if (needed <= 0)
|
||||
@ -1925,18 +1765,14 @@ void SpectrumAnalyzer::getFftFrame(juce::Array<float> &samples) {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion getFftFrame
|
||||
|
||||
//region applyWindowOnFftFrame
|
||||
void SpectrumAnalyzer::applyWindowOnFftFrame(std::vector<float> &fullFrame) {
|
||||
if (fullFrame.size() != FFTSIZE) return;
|
||||
window.multiplyWithWindowingTable(fullFrame.data(), FFTSIZE);
|
||||
processWindowedFrame(fullFrame);
|
||||
}
|
||||
|
||||
//endregion applyWindowOnFftFrame
|
||||
|
||||
//region processWindowedFrame
|
||||
void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &windowedFrame) {
|
||||
if (windowedFrame.size() != FFTSIZE || fftData.size() != FFTSIZE * 2) return;
|
||||
fillFftDataFromFrame(windowedFrame);
|
||||
@ -1947,11 +1783,7 @@ void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &windowedFrame) {
|
||||
renderValuesDb = getRenderValues();
|
||||
}
|
||||
|
||||
//endregion processWindowedFrame
|
||||
|
||||
|
||||
|
||||
//region fillFftDataFromFrame
|
||||
void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &windowedFrame) {
|
||||
for (int n = 0; n < FFTSIZE; ++n) {
|
||||
fftData[n] = windowedFrame[n];
|
||||
@ -1961,9 +1793,7 @@ void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &windowedFrame) {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion fillFftDataFromFrame
|
||||
|
||||
//region buildMagnitudeSpectrum
|
||||
void SpectrumAnalyzer::buildMagnitudeSpectrum() {
|
||||
for (int k = 0; k < BINS; ++k) {
|
||||
float mag = fftData[k];
|
||||
@ -1974,15 +1804,27 @@ void SpectrumAnalyzer::buildMagnitudeSpectrum() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion buildMagnitudeSpectrum
|
||||
float getFrequencyWeighting(float freq) {
|
||||
// Pre-Emphasis: +3dB pro Oktave (kompensiert Pink Noise Charakteristik)
|
||||
// Basis: 1kHz = 0dB
|
||||
const float referenceFreq = 1000.0f;
|
||||
float octaves = std::log2(freq / referenceFreq);
|
||||
float weightingDb = octaves * 3.0f; // 3dB pro Oktave
|
||||
|
||||
// Begrenzen auf sinnvolle Werte
|
||||
weightingDb = juce::jlimit(-12.0f, 12.0f, weightingDb);
|
||||
|
||||
return weightingDb;
|
||||
}
|
||||
|
||||
//region convertToDb
|
||||
void SpectrumAnalyzer::convertToDb() {
|
||||
for (int k = 0; k < magnitudes.size(); ++k) {
|
||||
float mag = std::max(magnitudes[k], 1e-9f);
|
||||
float dB = juce::Decibels::gainToDecibels(mag);
|
||||
|
||||
// SOFORT auf MINDB setzen wenn unter -60dB, nicht erst limitieren
|
||||
float freq = k * deltaF;
|
||||
float weighting = getFrequencyWeighting(freq);
|
||||
dB += weighting;
|
||||
if (dB < -60.0f) {
|
||||
dB = MINDB;
|
||||
}
|
||||
@ -1991,18 +1833,14 @@ void SpectrumAnalyzer::convertToDb() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion convertToDb
|
||||
|
||||
//region applySmoothing
|
||||
void SpectrumAnalyzer::applySmoothing() {
|
||||
applyEMA();
|
||||
applyFreqSmoothing();
|
||||
applyPeakHoldAndFalloff();
|
||||
}
|
||||
|
||||
//endregion applySmoothing
|
||||
|
||||
//region applyEMA
|
||||
void SpectrumAnalyzer::applyEMA() {
|
||||
for (int k = 0; k < magnitudesDb.size(); ++k) {
|
||||
float smoothedVal = smoothingFactor * magnitudesDb[k] + (1 - smoothingFactor) * emaSmoothedMagnitudesDb[k];
|
||||
@ -2010,9 +1848,7 @@ void SpectrumAnalyzer::applyEMA() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion applyEMA
|
||||
|
||||
//region applyFreqSmoothing
|
||||
void SpectrumAnalyzer::applyFreqSmoothing() {
|
||||
for (int k = 0; k < BINS; ++k) {
|
||||
double freq = k * deltaF;
|
||||
@ -2043,9 +1879,7 @@ void SpectrumAnalyzer::applyFreqSmoothing() {
|
||||
}
|
||||
}
|
||||
|
||||
//endregion applyFreqSmoothing
|
||||
|
||||
//region applyPeakHoldAndFalloff
|
||||
void SpectrumAnalyzer::applyPeakHoldAndFalloff() {
|
||||
std::vector<float> prevPeak = peakHoldMagnitudesDb;
|
||||
for (int k = 0; k < BINS; ++k) {
|
||||
@ -2053,17 +1887,12 @@ void SpectrumAnalyzer::applyPeakHoldAndFalloff() {
|
||||
float prev = prevPeak[k];
|
||||
|
||||
peakHoldMagnitudesDb[k] = std::max(current, prev - FALLOFFRATE * DELTAT);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//endregion applyPeakHoldAndFalloff
|
||||
|
||||
//region getRenderValues
|
||||
std::vector<float> SpectrumAnalyzer::getRenderValues() {
|
||||
std::vector<float> renderValues = peakHoldMagnitudesDb;
|
||||
|
||||
return renderValues;
|
||||
}
|
||||
|
||||
//endregion getRenderValues
|
||||
|
||||
@ -131,7 +131,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class CrystalizerEQAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer {
|
||||
class CrystalizerEQAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer, private juce::AudioProcessorValueTreeState::Listener {
|
||||
public:
|
||||
CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &);
|
||||
|
||||
@ -184,6 +184,10 @@ public:
|
||||
highBandFreqAttach, highBandSlopeAttach, highBandGainAttach, highBandQAttach,
|
||||
inputAttach, outputAttach;
|
||||
|
||||
std::unique_ptr<ButtonAttach>
|
||||
peak1BypassAttach, peak2BypassAttach, peak3BypassAttach,
|
||||
crystalizeAttach, masterBypassAttach;
|
||||
|
||||
juce::Label titleLabel,
|
||||
lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel,
|
||||
low12, low24, low36, low48, lowdBOctLabel,
|
||||
@ -233,8 +237,6 @@ public:
|
||||
|
||||
DesignSystem::ClickableTextEditor presetNameInput;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
// This reference is provided as a quick way for your editor to
|
||||
// access the processor object that created it.
|
||||
@ -378,5 +380,7 @@ private:
|
||||
|
||||
float getInterpolatedDb(float exactBin);
|
||||
|
||||
void parameterChanged(const juce::String& parameterID, float newValue) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CrystalizerEQAudioProcessorEditor)
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -18,66 +18,81 @@
|
||||
class AudioFIFO {
|
||||
public:
|
||||
AudioFIFO();
|
||||
|
||||
~AudioFIFO();
|
||||
|
||||
juce::Array<float> sampleStack;
|
||||
|
||||
void loadSamplesToFIFO(const juce::AudioBuffer<float> &samples);
|
||||
|
||||
juce::Array<float> sendSamplesToEditor();
|
||||
juce::SpinLock lock;
|
||||
|
||||
juce::SpinLock lock;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
};
|
||||
|
||||
class CrystalizerEQAudioProcessor : public juce::AudioProcessor , public juce::AudioProcessorValueTreeState::Listener
|
||||
{
|
||||
class CrystalizerEQAudioProcessor : public juce::AudioProcessor, public juce::AudioProcessorValueTreeState::Listener {
|
||||
public:
|
||||
//==============================================================================
|
||||
CrystalizerEQAudioProcessor();
|
||||
|
||||
~CrystalizerEQAudioProcessor() override;
|
||||
void parameterChanged (const juce::String& id, float newValue) override;
|
||||
|
||||
void parameterChanged(const juce::String &id, float newValue) override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
||||
|
||||
void releaseResources() override;
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
|
||||
#endif
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported(const BusesLayout &layouts) const override;
|
||||
#endif
|
||||
|
||||
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
||||
void processBlock(juce::AudioBuffer<float> &, juce::MidiBuffer &) override;
|
||||
|
||||
//==============================================================================
|
||||
juce::AudioProcessorEditor* createEditor() override;
|
||||
juce::AudioProcessorEditor *createEditor() override;
|
||||
|
||||
bool hasEditor() const override;
|
||||
|
||||
//==============================================================================
|
||||
const juce::String getName() const override;
|
||||
|
||||
bool acceptsMidi() const override;
|
||||
|
||||
bool producesMidi() const override;
|
||||
|
||||
bool isMidiEffect() const override;
|
||||
|
||||
double getTailLengthSeconds() const override;
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override;
|
||||
|
||||
int getCurrentProgram() override;
|
||||
void setCurrentProgram (int index) override;
|
||||
const juce::String getProgramName (int index) override;
|
||||
void changeProgramName (int index, const juce::String& newName) override;
|
||||
|
||||
void setCurrentProgram(int index) override;
|
||||
|
||||
const juce::String getProgramName(int index) override;
|
||||
|
||||
void changeProgramName(int index, const juce::String &newName) override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (juce::MemoryBlock& destData) override;
|
||||
void setStateInformation (const void* data, int sizeInBytes) override;
|
||||
void getStateInformation(juce::MemoryBlock &destData) override;
|
||||
|
||||
void setStateInformation(const void *data, int sizeInBytes) override;
|
||||
|
||||
// --------- Parameter (APVTS) ----------
|
||||
static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
||||
juce::AudioProcessorValueTreeState apvts { *this, nullptr, "PARAMS", createParameterLayout() };
|
||||
|
||||
void setPresetName (const juce::String& s);
|
||||
juce::AudioProcessorValueTreeState apvts{*this, nullptr, "PARAMS", createParameterLayout()};
|
||||
|
||||
void setPresetName(const juce::String &s);
|
||||
|
||||
juce::String getPresetName() const noexcept { return presetName; }
|
||||
|
||||
juce::StringArray getPresetNamesArray() const;
|
||||
@ -95,37 +110,34 @@ public:
|
||||
|
||||
AudioFIFO audioFIFO;
|
||||
|
||||
|
||||
private:
|
||||
// --------- EQ-Kette ----------
|
||||
using Gain = juce::dsp::Gain<float>;
|
||||
using Filter = juce::dsp::IIR::Filter<float>;
|
||||
using Chain = juce::dsp::ProcessorChain<Gain, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Gain>; // 0: LowCut (HP), 1: Peak, 2: HighCut (LP)
|
||||
using Chain = juce::dsp::ProcessorChain<Gain, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter, Filter
|
||||
, Filter, Filter, Filter, Filter, Gain>; // 0: LowCut (HP), 1: Peak, 2: HighCut (LP)
|
||||
juce::Random noiseRandL, noiseRandR;
|
||||
Chain leftChain, rightChain;
|
||||
|
||||
|
||||
void updateFilters(); // Koeffizienten aus Parametern berechnen
|
||||
void updateFilters();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
juce::AudioBuffer<float> processMultiBand(juce::AudioBuffer<float>& lowBuf, juce::AudioBuffer<float>& highBuf);
|
||||
juce::AudioBuffer<float> processMultiBand(juce::AudioBuffer<float> &lowBuf, juce::AudioBuffer<float> &highBuf);
|
||||
|
||||
|
||||
using Coeff = juce::dsp::IIR::Coefficients<float>;
|
||||
|
||||
juce::dsp::LinkwitzRileyFilter<float> mbLowpass;
|
||||
juce::dsp::LinkwitzRileyFilter<float> mbHighpass;
|
||||
juce::dsp::IIR::Filter<float> mbHighPeak;
|
||||
juce::dsp::WaveShaper<float> saturator;
|
||||
|
||||
juce::dsp::IIR::Filter<float> mbHighPeakL;
|
||||
juce::dsp::IIR::Filter<float> mbHighPeakR;
|
||||
|
||||
juce::dsp::LinkwitzRileyFilter<float> mbLowpassL, mbLowpassR;
|
||||
juce::dsp::LinkwitzRileyFilter<float> mbHighpassL, mbHighpassR;
|
||||
juce::dsp::WaveShaper<float> saturatorL, saturatorR;
|
||||
|
||||
|
||||
juce::String presetName { "Init" };
|
||||
juce::String presetName{"Init"};
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CrystalizerEQAudioProcessor)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CrystalizerEQAudioProcessor)
|
||||
};
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user