CrystalizerEQ v1.0.0

This commit is contained in:
Legaeli 2025-11-26 16:44:22 +01:00
parent f270e4613e
commit e239d3e955
6 changed files with 553 additions and 813 deletions

View File

@ -179,7 +179,6 @@ namespace AXIOM {
float ratio = 1.125f; float ratio = 1.125f;
float sizeFor(int step) const noexcept { 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)); return basePx * std::pow(ratio, static_cast<float>(step));
} }
}; };
@ -556,7 +555,6 @@ namespace AXIOM {
float bypassOpacity = 1.0f; float bypassOpacity = 1.0f;
if (svgXml != nullptr) { if (svgXml != nullptr) {
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
auto icon = juce::Drawable::createFromSVG(*svgXml); auto icon = juce::Drawable::createFromSVG(*svgXml);
if (icon != nullptr) { if (icon != nullptr) {
juce::Colour colour; juce::Colour colour;
@ -570,8 +568,6 @@ namespace AXIOM {
: Colours::ACCENTCOLOUR; : Colours::ACCENTCOLOUR;
} }
// Icon zeichnen
icon->replaceColour(juce::Colours::black, colour); icon->replaceColour(juce::Colours::black, colour);
icon->drawWithin(g, iconBounds, icon->drawWithin(g, iconBounds,
juce::RectanglePlacement::centred, bypassOpacity); juce::RectanglePlacement::centred, bypassOpacity);
@ -594,7 +590,6 @@ namespace AXIOM {
bool shouldDrawButtonAsDown) override { bool shouldDrawButtonAsDown) override {
bool isHovered = button.isMouseOverOrDragging(); bool isHovered = button.isMouseOverOrDragging();
bool isToggled = button.getToggleState(); bool isToggled = button.getToggleState();
bool isEnabled = button.isEnabled();
auto bounds = button.getLocalBounds().toFloat(); auto bounds = button.getLocalBounds().toFloat();
auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 1.5f; auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 1.5f;
auto hoverFactor = isHovered ? 1.05f : 1.0f; auto hoverFactor = isHovered ? 1.05f : 1.0f;
@ -602,8 +597,6 @@ namespace AXIOM {
auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor) auto iconBounds = juce::Rectangle<float>(iconSize * hoverFactor, iconSize * hoverFactor)
.withCentre(bounds.getCentre()); .withCentre(bounds.getCentre());
float bypassOpacity = 1.0f;
if (presetMenuButton != nullptr) { if (presetMenuButton != nullptr) {
auto menuIcon = juce::Drawable::createFromSVG(*presetMenuButton); auto menuIcon = juce::Drawable::createFromSVG(*presetMenuButton);
juce::Colour colour; juce::Colour colour;
@ -615,8 +608,6 @@ namespace AXIOM {
: Colours::BACKGROUNDCOLOUR; : Colours::BACKGROUNDCOLOUR;
} }
// Icon zeichnen
menuIcon->replaceColour(juce::Colours::black, colour); menuIcon->replaceColour(juce::Colours::black, colour);
menuIcon->drawWithin(g, iconBounds, menuIcon->drawWithin(g, iconBounds,
juce::RectanglePlacement::centred, 1.0f); juce::RectanglePlacement::centred, 1.0f);
@ -641,7 +632,6 @@ namespace AXIOM {
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsHighlighted,
bool shouldDrawButtonAsDown) override { bool shouldDrawButtonAsDown) override {
const bool isHovered = button.isMouseOverOrDragging(); const bool isHovered = button.isMouseOverOrDragging();
const bool isToggled = button.getToggleState();
auto bounds = button.getLocalBounds().toFloat(); auto bounds = button.getLocalBounds().toFloat();
auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 2 * 0.95f; 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 != if (lowBypassIcon != nullptr && lowCutIcon != nullptr && lowBellIcon != nullptr && lowShelfIcon !=
nullptr) { nullptr) {
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
auto bypassIcon = juce::Drawable::createFromSVG(*lowBypassIcon); auto bypassIcon = juce::Drawable::createFromSVG(*lowBypassIcon);
auto cutIcon = juce::Drawable::createFromSVG(*lowCutIcon); auto cutIcon = juce::Drawable::createFromSVG(*lowCutIcon);
auto bellIcon = juce::Drawable::createFromSVG(*lowBellIcon); auto bellIcon = juce::Drawable::createFromSVG(*lowBellIcon);
@ -712,8 +701,6 @@ namespace AXIOM {
: Colours::SURFACECOLOUR; : Colours::SURFACECOLOUR;
} }
// Icon zeichnen
if (button.getName() == "LowBypass") { if (button.getName() == "LowBypass") {
bypassIcon->replaceColour(juce::Colours::black, colour); bypassIcon->replaceColour(juce::Colours::black, colour);
bypassIcon->drawWithin(g, iconBounds, bypassIcon->drawWithin(g, iconBounds,
@ -769,7 +756,6 @@ namespace AXIOM {
if (highBypassIcon != nullptr && highCutIcon != nullptr && highBellIcon != nullptr && highShelfIcon if (highBypassIcon != nullptr && highCutIcon != nullptr && highBellIcon != nullptr && highShelfIcon
!= nullptr) { != nullptr) {
// WICHTIG: Erstelle für jeden Frame ein NEUES Drawable!
auto bypassIcon = juce::Drawable::createFromSVG(*highBypassIcon); auto bypassIcon = juce::Drawable::createFromSVG(*highBypassIcon);
auto cutIcon = juce::Drawable::createFromSVG(*highCutIcon); auto cutIcon = juce::Drawable::createFromSVG(*highCutIcon);
auto bellIcon = juce::Drawable::createFromSVG(*highBellIcon); auto bellIcon = juce::Drawable::createFromSVG(*highBellIcon);
@ -788,8 +774,6 @@ namespace AXIOM {
: Colours::SURFACECOLOUR; : Colours::SURFACECOLOUR;
} }
// Icon zeichnen
if (button.getName() == "HighBypass") { if (button.getName() == "HighBypass") {
bypassIcon->replaceColour(juce::Colours::black, colour); bypassIcon->replaceColour(juce::Colours::black, colour);
bypassIcon->drawWithin(g, iconBounds, bypassIcon->drawWithin(g, iconBounds,
@ -862,17 +846,14 @@ namespace AXIOM {
void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height, void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height,
float sliderPos, float rotaryStartAngle, float rotaryEndAngle, float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
juce::Slider &slider) override { juce::Slider &slider) override {
// Basis-Implementation - wird in Subklassen überschrieben
auto radius = juce::jmin(width / 2, height / 2) - 4.0f; auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
auto centreX = x + width * 0.5f; auto centreX = x + width * 0.5f;
auto centreY = y + height * 0.5f; auto centreY = y + height * 0.5f;
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
// Outer ring
g.setColour(Colours::BACKGROUNDCOLOUR); g.setColour(Colours::BACKGROUNDCOLOUR);
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f); g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
// Indicator
juce::Path p; juce::Path p;
auto pointerLength = radius * 0.6f; auto pointerLength = radius * 0.6f;
auto pointerThickness = 2.0f; auto pointerThickness = 2.0f;
@ -911,7 +892,6 @@ namespace AXIOM {
auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle; auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle;
const float arcPathWidth = 3.0f; const float arcPathWidth = 3.0f;
// Background
g.setColour(Colours::BACKGROUNDCOLOUR); g.setColour(Colours::BACKGROUNDCOLOUR);
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f); 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); g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth);
// Arc für Gain-Anzeige
juce::Path valueArc; juce::Path valueArc;
valueArc.addCentredArc(centreX, centreY, radius, radius, valueArc.addCentredArc(centreX, centreY, radius, radius,
0.0f, mid, angle, true); 0.0f, mid, angle, true);
@ -927,7 +906,6 @@ namespace AXIOM {
g.setColour(accentCol); g.setColour(accentCol);
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth)); g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
// Pointer
juce::Path p; juce::Path p;
p.addRectangle(-arcPathWidth / 2, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, p.addRectangle(-arcPathWidth / 2, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
radius * 0.5f * hoverFactor); radius * 0.5f * hoverFactor);
@ -961,7 +939,6 @@ namespace AXIOM {
} }
// Einfacher Ring
g.setColour(surfaceCol); g.setColour(surfaceCol);
g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f); g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f);
@ -971,7 +948,7 @@ namespace AXIOM {
g.setColour(accentCol); g.setColour(accentCol);
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth)); g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
// Dünner Pointer
juce::Path p; juce::Path p;
p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
radius * 0.4f * hoverFactor); radius * 0.4f * hoverFactor);
@ -990,7 +967,6 @@ namespace AXIOM {
juce::Slider::SliderLayout layout; juce::Slider::SliderLayout layout;
auto bounds = slider.getLocalBounds(); auto bounds = slider.getLocalBounds();
// TextBox in der Mitte (wie bei BaseSliderLookAndFeel)
layout.textBoxBounds = juce::Rectangle<int>( layout.textBoxBounds = juce::Rectangle<int>(
bounds.getCentreX() - bounds.getWidth() / 2, bounds.getCentreX() - bounds.getWidth() / 2,
bounds.getCentreY() - bounds.getHeight() / 2, bounds.getCentreY() - bounds.getHeight() / 2,
@ -998,8 +974,6 @@ namespace AXIOM {
bounds.getHeight() 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); layout.sliderBounds = bounds.reduced(labelPadding);
return layout; return layout;
@ -1008,21 +982,17 @@ namespace AXIOM {
void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height, void drawRotarySlider(juce::Graphics &g, int x, int y, int width, int height,
float sliderPos, float rotaryStartAngle, float rotaryEndAngle, float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
juce::Slider &slider) override { juce::Slider &slider) override {
// Basis-Implementation - wird in Subklassen überschrieben
auto radius = juce::jmin(width / 2, height / 2) - 4.0f; auto radius = juce::jmin(width / 2, height / 2) - 4.0f;
auto centreX = x + width * 0.5f; auto centreX = x + width * 0.5f;
auto centreY = y + height * 0.5f; auto centreY = y + height * 0.5f;
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
bool isEnabled = slider.isEnabled(); bool isEnabled = slider.isEnabled();
bool isHovered = slider.isMouseOverOrDragging();
// Outer ring
g.setColour(Colours::BACKGROUNDCOLOUR); g.setColour(Colours::BACKGROUNDCOLOUR);
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f); g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
juce::Colour accentCol = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR; juce::Colour accentCol = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;
// Indicator
juce::Path p; juce::Path p;
auto pointerLength = radius * 0.6f; auto pointerLength = radius * 0.6f;
auto pointerThickness = 2.0f; auto pointerThickness = 2.0f;
@ -1033,7 +1003,6 @@ namespace AXIOM {
g.fillPath(p); g.fillPath(p);
// Dann zeichne die Labels
const auto bounds = juce::Rectangle<int>(x, y, width, height); const auto bounds = juce::Rectangle<int>(x, y, width, height);
const auto centre = bounds.getCentre().toFloat(); const auto centre = bounds.getCentre().toFloat();
const float textRadius = std::min(width, height) * 0.5f + 5.0f; const float textRadius = std::min(width, height) * 0.5f + 5.0f;
@ -1044,7 +1013,6 @@ namespace AXIOM {
auto value = slider.getValue(); auto value = slider.getValue();
for (int i = 0; i < labels.size(); ++i) { 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 const bool isMatch = value == i; // Toleranz: 0.5
juce::Colour colour; juce::Colour colour;
@ -1105,7 +1073,6 @@ namespace AXIOM {
} }
// Einfacher Ring
g.setColour(surfaceCol); g.setColour(surfaceCol);
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f); g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f);
@ -1118,7 +1085,7 @@ namespace AXIOM {
g.setColour(accentCol); g.setColour(accentCol);
g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth)); g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth));
// Dünner Pointer
juce::Path p; juce::Path p;
p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, p.addRectangle(-1.0f, (-radius - arcPathWidth) * hoverFactor, arcPathWidth,
radius * 0.4f * hoverFactor); radius * 0.4f * hoverFactor);
@ -1168,9 +1135,6 @@ namespace AXIOM {
}; };
struct Layout { struct Layout {
///
/// @param mode Spacing mode -- e.g. Spacing::SizeMode::XS
/// @return Gap size in px as int
static int gap(Spacing::SizeMode mode) { static int gap(Spacing::SizeMode mode) {
return juce::roundToInt(Spacing::scale.space(mode)); return juce::roundToInt(Spacing::scale.space(mode));
} }
@ -1327,10 +1291,8 @@ namespace AXIOM {
bool shouldDrawButtonAsDown) override { bool shouldDrawButtonAsDown) override {
auto bounds = button.getLocalBounds(); auto bounds = button.getLocalBounds();
// Text holen
auto text = button.getButtonText(); auto text = button.getButtonText();
// Farbe wählen
auto isHovered = button.isMouseOver(); auto isHovered = button.isMouseOver();
bool isEnabled = button.isEnabled(); bool isEnabled = button.isEnabled();
juce::Colour colour = isHovered ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR; juce::Colour colour = isHovered ? Colours::FOREGROUNDHOVER : Colours::FOREGROUNDCOLOUR;
@ -1340,11 +1302,9 @@ namespace AXIOM {
g.setColour(colour); g.setColour(colour);
// Font einstellen
auto font = Typography::getFont(Typography::Style::Button, 1.0f); auto font = Typography::getFont(Typography::Style::Button, 1.0f);
g.setFont(font); g.setFont(font);
// Text zeichnen
g.drawFittedText(text, g.drawFittedText(text,
bounds.reduced(4), bounds.reduced(4),
juce::Justification::centred, juce::Justification::centred,
@ -1373,7 +1333,7 @@ namespace AXIOM {
std::function<void()> onMouseDown; std::function<void()> onMouseDown;
void mouseDown(const juce::MouseEvent &event) override { void mouseDown(const juce::MouseEvent &event) override {
juce::TextEditor::mouseDown(event); // Wichtig: Original-Verhalten beibehalten juce::TextEditor::mouseDown(event);
if (onMouseDown) if (onMouseDown)
onMouseDown(); onMouseDown();
} }
@ -1383,7 +1343,6 @@ namespace AXIOM {
public: public:
void fillTextEditorBackground(juce::Graphics &g, int width, int height, void fillTextEditorBackground(juce::Graphics &g, int width, int height,
juce::TextEditor &textEditor) override { juce::TextEditor &textEditor) override {
// Hintergrundfarbe
if (textEditor.isEnabled()) { if (textEditor.isEnabled()) {
g.setColour(Colours::SURFACECOLOUR); g.setColour(Colours::SURFACECOLOUR);
} else { } else {
@ -1404,7 +1363,6 @@ namespace AXIOM {
void drawTextEditorOutline(juce::Graphics &g, int width, int height, void drawTextEditorOutline(juce::Graphics &g, int width, int height,
juce::TextEditor &textEditor) override { juce::TextEditor &textEditor) override {
// Rahmen
auto bounds = juce::Rectangle<float>(0, 0, width, height); auto bounds = juce::Rectangle<float>(0, 0, width, height);
auto radius = Shape::getRadius(Shape::RadiusMode::S, bounds); auto radius = Shape::getRadius(Shape::RadiusMode::S, bounds);
auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin); auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin);
@ -1414,13 +1372,10 @@ namespace AXIOM {
if (!textEditor.isEnabled()) { if (!textEditor.isEnabled()) {
outlineColour = Colours::FOREGROUNDBYPASS.withAlpha(0.3f); outlineColour = Colours::FOREGROUNDBYPASS.withAlpha(0.3f);
} else if (textEditor.hasKeyboardFocus(true)) { } else if (textEditor.hasKeyboardFocus(true)) {
// Fokussiert - Accent-Farbe
outlineColour = Colours::ACCENTCOLOUR; outlineColour = Colours::ACCENTCOLOUR;
} else if (textEditor.isMouseOver()) { } else if (textEditor.isMouseOver()) {
// Hover
outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f); outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f);
} else { } else {
// Normal
outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f); outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f);
} }
@ -1440,153 +1395,136 @@ namespace AXIOM {
}; };
class PresetComboBoxLookAndFeel : public juce::LookAndFeel_V4, class PresetComboBoxLookAndFeel : public juce::LookAndFeel_V4,
private juce::ComponentListener private juce::ComponentListener {
{ public:
public: PresetComboBoxLookAndFeel() = default;
PresetComboBoxLookAndFeel() = default;
~PresetComboBoxLookAndFeel() override ~PresetComboBoxLookAndFeel() override {
{ }
}
void getIdealPopupMenuItemSize (const juce::String& text, bool /*isSeparator*/, void getIdealPopupMenuItemSize(const juce::String &text, bool isSeparator,
int /*standardMenuItemHeight*/, int& idealWidth, int standardMenuItemHeight, int &idealWidth,
int& idealHeight) override int &idealHeight) override {
{ idealHeight = 50;
idealHeight = 50;
auto font = Typography::getFont (Typography::Style::Body, 1.0f); auto font = Typography::getFont(Typography::Style::Body, 1.0f);
idealWidth = font.getStringWidth (text); idealWidth = font.getStringWidth(text);
} }
void drawPopupMenuUpDownArrow(juce::Graphics& g, int width, int height, void drawPopupMenuUpDownArrow(juce::Graphics &g, int width, int height,
bool isScrollUpArrow) override bool isScrollUpArrow) override {
{ g.setColour(Colours::ACCENTCOLOUR);
g.setColour(Colours::ACCENTCOLOUR);
auto arrowZone = juce::Rectangle<float>(0.0f, 0.0f, (float)width, (float)height); auto arrowZone = juce::Rectangle<float>(0.0f, 0.0f, (float) width, (float) height);
auto arrowWidth = arrowZone.getWidth() * 0.1f; auto arrowWidth = arrowZone.getWidth() * 0.1f;
auto arrowHeight = arrowZone.getHeight() * 0.1f; auto arrowHeight = arrowZone.getHeight() * 0.1f;
juce::Path arrow; juce::Path arrow;
arrow.startNewSubPath(arrowZone.getCentreX() - arrowWidth * 0.5f, arrow.startNewSubPath(arrowZone.getCentreX() - arrowWidth * 0.5f,
arrowZone.getCentreY() + (isScrollUpArrow ? arrowHeight * 0.5f : -arrowHeight * 0.5f)); arrowZone.getCentreY() + (isScrollUpArrow
arrow.lineTo(arrowZone.getCentreX(), ? arrowHeight * 0.5f
arrowZone.getCentreY() + (isScrollUpArrow ? -arrowHeight * 0.5f : arrowHeight * 0.5f)); : -arrowHeight * 0.5f));
arrow.lineTo(arrowZone.getCentreX() + arrowWidth * 0.5f, arrow.lineTo(arrowZone.getCentreX(),
arrowZone.getCentreY() + (isScrollUpArrow ? arrowHeight * 0.5f : -arrowHeight * 0.5f)); 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 void drawPopupMenuBackground(juce::Graphics &g, int width, int height) override {
{ g.fillAll(Colours::SURFACECOLOUR);
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, g.fillAll(backGroundColour);
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; 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)); bool isHovered = box.isMouseOver();
if (isTicked) { bool isEnabled = box.isEnabled();
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);
}
void drawComboBox(juce::Graphics& g, int width, int height, bool isButtonDown, juce::Colour bgColour;
int buttonX, int buttonY, int buttonW, int buttonH, if (!isEnabled) {
juce::ComboBox& box) override bgColour = Colours::SURFACEBYPASS;
{ } else if (isButtonDown) {
auto bounds = juce::Rectangle<float>(0, 0, (float)width, (float)height); bgColour = Colours::SURFACEHOVER;
auto cornerSize = Shape::getRadius(Shape::RadiusMode::S, bounds); } else if (isHovered) {
bgColour = Colours::SURFACECOLOUR.brighter(0.1f);
} else {
bgColour = Colours::SURFACECOLOUR;
}
bool isHovered = box.isMouseOver(); g.setColour(bgColour);
bool isEnabled = box.isEnabled(); g.fillRoundedRectangle(bounds, cornerSize);
juce::Colour bgColour; juce::Colour outlineColour;
if (!isEnabled) { if (!isEnabled) {
bgColour = Colours::SURFACEBYPASS; outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.2f);
} else if (isButtonDown) { } else if (isButtonDown) {
bgColour = Colours::SURFACEHOVER; outlineColour = Colours::ACCENTCOLOUR;
} else if (isHovered) { } else if (isHovered) {
bgColour = Colours::SURFACECOLOUR.brighter(0.1f); outlineColour = Colours::FOREGROUNDHOVER.withAlpha(0.5f);
} else { } else {
bgColour = Colours::SURFACECOLOUR; outlineColour = Colours::FOREGROUNDCOLOUR.withAlpha(0.3f);
} }
g.setColour(bgColour); auto strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin);
g.fillRoundedRectangle(bounds, cornerSize); g.setColour(outlineColour);
g.drawRoundedRectangle(bounds.reduced(strokeWidth * 0.5f), cornerSize, strokeWidth);
juce::Colour outlineColour; auto arrowZone = juce::Rectangle<float>((float) buttonX, (float) buttonY,
if (!isEnabled) { (float) buttonW, (float) buttonH);
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 strokeWidth = Shape::getStrokeWidth(Shape::StrokeMode::Thin); juce::Colour arrowColour = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR;
g.setColour(outlineColour); if (isHovered && isEnabled) {
g.drawRoundedRectangle(bounds.reduced(strokeWidth * 0.5f), cornerSize, strokeWidth); arrowColour = Colours::ACCENTHOVER;
}
auto arrowZone = juce::Rectangle<float>((float)buttonX, (float)buttonY, g.setColour(arrowColour);
(float)buttonW, (float)buttonH);
juce::Colour arrowColour = isEnabled ? Colours::ACCENTCOLOUR : Colours::ACCENTWEAKCOLOUR; juce::Path arrow;
if (isHovered && isEnabled) { auto arrowW = arrowZone.getWidth() * 0.3f;
arrowColour = Colours::ACCENTHOVER; 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; g.strokePath(arrow, juce::PathStrokeType(1.5f));
auto arrowW = arrowZone.getWidth() * 0.3f; }
auto arrowH = arrowZone.getHeight() * 0.15f;
auto centreX = arrowZone.getCentreX();
auto centreY = arrowZone.getCentreY();
arrow.startNewSubPath(centreX - arrowW * 0.5f, centreY - arrowH * 0.5f); void positionComboBoxText(juce::ComboBox &box, juce::Label &label) override {
arrow.lineTo(centreX, centreY + arrowH * 0.5f); label.setBounds(8, 0, box.getWidth() - 30, box.getHeight());
arrow.lineTo(centreX + arrowW * 0.5f, centreY - arrowH * 0.5f); label.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
label.setJustificationType(juce::Justification::centredLeft);
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:
};
private:
};
private: private:
}; };

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15) 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) # JUCE einbinden (lokaler Pfad zu JUCE, z.B. als Submodul oder manuell kopiert)
add_subdirectory(juce) # <-- Pfad zu deinem JUCE-Ordner relativ zum Projekt add_subdirectory(juce) # <-- Pfad zu deinem JUCE-Ordner relativ zum Projekt

View File

@ -1,11 +1,3 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin editor.
==============================================================================
*/
#include "PluginProcessor.h" #include "PluginProcessor.h"
#include "PluginEditor.h" #include "PluginEditor.h"
#include "AXIOMDesignSystem.h" #include "AXIOMDesignSystem.h"
@ -22,7 +14,6 @@ using Components = DesignSystem::Components;
using SliderStyles = Components::SliderStyles; using SliderStyles = Components::SliderStyles;
//region setupSliders
void CrystalizerEQAudioProcessorEditor::setupSliders() { void CrystalizerEQAudioProcessorEditor::setupSliders() {
auto style = juce::Slider::RotaryHorizontalVerticalDrag; auto style = juce::Slider::RotaryHorizontalVerticalDrag;
for (auto *s: sliders) { for (auto *s: sliders) {
@ -57,11 +48,8 @@ void CrystalizerEQAudioProcessorEditor::setupSliders() {
highBandSlopeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0); highBandSlopeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
} }
//endregion setupSliders
//region setupAttachments
void CrystalizerEQAudioProcessorEditor::setupAttachments() { void CrystalizerEQAudioProcessorEditor::setupAttachments() {
lowBandFreqAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandFreq", lowBandFreqSlider); lowBandFreqAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandFreq", lowBandFreqSlider);
lowBandSlopeAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandSlope", lowBandSlopeSlider); lowBandSlopeAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandSlope", lowBandSlopeSlider);
lowBandGainAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "LowBandGain", lowBandGainSlider); 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); inputAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "InputGain", inputSlider);
outputAttach = std::make_unique<SliderAttach>(audioProcessor.apvts, "OutputGain", outputSlider); 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() { void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
titleLabel.setText("Crystalizer", juce::dontSendNotification); titleLabel.setText("Crystalizer", juce::dontSendNotification);
lowBypass.setName("LowBypass"); lowBypass.setName("LowBypass");
lowBell.setName("LowBell"); lowBell.setName("LowBell");
lowCut.setName("LowCut"); lowCut.setName("LowCut");
@ -130,38 +121,22 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
inputSlider.setName("Input"); inputSlider.setName("Input");
outputSlider.setName("Output"); outputSlider.setName("Output");
//lowBandFreqSlider.setTextValueSuffix ("\nHz");
//lowBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
lowBandGainSlider.setTextValueSuffix("\ndB"); lowBandGainSlider.setTextValueSuffix("\ndB");
//lowBandQSlider.setTextValueSuffix ("\nQ");
//peak1FreqSlider.setTextValueSuffix("\nHz");
peak1GainSlider.setTextValueSuffix("\ndB"); peak1GainSlider.setTextValueSuffix("\ndB");
//peak1QSlider.setTextValueSuffix("\nQ");
peak1BypassButton.setName("peak1Bypass"); peak1BypassButton.setName("peak1Bypass");
//peak2FreqSlider.setTextValueSuffix("\nHz");
peak2GainSlider.setTextValueSuffix("\ndB"); peak2GainSlider.setTextValueSuffix("\ndB");
//peak2QSlider.setTextValueSuffix("\nQ");
peak2BypassButton.setName("peak2Bypass"); peak2BypassButton.setName("peak2Bypass");
//peak3FreqSlider.setTextValueSuffix("\nHz");
peak3GainSlider.setTextValueSuffix("\ndB"); peak3GainSlider.setTextValueSuffix("\ndB");
//peak3QSlider.setTextValueSuffix("\nQ");
peak3BypassButton.setName("peak3Bypass"); peak3BypassButton.setName("peak3Bypass");
//highBandFreqSlider.setTextValueSuffix ("\nHz");
//highBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
highBandGainSlider.setTextValueSuffix("\ndB"); highBandGainSlider.setTextValueSuffix("\ndB");
// highBandQSlider.setTextValueSuffix ("\nQ");
inputSlider.setTextValueSuffix("\nIN"); inputSlider.setTextValueSuffix("\nIN");
outputSlider.setTextValueSuffix("\nOUT"); outputSlider.setTextValueSuffix("\nOUT");
crystalizeButton.setName("CrystalizeButton"); crystalizeButton.setName("CrystalizeButton");
resetButton.setName("ResetButton"); resetButton.setName("ResetButton");
@ -176,9 +151,7 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
deletePresetButton.setButtonText("Delete Preset"); deletePresetButton.setButtonText("Delete Preset");
} }
//endregion setupDisplayNames
//region setupToggleButtons
void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
bypassButtonLookAndFeel = std::make_unique<Components::BypassButtonLookAndFeel>(); bypassButtonLookAndFeel = std::make_unique<Components::BypassButtonLookAndFeel>();
svgToggleButtonLookAndFeel = std::make_unique<Components::SvgToggleButtonLookAndFeel>(); svgToggleButtonLookAndFeel = std::make_unique<Components::SvgToggleButtonLookAndFeel>();
@ -212,40 +185,23 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
&peak1BypassButton, &peak2BypassButton, &peak3BypassButton, &crystalizeButton, &masterBypassButton &peak1BypassButton, &peak2BypassButton, &peak3BypassButton, &crystalizeButton, &masterBypassButton
}) { }) {
b->onClick = [this, b]() { b->onClick = [this, b]() {
juce::String paramID; // nimm juce::String statt std::string
juce::String mode; juce::String mode;
if (b == &peak1BypassButton) { if (b == &peak1BypassButton) {
paramID = "Peak1Bypass";
mode = "Low-Mid"; mode = "Low-Mid";
} else if (b == &peak2BypassButton) { } else if (b == &peak2BypassButton) {
paramID = "Peak2Bypass";
mode = "Mid"; mode = "Mid";
} else if (b == &peak3BypassButton) { } else if (b == &peak3BypassButton) {
paramID = "Peak3Bypass";
mode = "High-Mid"; mode = "High-Mid";
} else if (b == &crystalizeButton) {
paramID = "CrystalizeButton";
} else if (b == &masterBypassButton) { } else if (b == &masterBypassButton) {
paramID = "MasterBypass";
mode = "Master"; mode = "Master";
} }
if (auto *param = audioProcessor.apvts.getParameter(paramID)) { const float target = b->getToggleState() ? 1.0f : 0.0f;
const bool isToggled = b->getToggleState();
const float target = isToggled ? 1.0f : 0.0f; if (mode == "Low-Mid") disableLowMidBand(target);
else if (mode == "Mid") disableMidBand(target);
// Nur senden, wenn sich wirklich etwas ändert (Schwelle 0.5f) else if (mode == "High-Mid") disableHighMidBand(target);
if ((param->getValue() >= 0.5f) != isToggled) { else if (mode == "Master") disableEverything(target);
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 (b == &crystalizeButton) { if (b == &crystalizeButton) {
isAnimatingCrystalize = true; isAnimatingCrystalize = true;
@ -255,9 +211,7 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() {
} }
} }
//endregion setupToggleButtons
//region disableLowBand
void CrystalizerEQAudioProcessorEditor::disableLowBand(const float target) { void CrystalizerEQAudioProcessorEditor::disableLowBand(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
lowBandFreqSlider.setEnabled(isToggled); lowBandFreqSlider.setEnabled(isToggled);
@ -269,9 +223,8 @@ void CrystalizerEQAudioProcessorEditor::disableLowBand(const float target) {
lowBandQSlider.setEnabled(isToggled); lowBandQSlider.setEnabled(isToggled);
lowBandQLabel.setEnabled(isToggled); lowBandQLabel.setEnabled(isToggled);
}; };
//endregion disableLowBand
//region disableLowMidBand
void CrystalizerEQAudioProcessorEditor::disableLowMidBand(const float target) { void CrystalizerEQAudioProcessorEditor::disableLowMidBand(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
peak1GainSlider.setEnabled(isToggled); peak1GainSlider.setEnabled(isToggled);
@ -282,9 +235,7 @@ void CrystalizerEQAudioProcessorEditor::disableLowMidBand(const float target) {
peak1FreqLabel.setEnabled(isToggled); peak1FreqLabel.setEnabled(isToggled);
} }
//endregion disableLowMidBand
//region disableMidBand
void CrystalizerEQAudioProcessorEditor::disableMidBand(const float target) { void CrystalizerEQAudioProcessorEditor::disableMidBand(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
peak2GainSlider.setEnabled(isToggled); peak2GainSlider.setEnabled(isToggled);
@ -295,9 +246,7 @@ void CrystalizerEQAudioProcessorEditor::disableMidBand(const float target) {
peak2FreqLabel.setEnabled(isToggled); peak2FreqLabel.setEnabled(isToggled);
} }
//endregion disableMidBand
//region disableHighMidBand
void CrystalizerEQAudioProcessorEditor::disableHighMidBand(const float target) { void CrystalizerEQAudioProcessorEditor::disableHighMidBand(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
peak3GainSlider.setEnabled(isToggled); peak3GainSlider.setEnabled(isToggled);
@ -308,9 +257,7 @@ void CrystalizerEQAudioProcessorEditor::disableHighMidBand(const float target) {
peak3FreqLabel.setEnabled(isToggled); peak3FreqLabel.setEnabled(isToggled);
} }
//endregion disableHighMidBand
//region disableHighBand
void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) { void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
highBandFreqSlider.setEnabled(isToggled); highBandFreqSlider.setEnabled(isToggled);
@ -322,9 +269,8 @@ void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) {
highBandQSlider.setEnabled(isToggled); highBandQSlider.setEnabled(isToggled);
highBandQLabel.setEnabled(isToggled); highBandQLabel.setEnabled(isToggled);
}; };
//endregion disableHighBand
//region disableEverything
void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) { void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) {
bool isToggled = target <= 0.5f; bool isToggled = target <= 0.5f;
@ -372,9 +318,7 @@ void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) {
} }
} }
//endregion disableEverything
//region addComponentsToLayout
void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() { void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
addAndMakeVisible(headerBar); addAndMakeVisible(headerBar);
headerBar.addAndMakeVisible(titleLabel); headerBar.addAndMakeVisible(titleLabel);
@ -489,19 +433,14 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
addAndMakeVisible(footerBar); addAndMakeVisible(footerBar);
} }
//endregion addComponentsToLayout
//region setupLabels
void CrystalizerEQAudioProcessorEditor::setupLabels() { void CrystalizerEQAudioProcessorEditor::setupLabels() {
auto setupLabel = [](juce::Label &label, const juce::String &text) { auto setupLabel = [](juce::Label &label, const juce::String &text) {
label.setText(text, juce::dontSendNotification); label.setText(text, juce::dontSendNotification);
label.setJustificationType(juce::Justification::centred); label.setJustificationType(juce::Justification::centred);
label.setColour(juce::Label::textColourId, juce::Colours::black); label.setColour(juce::Label::textColourId, juce::Colours::black);
label.setInterceptsMouseClicks(false, false); // Klicks gehen an den Slider label.setInterceptsMouseClicks(false, false); // Klicks gehen an den Slider
}; }; {
//SetupLabels
{
//LOW-BAND //LOW-BAND
setupLabel(lowBandFreqLabel, "Low\nHz"); setupLabel(lowBandFreqLabel, "Low\nHz");
setupLabel(lowBandSlopeLabel, "Slope"); setupLabel(lowBandSlopeLabel, "Slope");
@ -543,13 +482,9 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() {
setupLabel(presetBoxLabel, "Presets"); setupLabel(presetBoxLabel, "Presets");
/*setupLabel(inputLabel, "IN");
setupLabel(outputLabel, "OUT");*/
} }
} }
//endregion setupLabels
void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() { void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() {
Typography::applyToLabel(titleLabel, Typography::Style::Display, 1.f); Typography::applyToLabel(titleLabel, Typography::Style::Display, 1.f);
@ -564,7 +499,7 @@ void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() {
} }
} }
//region setupSliderTextBoxes
void CrystalizerEQAudioProcessorEditor::setupSliderTextBoxes() { void CrystalizerEQAudioProcessorEditor::setupSliderTextBoxes() {
baseLookAndFeel = std::make_unique<AXIOM::DesignSystem::Components::BaseSliderLookAndFeel>(); baseLookAndFeel = std::make_unique<AXIOM::DesignSystem::Components::BaseSliderLookAndFeel>();
for (auto *s: sliders) { for (auto *s: sliders) {
@ -572,9 +507,7 @@ void CrystalizerEQAudioProcessorEditor::setupSliderTextBoxes() {
} }
} }
//endregion setupSliderTextBoxes
//region handleLowBandModes
void CrystalizerEQAudioProcessorEditor::handleLowBandModes() { void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
for (int i = 0; i < lowBandModeButtons.size(); ++i) { for (int i = 0; i < lowBandModeButtons.size(); ++i) {
lowBandModeButtons[i]->onClick = [this, i] { lowBandModeButtons[i]->onClick = [this, i] {
@ -589,7 +522,6 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
lowBandModeButtons[j]->setToggleState(false, juce::dontSendNotification); lowBandModeButtons[j]->setToggleState(false, juce::dontSendNotification);
} }
} }
// Aktuellen aktivieren
lowBandBools[i] = true; lowBandBools[i] = true;
param->setValueNotifyingHost(param->convertTo0to1((float) i)); param->setValueNotifyingHost(param->convertTo0to1((float) i));
@ -604,9 +536,7 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
} }
} }
//endregion handleLowBandModes
//region handleHighBandModes
void CrystalizerEQAudioProcessorEditor::handleHighBandModes() { void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
for (int i = 0; i < highBandModeButtons.size(); ++i) { for (int i = 0; i < highBandModeButtons.size(); ++i) {
highBandModeButtons[i]->onClick = [this, i] { highBandModeButtons[i]->onClick = [this, i] {
@ -627,7 +557,6 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
param->endChangeGesture(); param->endChangeGesture();
updateFrequencyRanges(); updateFrequencyRanges();
} }
} else if (highBandBools[i]) { } else if (highBandBools[i]) {
highBandModeButtons[i]->setToggleState(true, juce::dontSendNotification); highBandModeButtons[i]->setToggleState(true, juce::dontSendNotification);
} }
@ -635,9 +564,7 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() {
} }
} }
//endregion handleHighBandModes
//region setupEventListeners
void CrystalizerEQAudioProcessorEditor::setupEventListeners() { void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
presetMenuButton.onClick = [this]() { presetMenuButton.onClick = [this]() {
auto *menu = new DesignSystem::PresetMenu(&presetNameInput, &savePresetButton, &deletePresetButton); auto *menu = new DesignSystem::PresetMenu(&presetNameInput, &savePresetButton, &deletePresetButton);
@ -657,10 +584,6 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
nullptr nullptr
); );
box.setLookAndFeel(presetMenuLookAndFeel.get()); box.setLookAndFeel(presetMenuLookAndFeel.get());
/*juce::Timer::callAfterDelay(100, [this]() {
presetNameInput.giveAwayKeyboardFocus();
});*/
}; };
savePresetButton.onClick = [this]() { savePresetButton.onClick = [this]() {
@ -707,7 +630,7 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
} }
const auto selectedPresetWithExt = selectedPreset + ".xml"; const auto selectedPresetWithExt = selectedPreset + ".xml";
audioProcessor.loadPreset(selectedPresetWithExt); audioProcessor.loadPreset(selectedPresetWithExt);
updateModeButtons(); // ← NEU! updateModeButtons(); // ← NEU!
updateFrequencyRanges(); updateFrequencyRanges();
}; };
@ -737,9 +660,8 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() {
}; };
} }
//endregion setupEventListeners
void CrystalizerEQAudioProcessorEditor::updateModeButtons() { void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
// Low Band Modes aktualisieren
int lowMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("LowBandModes")->load()); int lowMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("LowBandModes")->load());
for (int i = 0; i < lowBandModeButtons.size(); ++i) { for (int i = 0; i < lowBandModeButtons.size(); ++i) {
@ -747,7 +669,6 @@ void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
lowBandModeButtons[i]->setToggleState(lowBandBools[i], juce::dontSendNotification); lowBandModeButtons[i]->setToggleState(lowBandBools[i], juce::dontSendNotification);
} }
// High Band Modes aktualisieren
int highMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("HighBandModes")->load()); int highMode = static_cast<int>(audioProcessor.apvts.getRawParameterValue("HighBandModes")->load());
for (int i = 0; i < highBandModeButtons.size(); ++i) { for (int i = 0; i < highBandModeButtons.size(); ++i) {
@ -755,7 +676,17 @@ void CrystalizerEQAudioProcessorEditor::updateModeButtons() {
highBandModeButtons[i]->setToggleState(highBandBools[i], juce::dontSendNotification); 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() { void CrystalizerEQAudioProcessorEditor::initPresetSystem() {
auto presets = audioProcessor.getPresetNamesArray(); auto presets = audioProcessor.getPresetNamesArray();
presetBox.addItemList(presets, 1); presetBox.addItemList(presets, 1);
@ -780,8 +711,6 @@ void CrystalizerEQAudioProcessorEditor::initPresetSystem() {
presetBox.setLookAndFeel(presetComboBoxLookAndFeel.get()); presetBox.setLookAndFeel(presetComboBoxLookAndFeel.get());
} }
//endregion initPresetSystem
//============================================================================== //==============================================================================
CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &p) CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &p)
: AudioProcessorEditor(&p), audioProcessor(p), spectrumAnalyzer(p.audioFIFO, 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); auto logoIcon = juce::XmlDocument::parse(BinaryData::logo_icon_svg);
if (logoIcon != nullptr) if (logoIcon != nullptr)
logoDrawable = juce::Drawable::createFromSVG(*logoIcon); logoDrawable = juce::Drawable::createFromSVG(*logoIcon);
audioProcessor.apvts.addParameterListener("LowBandModes", this);
audioProcessor.apvts.addParameterListener("HighBandModes", this);
updateModeButtons();
} }
CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() { CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() {
@ -821,7 +752,8 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() {
presetNameInput.setLookAndFeel(nullptr); presetNameInput.setLookAndFeel(nullptr);
presetBox.setLookAndFeel(nullptr); presetBox.setLookAndFeel(nullptr);
audioProcessor.apvts.removeParameterListener("LowBandModes", this);
audioProcessor.apvts.removeParameterListener("HighBandModes", this);
for (auto *b: lowBandModeButtons) { for (auto *b: lowBandModeButtons) {
b->setLookAndFeel(nullptr); b->setLookAndFeel(nullptr);
} }
@ -847,7 +779,6 @@ float CrystalizerEQAudioProcessorEditor::getInterpolatedDb(float exactBin) {
return y1 + fraction * (y2 - y1); return y1 + fraction * (y2 - y1);
} }
//region paintAnalyzer
void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) { void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
analyzerRect = getLocalArea(&analyzerArea, analyzerArea.getLocalBounds()); analyzerRect = getLocalArea(&analyzerArea, analyzerArea.getLocalBounds());
juce::Graphics::ScopedSaveState guard(g); juce::Graphics::ScopedSaveState guard(g);
@ -855,15 +786,15 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
auto r = analyzerRect.toFloat(); auto r = analyzerRect.toFloat();
// Hintergrund // BACKGROUND
g.setColour(juce::Colours::black); g.setColour(juce::Colours::black);
g.fillRect(r); g.fillRect(r);
// Rahmen // BORDER
g.setColour(juce::Colours::grey); g.setColour(juce::Colours::grey);
g.drawRect(analyzerRect); g.drawRect(analyzerRect);
// === Spektrum zeichnen === // SPECTRUM
g.setColour(Colours::ACCENTCOLOUR); g.setColour(Colours::ACCENTCOLOUR);
const float analyzerWidth = analyzerRect.getWidth(); const float analyzerWidth = analyzerRect.getWidth();
@ -874,49 +805,36 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
const float maxDb = spectrumAnalyzer.MAXDB; const float maxDb = spectrumAnalyzer.MAXDB;
const float dbRange = maxDb - minDb; // = 96 dB 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 minFreq = spectrumAnalyzer.MINFREQ; // 20 Hz
const float maxFreq = spectrumAnalyzer.MAXFREQ; // 20 kHz const float maxFreq = spectrumAnalyzer.MAXFREQ; // 20 kHz
float sampleRate = spectrumAnalyzer.sampleRate;
float deltaF = spectrumAnalyzer.deltaF; float deltaF = spectrumAnalyzer.deltaF;
// Zeichne für jede X-Position
juce::Path spectrumPath; juce::Path spectrumPath;
bool pathStarted = false; bool pathStarted = false;
// Erhöhe die Auflösung - zeichne mehr Punkte als Pixel const int resolutionMultiplier = 4;
const int resolutionMultiplier = 4; // 4x mehr Punkte
const int numPoints = static_cast<int>(analyzerWidth) * resolutionMultiplier; const int numPoints = static_cast<int>(analyzerWidth) * resolutionMultiplier;
for (int i = 0; i < numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
// Berechne X-Position
float x = (i / static_cast<float>(numPoints)) * analyzerWidth; float x = (i / static_cast<float>(numPoints)) * analyzerWidth;
// Berechne Frequenz (logarithmisch)
float normalizedX = i / static_cast<float>(numPoints); float normalizedX = i / static_cast<float>(numPoints);
float logMin = std::log10(minFreq); float logMin = std::log10(minFreq);
float logMax = std::log10(maxFreq); float logMax = std::log10(maxFreq);
float logFreq = logMin + normalizedX * (logMax - logMin); float logFreq = logMin + normalizedX * (logMax - logMin);
float freq = std::pow(10.0f, logFreq); float freq = std::pow(10.0f, logFreq);
// Finde Bin und interpoliere
float exactBin = freq / deltaF; float exactBin = freq / deltaF;
float dB = getInterpolatedDb(exactBin); float dB = getInterpolatedDb(exactBin);
// Normalisiere
float normalized = (dB - minDb) / dbRange; float normalized = (dB - minDb) / dbRange;
normalized = juce::jlimit(0.0f, 1.0f, normalized); normalized = juce::jlimit(0.0f, 1.0f, normalized);
// Berechne Y-Position
float pixelHeight = normalized * analyzerHeight; float pixelHeight = normalized * analyzerHeight;
float y = analyzerBottom - pixelHeight; float y = analyzerBottom - pixelHeight;
// Füge zum Path hinzu
if (!pathStarted) { if (!pathStarted) {
spectrumPath.startNewSubPath(x + analyzerRect.getX(), y); spectrumPath.startNewSubPath(x + analyzerRect.getX(), y);
pathStarted = true; pathStarted = true;
@ -925,7 +843,6 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
} }
} }
// Zeichne die Linie mit Anti-Aliasing
if (pathStarted) { if (pathStarted) {
juce::PathStrokeType stroke(2.0f); juce::PathStrokeType stroke(2.0f);
stroke.setJointStyle(juce::PathStrokeType::curved); // Runde Ecken stroke.setJointStyle(juce::PathStrokeType::curved); // Runde Ecken
@ -939,13 +856,11 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) {
g.fillPath(spectrumPath); g.fillPath(spectrumPath);
} }
// Optional: Zeichne Frequenz-Grid-Linien
drawFrequencyGrid(g, dbRange); drawFrequencyGrid(g, dbRange);
} }
float CrystalizerEQAudioProcessorEditor::mapFrequencyToX(float freq, float minFreq, float maxFreq, float width) { 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 logMin = std::log10(minFreq);
float logMax = std::log10(maxFreq); float logMax = std::log10(maxFreq);
float logFreq = std::log10(freq); float logFreq = std::log10(freq);
@ -954,6 +869,7 @@ float CrystalizerEQAudioProcessorEditor::mapFrequencyToX(float freq, float minFr
return normalized * width; return normalized * width;
} }
void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, const float dbRange) { void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, const float dbRange) {
g.setColour(Colours::FOREGROUNDCOLOUR.withAlpha(0.3f)); 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) { for (int i = 0; i < gridDB.size(); ++i) {
float db = gridDB[i]; float db = gridDB[i];
// 1: dB normalisieren (wie bei der Kurve)
float normalized = (db - minDB) / dbRange; float normalized = (db - minDB) / dbRange;
normalized = juce::jlimit(0.0f, 1.0f, normalized); normalized = juce::jlimit(0.0f, 1.0f, normalized);
// 2: Y-Position wie beim Spektrum berechnen
float pixelHeight = normalized * analyzerHeight; float pixelHeight = normalized * analyzerHeight;
float y = analyzerBottom - pixelHeight; float y = analyzerBottom - pixelHeight;
@ -997,7 +911,6 @@ void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics &g, con
static_cast<float>(analyzerRect.getY()), static_cast<float>(analyzerRect.getY()),
static_cast<float>(analyzerRect.getBottom())); static_cast<float>(analyzerRect.getBottom()));
// Optional: Frequenz-Label zeichnen
if (freq == 20) continue; if (freq == 20) continue;
g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f)); g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
juce::String label = (freq >= 1000) ? juce::String(freq / 1000) + "k" : juce::String(freq); 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) { void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) {
for (auto *b: lowBandModeButtons) { for (auto *b: lowBandModeButtons) {
@ -1028,7 +940,7 @@ void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) {
} }
} }
//==============================================================================
void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) { void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
g.fillAll(Colours::BACKGROUNDCOLOUR); g.fillAll(Colours::BACKGROUNDCOLOUR);
g.setColour(AXIOM::DesignSystem::Colours::FOREGROUNDCOLOUR); g.setColour(AXIOM::DesignSystem::Colours::FOREGROUNDCOLOUR);
@ -1051,7 +963,6 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
g.fillRect(hBX, hBY, hBW, mPY - hBPad); g.fillRect(hBX, hBY, hBW, mPY - hBPad);
g.setColour(Colours::SURFACECOLOUR); g.setColour(Colours::SURFACECOLOUR);
const auto pA = getLocalArea(&presetArea, presetArea.getLocalBounds()); const auto pA = getLocalArea(&presetArea, presetArea.getLocalBounds());
auto pAX = pA.getX(); auto pAX = pA.getX();
@ -1077,7 +988,6 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
g.fillRect(fABorder); g.fillRect(fABorder);
fABorder = fABorder.withX(fABorder.getX() - 2 * fABorderWidth).withY(fABorder.getY() - 2 * fABorderWidth); fABorder = fABorder.withX(fABorder.getX() - 2 * fABorderWidth).withY(fABorder.getY() - 2 * fABorderWidth);
//g.setColour(Colours::FOREGROUNDBYPASS);
g.fillRect(fABorder); g.fillRect(fABorder);
g.setColour(Colours::BACKGROUNDBYPASS); g.setColour(Colours::BACKGROUNDBYPASS);
@ -1095,10 +1005,9 @@ void CrystalizerEQAudioProcessorEditor::paint(juce::Graphics &g) {
logoDrawable->drawWithin(g, logoArea, juce::RectanglePlacement::xLeft, 1.0f); logoDrawable->drawWithin(g, logoArea, juce::RectanglePlacement::xLeft, 1.0f);
*/ */
} }
//region paintBorderLines
void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) { void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) {
g.setColour(DesignSystem::Colours::BACKGROUNDCOLOUR); g.setColour(DesignSystem::Colours::BACKGROUNDCOLOUR);
auto prevRight = (float) lowFilterArea.getRight(); auto prevRight = (float) lowFilterArea.getRight();
@ -1140,12 +1049,10 @@ void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) {
} }
} }
//endregion paintBorderLines
//region setKnobVisibility
void CrystalizerEQAudioProcessorEditor::setKnobVisibility() { void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
int lowMode = (int) audioProcessor.apvts int lowMode = (int) audioProcessor.apvts
.getRawParameterValue("LowBandModes")->load(); // 0..3 .getRawParameterValue("LowBandModes")->load();
const bool masterIsToggled = masterBypassButton.getToggleState(); const bool masterIsToggled = masterBypassButton.getToggleState();
const float target = masterIsToggled ? 1.0f : 0.0f; const float target = masterIsToggled ? 1.0f : 0.0f;
@ -1166,7 +1073,7 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
lowBandModeLabel.setEnabled(lowMode >= 1); lowBandModeLabel.setEnabled(lowMode >= 1);
int highMode = (int) audioProcessor.apvts int highMode = (int) audioProcessor.apvts
.getRawParameterValue("HighBandModes")->load(); // 0..3 .getRawParameterValue("HighBandModes")->load();
if (masterIsToggled) { if (masterIsToggled) {
highMode = 0; highMode = 0;
@ -1184,56 +1091,38 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
highBandModeLabel.setEnabled(highMode >= 1); highBandModeLabel.setEnabled(highMode >= 1);
} }
//endregion setKnobVisibility
void CrystalizerEQAudioProcessorEditor::updateFrequencyRanges() void CrystalizerEQAudioProcessorEditor::updateFrequencyRanges() {
{ int lowMode = (int) audioProcessor.apvts.getRawParameterValue("LowBandModes")->load();
// Low Band Range anpassen
int lowMode = (int)audioProcessor.apvts.getRawParameterValue("LowBandModes")->load();
if (lowMode == 3) // Bell Mode if (lowMode == 3) {
{
// Begrenzter Bereich für Low Bell
lowBandFreqSlider.setRange(20.0, 500.0, 1.0); lowBandFreqSlider.setRange(20.0, 500.0, 1.0);
// Aktuellen Wert prüfen und ggf. anpassen
float currentFreq = audioProcessor.apvts.getRawParameterValue("LowBandFreq")->load(); float currentFreq = audioProcessor.apvts.getRawParameterValue("LowBandFreq")->load();
if (currentFreq > 500.0f) if (currentFreq > 500.0f) {
{
lowBandFreqSlider.setValue(500.0, juce::sendNotificationSync); lowBandFreqSlider.setValue(500.0, juce::sendNotificationSync);
} }
} } else if (lowMode >= 1) {
else if (lowMode >= 1) // Cut oder Shelf Mode
{
// Volle Bandbreite
lowBandFreqSlider.setRange(20.0, 20000.0, 1.0); lowBandFreqSlider.setRange(20.0, 20000.0, 1.0);
} }
lowBandFreqSlider.repaint(); 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 if (highMode == 3) {
{
// Begrenzter Bereich für High Bell
highBandFreqSlider.setRange(8000.0, 20000.0, 1.0); highBandFreqSlider.setRange(8000.0, 20000.0, 1.0);
// Aktuellen Wert prüfen und ggf. anpassen
float currentFreq = audioProcessor.apvts.getRawParameterValue("HighBandFreq")->load(); float currentFreq = audioProcessor.apvts.getRawParameterValue("HighBandFreq")->load();
if (currentFreq < 8000.0f) if (currentFreq < 8000.0f) {
{
highBandFreqSlider.setValue(8000.0, juce::sendNotificationSync); highBandFreqSlider.setValue(8000.0, juce::sendNotificationSync);
} }
} } else if (highMode >= 1) {
else if (highMode >= 1) // Cut oder Shelf Mode
{
// Volle Bandbreite
highBandFreqSlider.setRange(20.0, 20000.0, 1.0); highBandFreqSlider.setRange(20.0, 20000.0, 1.0);
} }
highBandFreqSlider.repaint(); highBandFreqSlider.repaint();
} }
//region animateCrystalizeButton
void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() { void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() {
if (isAnimatingCrystalize) { if (isAnimatingCrystalize) {
const float step = 0.1f; const float step = 0.1f;
@ -1260,9 +1149,7 @@ void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() {
} }
} }
//endregion animateCrystalizeButton
//region timerCallback
void CrystalizerEQAudioProcessorEditor::timerCallback() { void CrystalizerEQAudioProcessorEditor::timerCallback() {
setKnobVisibility(); setKnobVisibility();
spectrumAnalyzer.processSamples(); spectrumAnalyzer.processSamples();
@ -1279,7 +1166,6 @@ void CrystalizerEQAudioProcessorEditor::timerCallback() {
} }
} }
//endregion timerCallback
void CrystalizerEQAudioProcessorEditor::resized() { void CrystalizerEQAudioProcessorEditor::resized() {
auto pluginArea = getLocalBounds(); auto pluginArea = getLocalBounds();
@ -1288,10 +1174,8 @@ void CrystalizerEQAudioProcessorEditor::resized() {
setupHeader(); setupHeader();
setupBody(); setupBody();
setupFooter(); setupFooter();
} }
//region resetAllCheckboxes
void CrystalizerEQAudioProcessorEditor::resetAllCheckboxes() { void CrystalizerEQAudioProcessorEditor::resetAllCheckboxes() {
const auto notify = juce::dontSendNotification; const auto notify = juce::dontSendNotification;
masterBypassButton.setToggleState(false, notify); masterBypassButton.setToggleState(false, notify);
@ -1311,9 +1195,7 @@ void CrystalizerEQAudioProcessorEditor::resetAllCheckboxes() {
highShelf.setToggleState(true, notify); highShelf.setToggleState(true, notify);
} }
//endregion resetAllCheckboxes
//region scalePluginWindow
void CrystalizerEQAudioProcessorEditor::scalePluginWindow(juce::Rectangle<int> area) { void CrystalizerEQAudioProcessorEditor::scalePluginWindow(juce::Rectangle<int> area) {
if (area.getWidth() < 520) Spacing::setUiScale(Spacing::uiScaleMode::XS); if (area.getWidth() < 520) Spacing::setUiScale(Spacing::uiScaleMode::XS);
else if (area.getWidth() < 720) Spacing::setUiScale(Spacing::uiScaleMode::S); 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); else Spacing::setUiScale(Spacing::uiScaleMode::XL);
} }
//endregion scalePluginWindow
//region setupMainGrid
void CrystalizerEQAudioProcessorEditor::setupMainGrid(juce::Rectangle<int> area) { void CrystalizerEQAudioProcessorEditor::setupMainGrid(juce::Rectangle<int> area) {
const float headerHeight = static_cast<float>(getHeight()) * 0.1f; const float headerHeight = static_cast<float>(getHeight()) * 0.1f;
const float footerHeight = static_cast<float>(getHeight()) * 0.1f; const float footerHeight = static_cast<float>(getHeight()) * 0.1f;
Layout::GridSpec spec{ 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)}, /* rows */ {Layout::pxTrack(headerHeight), Layout::fr(1), Layout::pxTrack(footerHeight)},
// Header / Body / Footer // Header / Body / Footer
/* colGap */ Spacing::SizeMode::S, /* colGap */ Spacing::SizeMode::S,
@ -1340,9 +1220,7 @@ void CrystalizerEQAudioProcessorEditor::setupMainGrid(juce::Rectangle<int> area)
Layout::grid(area, spec, {&headerBar, &mainPanel, &footerBar}); Layout::grid(area, spec, {&headerBar, &mainPanel, &footerBar});
} }
//endregion setupMainGrid
//region setupHeader
void CrystalizerEQAudioProcessorEditor::setupHeader() { void CrystalizerEQAudioProcessorEditor::setupHeader() {
const auto bounds = headerBar.getLocalBounds(); const auto bounds = headerBar.getLocalBounds();
@ -1393,12 +1271,9 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() {
/* pad */ Layout::padding(Spacing::SizeMode::S) /* pad */ Layout::padding(Spacing::SizeMode::S)
}; };
Layout::grid(presetAreaBounds, presetSpec, { Layout::grid(presetAreaBounds, presetSpec, {
// Label über beide Spalten (row1, col1..2)
Layout::area(presetBoxLabel, 1, 2, 2, 3), Layout::area(presetBoxLabel, 1, 2, 2, 3),
// Box unten links (row2, col1)
Layout::area(presetBox, 2, 2, 3, 3), Layout::area(presetBox, 2, 2, 3, 3),
Layout::area(resetButton, 2, 1, 2, 2), Layout::area(resetButton, 2, 1, 2, 2),
// Menütaste unten rechts (row2, col2)
Layout::area(presetMenuButton, 2, 3, 3, 4) Layout::area(presetMenuButton, 2, 3, 3, 4)
.withWidth(iconSize) .withWidth(iconSize)
.withHeight(iconSize) .withHeight(iconSize)
@ -1406,9 +1281,7 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() {
}); });
} }
//endregion setupHeader
//region setupBody
void CrystalizerEQAudioProcessorEditor::setupBody() { void CrystalizerEQAudioProcessorEditor::setupBody() {
const auto bounds = mainPanel.getLocalBounds(); const auto bounds = mainPanel.getLocalBounds();
@ -1432,22 +1305,17 @@ void CrystalizerEQAudioProcessorEditor::setupBody() {
Layout::area(analyzerArea, 1, 2, 2, 5) Layout::area(analyzerArea, 1, 2, 2, 5)
.withMargin(juce::GridItem::Margin(filterAreaMargin, 0, filterAreaMargin, 0)), .withMargin(juce::GridItem::Margin(filterAreaMargin, 0, filterAreaMargin, 0)),
Layout::area(crystalizeButton, 1, 5, 2, 6) Layout::area(crystalizeButton, 1, 5, 2, 6)
.withWidth(crystalizeIconSize) .withWidth(crystalizeIconSize)
.withHeight(crystalizeIconSize * 0.5f) .withHeight(crystalizeIconSize * 0.5f)
.withAlignSelf(juce::GridItem::AlignSelf::center) .withAlignSelf(juce::GridItem::AlignSelf::center)
.withJustifySelf(juce::GridItem::JustifySelf::center), .withJustifySelf(juce::GridItem::JustifySelf::center),
Layout::area(filterArea, 2, 1, 3, 6) Layout::area(filterArea, 2, 1, 3, 6)
.withMargin(juce::GridItem::Margin(0, filterAreaMargin, 0, filterAreaMargin)) .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 filterAreaBounds = filterArea.getLocalBounds();
const auto filterAreaWidth = static_cast<float>(filterArea.getWidth()); const auto filterAreaWidth = static_cast<float>(filterArea.getWidth());
const auto filterAreaHeight = static_cast<float>(filterArea.getHeight());
const auto filterColWidth = filterAreaWidth * 0.2f; const auto filterColWidth = filterAreaWidth * 0.2f;
@ -1478,9 +1346,7 @@ void CrystalizerEQAudioProcessorEditor::setupBody() {
setupHighBandLayout(); setupHighBandLayout();
} }
//endregion setupBody
//region setupLowBandLayout
void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() { void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
const auto bounds = lowFilterArea.getLocalBounds(); const auto bounds = lowFilterArea.getLocalBounds();
const auto areaWidth = static_cast<float>(bounds.getWidth()); const auto areaWidth = static_cast<float>(bounds.getWidth());
@ -1546,9 +1412,6 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
}); });
const auto modeBoxBounds = lowBandModeBox.getLocalBounds(); 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; const auto slopeH = lowBandSlopeSlider.getY() - lowBandModeBox.getY() + lowBandModeLabel.getFont().getHeight() / 2;
Layout::GridSpec lowBandModeBoxSpec{ Layout::GridSpec lowBandModeBoxSpec{
@ -1569,9 +1432,7 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
}); });
} }
//endregion setupLowBandLayout
//region setupLowMidBandLayout
void CrystalizerEQAudioProcessorEditor::setupLowMidBandLayout() { void CrystalizerEQAudioProcessorEditor::setupLowMidBandLayout() {
const auto bounds = lowMidFilterArea.getLocalBounds(); const auto bounds = lowMidFilterArea.getLocalBounds();
const auto areaWidth = static_cast<float>(bounds.getWidth()); const auto areaWidth = static_cast<float>(bounds.getWidth());
@ -1671,9 +1532,7 @@ void CrystalizerEQAudioProcessorEditor::setupMidBandLayout() {
}); });
} }
//endregion setupMidBandLayout
//region setupHighMidBandLayout
void CrystalizerEQAudioProcessorEditor::setupHighMidBandLayout() { void CrystalizerEQAudioProcessorEditor::setupHighMidBandLayout() {
const auto bounds = highMidFilterArea.getLocalBounds(); const auto bounds = highMidFilterArea.getLocalBounds();
const auto areaWidth = static_cast<float>(bounds.getWidth()); const auto areaWidth = static_cast<float>(bounds.getWidth());
@ -1721,9 +1580,7 @@ void CrystalizerEQAudioProcessorEditor::setupHighMidBandLayout() {
}); });
} }
//endregion setupHighMidBandLayout
//region setupHighBandLayout
void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() { void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
const auto bounds = highFilterArea.getLocalBounds(); const auto bounds = highFilterArea.getLocalBounds();
const auto areaWidth = static_cast<float>(bounds.getWidth()); const auto areaWidth = static_cast<float>(bounds.getWidth());
@ -1788,9 +1645,6 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
}); });
const auto modeBoxBounds = lowBandModeBox.getLocalBounds(); 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() / const auto slopeH = highBandSlopeSlider.getY() - highBandModeBox.getY() + highBandModeLabel.getFont().getHeight() /
2; 2;
@ -1812,15 +1666,10 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
}); });
} }
//endregion setupHighBandLayout
//region setupFooter
void CrystalizerEQAudioProcessorEditor::setupFooter() { void CrystalizerEQAudioProcessorEditor::setupFooter() {
const auto bounds = footerBar.getLocalBounds(); 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 refW = getReferenceCell()[0];
const auto refH = getReferenceCell()[1]; const auto refH = getReferenceCell()[1];
@ -1836,9 +1685,6 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
}); });
const auto globalControlAreaBounds = globalControlArea.getLocalBounds(); 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 sliderRadius = outputSlider.getWidth() / 2.0f;
const auto sliderWidth = outputSlider.getWidth(); const auto sliderWidth = outputSlider.getWidth();
@ -1874,9 +1720,7 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
}); });
} }
//endregion setupFooter
//region getReferenceCell
juce::Array<float> CrystalizerEQAudioProcessorEditor::getReferenceCell() { juce::Array<float> CrystalizerEQAudioProcessorEditor::getReferenceCell() {
const auto bounds = midFilterArea.getLocalBounds(); const auto bounds = midFilterArea.getLocalBounds();
const auto areaWidth = static_cast<float>(bounds.getWidth()); const auto areaWidth = static_cast<float>(bounds.getWidth());
@ -1890,18 +1734,14 @@ juce::Array<float> CrystalizerEQAudioProcessorEditor::getReferenceCell() {
return refCell; return refCell;
} }
//endregion getReferenceCell
//region processSamples
void SpectrumAnalyzer::processSamples() { void SpectrumAnalyzer::processSamples() {
auto samples = audioFIFO.sendSamplesToEditor(); auto samples = audioFIFO.sendSamplesToEditor();
getFftFrame(samples); getFftFrame(samples);
} }
//endregion processSamples
//region getFftFrame
void SpectrumAnalyzer::getFftFrame(juce::Array<float> &samples) { void SpectrumAnalyzer::getFftFrame(juce::Array<float> &samples) {
const int needed = FFTSIZE - fftFrame.size(); const int needed = FFTSIZE - fftFrame.size();
if (needed <= 0) 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) { void SpectrumAnalyzer::applyWindowOnFftFrame(std::vector<float> &fullFrame) {
if (fullFrame.size() != FFTSIZE) return; if (fullFrame.size() != FFTSIZE) return;
window.multiplyWithWindowingTable(fullFrame.data(), FFTSIZE); window.multiplyWithWindowingTable(fullFrame.data(), FFTSIZE);
processWindowedFrame(fullFrame); processWindowedFrame(fullFrame);
} }
//endregion applyWindowOnFftFrame
//region processWindowedFrame
void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &windowedFrame) { void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &windowedFrame) {
if (windowedFrame.size() != FFTSIZE || fftData.size() != FFTSIZE * 2) return; if (windowedFrame.size() != FFTSIZE || fftData.size() != FFTSIZE * 2) return;
fillFftDataFromFrame(windowedFrame); fillFftDataFromFrame(windowedFrame);
@ -1947,11 +1783,7 @@ void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &windowedFrame) {
renderValuesDb = getRenderValues(); renderValuesDb = getRenderValues();
} }
//endregion processWindowedFrame
//region fillFftDataFromFrame
void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &windowedFrame) { void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &windowedFrame) {
for (int n = 0; n < FFTSIZE; ++n) { for (int n = 0; n < FFTSIZE; ++n) {
fftData[n] = windowedFrame[n]; fftData[n] = windowedFrame[n];
@ -1961,9 +1793,7 @@ void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &windowedFrame) {
} }
} }
//endregion fillFftDataFromFrame
//region buildMagnitudeSpectrum
void SpectrumAnalyzer::buildMagnitudeSpectrum() { void SpectrumAnalyzer::buildMagnitudeSpectrum() {
for (int k = 0; k < BINS; ++k) { for (int k = 0; k < BINS; ++k) {
float mag = fftData[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() { void SpectrumAnalyzer::convertToDb() {
for (int k = 0; k < magnitudes.size(); ++k) { for (int k = 0; k < magnitudes.size(); ++k) {
float mag = std::max(magnitudes[k], 1e-9f); float mag = std::max(magnitudes[k], 1e-9f);
float dB = juce::Decibels::gainToDecibels(mag); 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) { if (dB < -60.0f) {
dB = MINDB; dB = MINDB;
} }
@ -1991,18 +1833,14 @@ void SpectrumAnalyzer::convertToDb() {
} }
} }
//endregion convertToDb
//region applySmoothing
void SpectrumAnalyzer::applySmoothing() { void SpectrumAnalyzer::applySmoothing() {
applyEMA(); applyEMA();
applyFreqSmoothing(); applyFreqSmoothing();
applyPeakHoldAndFalloff(); applyPeakHoldAndFalloff();
} }
//endregion applySmoothing
//region applyEMA
void SpectrumAnalyzer::applyEMA() { void SpectrumAnalyzer::applyEMA() {
for (int k = 0; k < magnitudesDb.size(); ++k) { for (int k = 0; k < magnitudesDb.size(); ++k) {
float smoothedVal = smoothingFactor * magnitudesDb[k] + (1 - smoothingFactor) * emaSmoothedMagnitudesDb[k]; float smoothedVal = smoothingFactor * magnitudesDb[k] + (1 - smoothingFactor) * emaSmoothedMagnitudesDb[k];
@ -2010,9 +1848,7 @@ void SpectrumAnalyzer::applyEMA() {
} }
} }
//endregion applyEMA
//region applyFreqSmoothing
void SpectrumAnalyzer::applyFreqSmoothing() { void SpectrumAnalyzer::applyFreqSmoothing() {
for (int k = 0; k < BINS; ++k) { for (int k = 0; k < BINS; ++k) {
double freq = k * deltaF; double freq = k * deltaF;
@ -2043,9 +1879,7 @@ void SpectrumAnalyzer::applyFreqSmoothing() {
} }
} }
//endregion applyFreqSmoothing
//region applyPeakHoldAndFalloff
void SpectrumAnalyzer::applyPeakHoldAndFalloff() { void SpectrumAnalyzer::applyPeakHoldAndFalloff() {
std::vector<float> prevPeak = peakHoldMagnitudesDb; std::vector<float> prevPeak = peakHoldMagnitudesDb;
for (int k = 0; k < BINS; ++k) { for (int k = 0; k < BINS; ++k) {
@ -2053,17 +1887,12 @@ void SpectrumAnalyzer::applyPeakHoldAndFalloff() {
float prev = prevPeak[k]; float prev = prevPeak[k];
peakHoldMagnitudesDb[k] = std::max(current, prev - FALLOFFRATE * DELTAT); peakHoldMagnitudesDb[k] = std::max(current, prev - FALLOFFRATE * DELTAT);
} }
} }
//endregion applyPeakHoldAndFalloff
//region getRenderValues
std::vector<float> SpectrumAnalyzer::getRenderValues() { std::vector<float> SpectrumAnalyzer::getRenderValues() {
std::vector<float> renderValues = peakHoldMagnitudesDb; std::vector<float> renderValues = peakHoldMagnitudesDb;
return renderValues; return renderValues;
} }
//endregion getRenderValues

View File

@ -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: public:
CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &); CrystalizerEQAudioProcessorEditor(CrystalizerEQAudioProcessor &);
@ -184,6 +184,10 @@ public:
highBandFreqAttach, highBandSlopeAttach, highBandGainAttach, highBandQAttach, highBandFreqAttach, highBandSlopeAttach, highBandGainAttach, highBandQAttach,
inputAttach, outputAttach; inputAttach, outputAttach;
std::unique_ptr<ButtonAttach>
peak1BypassAttach, peak2BypassAttach, peak3BypassAttach,
crystalizeAttach, masterBypassAttach;
juce::Label titleLabel, juce::Label titleLabel,
lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel, lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel,
low12, low24, low36, low48, lowdBOctLabel, low12, low24, low36, low48, lowdBOctLabel,
@ -233,8 +237,6 @@ public:
DesignSystem::ClickableTextEditor presetNameInput; DesignSystem::ClickableTextEditor presetNameInput;
private: private:
// This reference is provided as a quick way for your editor to // This reference is provided as a quick way for your editor to
// access the processor object that created it. // access the processor object that created it.
@ -378,5 +380,7 @@ private:
float getInterpolatedDb(float exactBin); float getInterpolatedDb(float exactBin);
void parameterChanged(const juce::String& parameterID, float newValue) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CrystalizerEQAudioProcessorEditor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CrystalizerEQAudioProcessorEditor)
}; };

File diff suppressed because it is too large Load Diff

View File

@ -18,66 +18,81 @@
class AudioFIFO { class AudioFIFO {
public: public:
AudioFIFO(); AudioFIFO();
~AudioFIFO(); ~AudioFIFO();
juce::Array<float> sampleStack; juce::Array<float> sampleStack;
void loadSamplesToFIFO(const juce::AudioBuffer<float> &samples); void loadSamplesToFIFO(const juce::AudioBuffer<float> &samples);
juce::Array<float> sendSamplesToEditor(); juce::Array<float> sendSamplesToEditor();
juce::SpinLock lock;
juce::SpinLock lock;
private: private:
}; };
class CrystalizerEQAudioProcessor : public juce::AudioProcessor , public juce::AudioProcessorValueTreeState::Listener class CrystalizerEQAudioProcessor : public juce::AudioProcessor, public juce::AudioProcessorValueTreeState::Listener {
{
public: public:
//============================================================================== //==============================================================================
CrystalizerEQAudioProcessor(); CrystalizerEQAudioProcessor();
~CrystalizerEQAudioProcessor() override; ~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; void releaseResources() override;
#ifndef JucePlugin_PreferredChannelConfigurations #ifndef JucePlugin_PreferredChannelConfigurations
bool isBusesLayoutSupported (const BusesLayout& layouts) const override; bool isBusesLayoutSupported(const BusesLayout &layouts) const override;
#endif #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; bool hasEditor() const override;
//============================================================================== //==============================================================================
const juce::String getName() const override; const juce::String getName() const override;
bool acceptsMidi() const override; bool acceptsMidi() const override;
bool producesMidi() const override; bool producesMidi() const override;
bool isMidiEffect() const override; bool isMidiEffect() const override;
double getTailLengthSeconds() const override; double getTailLengthSeconds() const override;
//============================================================================== //==============================================================================
int getNumPrograms() override; int getNumPrograms() override;
int getCurrentProgram() override; int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override; void setCurrentProgram(int index) override;
void changeProgramName (int index, const juce::String& newName) override;
const juce::String getProgramName(int index) override;
void changeProgramName(int index, const juce::String &newName) override;
//============================================================================== //==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override; void getStateInformation(juce::MemoryBlock &destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
void setStateInformation(const void *data, int sizeInBytes) override;
// --------- Parameter (APVTS) ---------- // --------- Parameter (APVTS) ----------
static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); 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::String getPresetName() const noexcept { return presetName; }
juce::StringArray getPresetNamesArray() const; juce::StringArray getPresetNamesArray() const;
@ -95,37 +110,34 @@ public:
AudioFIFO audioFIFO; AudioFIFO audioFIFO;
private: private:
// --------- EQ-Kette ----------
using Gain = juce::dsp::Gain<float>; using Gain = juce::dsp::Gain<float>;
using Filter = juce::dsp::IIR::Filter<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; juce::Random noiseRandL, noiseRandR;
Chain leftChain, rightChain; 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>; using Coeff = juce::dsp::IIR::Coefficients<float>;
juce::dsp::LinkwitzRileyFilter<float> mbLowpass;
juce::dsp::LinkwitzRileyFilter<float> mbHighpass; juce::dsp::IIR::Filter<float> mbHighPeakL;
juce::dsp::IIR::Filter<float> mbHighPeak; juce::dsp::IIR::Filter<float> mbHighPeakR;
juce::dsp::WaveShaper<float> saturator;
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)
}; };