diff --git a/CrystalizerEQ/AXIOMDesignSystem.h b/CrystalizerEQ/AXIOMDesignSystem.h index e6823e8..f8eaa81 100644 --- a/CrystalizerEQ/AXIOMDesignSystem.h +++ b/CrystalizerEQ/AXIOMDesignSystem.h @@ -123,11 +123,11 @@ namespace AXIOM { float snappedUnits; switch (mode) { case SnapMode::Nearest: snappedUnits = std::round(units); - break; + break; case SnapMode::Floor: snappedUnits = std::floor(units + 1e-6f); - break; + break; case SnapMode::Ceiling: snappedUnits = std::ceil(units - 1e-6f); - break; + break; } return snappedUnits * grid; }; @@ -142,15 +142,15 @@ namespace AXIOM { static void setUiScale(uiScaleMode mode) noexcept { switch (mode) { case uiScaleMode::XS: scale.uiScale = 0.5f; - break; + break; case uiScaleMode::S: scale.uiScale = 0.75f; - break; + break; case uiScaleMode::M: scale.uiScale = 1.0f; - break; + break; case uiScaleMode::L: scale.uiScale = 1.5f; - break; + break; case uiScaleMode::XL: scale.uiScale = 1.75f; - break; + break; } scale.uiScale = juce::jlimit(0.3f, 3.0f, scale.uiScale); } @@ -585,7 +585,7 @@ namespace AXIOM { std::unique_ptr svgXml; }; -class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { + class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { public: PresetMenuButtonLookAndFeel() { @@ -605,10 +605,7 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { auto hoverFactor = isHovered ? 1.05f : 1.0f; auto iconBounds = juce::Rectangle(iconSize * hoverFactor, iconSize * hoverFactor) - .withCentre(bounds.getCentre()) - .withX(bounds.getX()); -// ganz links im Button starten -; + .withCentre(bounds.getCentre()); float bypassOpacity = 1.0f; @@ -637,7 +634,7 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { }; class SvgToggleButtonLookAndFeel : public juce::LookAndFeel_V4 { - public: + public: SvgToggleButtonLookAndFeel() { passiveSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_passive_icon_svg); activeSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_active_icon_svg); @@ -666,22 +663,22 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { if (passiveIcon != nullptr && activeIcon != nullptr) { - activeIcon->drawWithin(g, iconBounds, - juce::RectanglePlacement::centred, activeIconOpacity); - passiveIcon->drawWithin(g, iconBounds, - juce::RectanglePlacement::centred, passiveIconOpacity); + activeIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, activeIconOpacity); + passiveIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, passiveIconOpacity); } } - private: + private: std::unique_ptr passiveSvg; std::unique_ptr activeSvg; }; class lowBandButtonLookAndFeel : public juce::LookAndFeel_V4 { - public: + public: lowBandButtonLookAndFeel() { lowBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg); @@ -755,10 +752,6 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { juce::RectanglePlacement::centred, bypassOpacity); } - - - - } } @@ -772,7 +765,7 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { }; class highBandButtonLookAndFeel : public juce::LookAndFeel_V4 { - public: + public: highBandButtonLookAndFeel() { highBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg); @@ -926,105 +919,105 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { } }; class GainSliderLookAndFeel : public BaseSliderLookAndFeel - { - public: - void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, - float sliderPos, float rotaryStartAngle, float rotaryEndAngle, - juce::Slider& slider) override - { - bool isHovered = slider.isMouseOverOrDragging(); - auto isEnabled = slider.isEnabled(); - float hoverFactor = 1.0f; - auto surfaceCol = Colours::SURFACECOLOUR; - auto accentCol = Colours::ACCENTCOLOUR; - if (isEnabled) { - surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR; - accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; - hoverFactor = isHovered ? 1.05f : 1.0f; - } else { - surfaceCol = Colours::SURFACEBYPASS; - accentCol = Colours::ACCENTWEAKCOLOUR; - } + { + public: + void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, + float sliderPos, float rotaryStartAngle, float rotaryEndAngle, + juce::Slider& slider) override + { + bool isHovered = slider.isMouseOverOrDragging(); + auto isEnabled = slider.isEnabled(); + float hoverFactor = 1.0f; + auto surfaceCol = Colours::SURFACECOLOUR; + auto accentCol = Colours::ACCENTCOLOUR; + if (isEnabled) { + surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR; + accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; + hoverFactor = isHovered ? 1.05f : 1.0f; + } else { + surfaceCol = Colours::SURFACEBYPASS; + accentCol = Colours::ACCENTWEAKCOLOUR; + } - auto radius = (juce::jmin(width / 2, height / 2) - 4.0f); // * hoverFactor; - auto centreX = x + width * 0.5f; - auto centreY = y + height * 0.5f; - auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); + auto radius = (juce::jmin(width / 2, height / 2) - 4.0f); // * hoverFactor; + auto centreX = x + width * 0.5f; + auto centreY = y + height * 0.5f; + auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); - auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle; - const float arcPathWidth = 3.0f; + 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); + // Background + g.setColour(Colours::BACKGROUNDCOLOUR); + g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f); - g.setColour(surfaceCol); - g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth); + g.setColour(surfaceCol); + 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); + // Arc für Gain-Anzeige + juce::Path valueArc; + valueArc.addCentredArc(centreX, centreY, radius, radius, + 0.0f, mid, angle, true); - g.setColour(accentCol); - g.strokePath(valueArc, juce::PathStrokeType(arcPathWidth)); + 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); - p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); - g.fillPath(p); + // Pointer + juce::Path p; + p.addRectangle(-arcPathWidth / 2, (-radius - arcPathWidth) * hoverFactor, arcPathWidth, radius * 0.5f * hoverFactor); + p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); + g.fillPath(p); - } - }; + } + }; class FreqQSliderLookAndFeel : public BaseSliderLookAndFeel - { - public: - void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, - float sliderPos, float rotaryStartAngle, float rotaryEndAngle, - juce::Slider& slider) override - { - 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); - const float arcPathWidth = 2.0f; - bool isHovered = slider.isMouseOverOrDragging(); - bool isEnabled = slider.isEnabled(); - float hoverFactor = 1.0f; - auto surfaceCol = Colours::SURFACECOLOUR; - auto accentCol = Colours::ACCENTCOLOUR; - if (isEnabled) { - surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR; - accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; - hoverFactor = isHovered ? 1.05f : 1.0f; - } else { - surfaceCol = Colours::SURFACEBYPASS; - accentCol = Colours::ACCENTWEAKCOLOUR; - } + { + public: + void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, + float sliderPos, float rotaryStartAngle, float rotaryEndAngle, + juce::Slider& slider) override + { + 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); + const float arcPathWidth = 2.0f; + bool isHovered = slider.isMouseOverOrDragging(); + bool isEnabled = slider.isEnabled(); + float hoverFactor = 1.0f; + auto surfaceCol = Colours::SURFACECOLOUR; + auto accentCol = Colours::ACCENTCOLOUR; + if (isEnabled) { + surfaceCol = isHovered ? Colours::SURFACEBYPASS : Colours::SURFACECOLOUR; + accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; + hoverFactor = isHovered ? 1.05f : 1.0f; + } else { + surfaceCol = Colours::SURFACEBYPASS; + accentCol = Colours::ACCENTWEAKCOLOUR; + } - // Einfacher Ring - g.setColour(surfaceCol); - g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f); + // Einfacher Ring + g.setColour(surfaceCol); + g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f); - juce::Path valueArc; - valueArc.addCentredArc(centreX, centreY, radius, radius, - 0.0f, rotaryStartAngle, angle, true); + juce::Path valueArc; + valueArc.addCentredArc(centreX, centreY, radius, radius, + 0.0f, rotaryStartAngle, angle, true); - 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); - p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); + 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); + p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); - g.setColour(accentCol); - g.fillPath(p); - } - }; + g.setColour(accentCol); + g.fillPath(p); + } + }; class SlopeSliderLookAndFeel : public BaseSliderLookAndFeel { public: @@ -1124,58 +1117,58 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { }; class GlobalSliderLookAndFeel : public BaseSliderLookAndFeel - { - public: - void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, - float sliderPos, float rotaryStartAngle, float rotaryEndAngle, - juce::Slider& slider) override - { - 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); - const float arcPathWidth = 2.0f; - auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle; - bool isHovered = slider.isMouseOverOrDragging(); - bool isEnabled = slider.isEnabled(); - float hoverFactor = 1.0f; - auto surfaceCol = Colours::SURFACECOLOUR; - auto accentCol = Colours::ACCENTCOLOUR; - auto ringCol = Colours::SURFACEHOVER; - if (isEnabled) { - surfaceCol = isHovered ? Colours::SURFACEHOVER : Colours::SURFACECOLOUR; - accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; - ringCol = isHovered ? Colours::SURFACECOLOUR : Colours::SURFACEHOVER; - hoverFactor = isHovered ? 1.05f : 1.0f; - } else { - surfaceCol = Colours::SURFACEBYPASS; - accentCol = Colours::ACCENTWEAKCOLOUR; - ringCol = Colours::SURFACEHOVER; - } + { + public: + void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, + float sliderPos, float rotaryStartAngle, float rotaryEndAngle, + juce::Slider& slider) override + { + 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); + const float arcPathWidth = 2.0f; + auto mid = (rotaryEndAngle - rotaryStartAngle) / 2 + rotaryStartAngle; + bool isHovered = slider.isMouseOverOrDragging(); + bool isEnabled = slider.isEnabled(); + float hoverFactor = 1.0f; + auto surfaceCol = Colours::SURFACECOLOUR; + auto accentCol = Colours::ACCENTCOLOUR; + auto ringCol = Colours::SURFACEHOVER; + if (isEnabled) { + surfaceCol = isHovered ? Colours::SURFACEHOVER : Colours::SURFACECOLOUR; + accentCol = isHovered ? Colours::ACCENTHOVER : Colours::ACCENTCOLOUR; + ringCol = isHovered ? Colours::SURFACECOLOUR : Colours::SURFACEHOVER; + hoverFactor = isHovered ? 1.05f : 1.0f; + } else { + surfaceCol = Colours::SURFACEBYPASS; + accentCol = Colours::ACCENTWEAKCOLOUR; + ringCol = Colours::SURFACEHOVER; + } - // Einfacher Ring - g.setColour(surfaceCol); - g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f); + // Einfacher Ring + g.setColour(surfaceCol); + g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f); - g.setColour(ringCol); - g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth); + g.setColour(ringCol); + g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth); - juce::Path valueArc; - valueArc.addCentredArc(centreX, centreY, radius, radius, - 0.0f, mid, angle, true); + juce::Path valueArc; + valueArc.addCentredArc(centreX, centreY, radius, radius, + 0.0f, mid, angle, true); - 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); - p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); + 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); + p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY)); - g.setColour(accentCol); - g.fillPath(p); - } - }; + g.setColour(accentCol); + g.fillPath(p); + } + }; struct SliderStyles { struct Size { @@ -1241,17 +1234,17 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { float basisPx = -1.f, Spacing::SizeMode margin = Spacing::SizeMode::XS) { - juce::FlexItem item{ component }; - item.flexGrow = flexGrow; - item.flexShrink = flexShrink; + juce::FlexItem item{ component }; + item.flexGrow = flexGrow; + item.flexShrink = flexShrink; - if (basisPx >= 0.f) - item.flexBasis = basisPx; + if (basisPx >= 0.f) + item.flexBasis = basisPx; - const float half = (float) gap(margin) * 0.5f; - item.margin = juce::FlexItem::Margin{ half, half, half, half }; + const float half = (float) gap(margin) * 0.5f; + item.margin = juce::FlexItem::Margin{ half, half, half, half }; - return item; + return item; } static void flexRow(juce::Rectangle bounds, @@ -1346,6 +1339,118 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { } }; + class PresetMenu : public juce::Component { + public: + PresetMenu(juce::TextEditor* inputField, + juce::TextButton* saveButton, + juce::TextButton* deleteButton) { + addAndMakeVisible(*inputField); + addAndMakeVisible(*saveButton); + addAndMakeVisible(*deleteButton); + } + + }; + class PresetMenuLookAndFeel : public juce::LookAndFeel_V4 { + public: + void drawCallOutBoxBackground(juce::CallOutBox &callOutBox, juce::Graphics &g, const juce::Path &path, juce::Image &image) override { + g.setColour(Colours::SURFACECOLOUR); + g.fillPath(path); + + g.setColour (Colours::FOREGROUNDCOLOUR.withAlpha(0.3f)); + g.strokePath (path, juce::PathStrokeType (1.0f)); + }; + + + int getCallOutBoxBorderSize (const juce::CallOutBox&) override + { + return 4; + } + }; + + class PresetButtonsLookAndFeel : public juce::LookAndFeel_V4 { + public: + + void drawButtonText(juce::Graphics &g, juce::TextButton &button, bool shouldDrawButtonAsHighlighted, 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; + + if (!isEnabled) colour = colour.withMultipliedAlpha(0.6f); + + + 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, + 1); + } + void drawButtonBackground(juce::Graphics& g, juce::Button& button, + const juce::Colour& backgroundColour, + bool shouldDrawButtonAsHighlighted, + bool shouldDrawButtonAsDown) override + { + auto bounds = button.getLocalBounds().toFloat(); + + bool isHovered = button.isMouseOver(); + bool isEnabled = button.isEnabled(); + + auto colour = isHovered ? Colours::BACKGROUNDHOVER : Colours::BACKGROUNDCOLOUR; + if (!isEnabled) colour = colour.withMultipliedAlpha(0.6f); + + g.setColour(colour); + g.fillRoundedRectangle(bounds.reduced(1.0f), 4.0f); + } + }; + + class PresetComboBoxLookAndFeel : public juce::LookAndFeel_V4 { + public: + juce::PopupMenu::Options getOptionsForComboBoxPopupMenu(juce::ComboBox& box, + juce::Label& label) override + { + // Basis-Options holen + auto options = juce::LookAndFeel_V4::getOptionsForComboBoxPopupMenu(box, label); + + // Anzahl der sichtbaren Einträge + const int visibleRows = 5; + + // Höhe einer Zeile + auto font = getComboBoxFont(box); + int rowHeight = (int)std::ceil(font.getHeight() * 1.6f); + + // Maximale Höhe des Popups + int maxPopupHeight = visibleRows * rowHeight; + + // Screen-Bounds der ComboBox + auto boxScreen = box.getScreenBounds(); + + // Wichtig: Ein sehr breiter aber in der Höhe begrenzter Bereich + // JUCE wird das Popup darin zentrieren + juce::Rectangle constrainedArea( + 0, // X: ganze Screen-Breite + boxScreen.getBottom(), // Y: direkt unter der Box + juce::Desktop::getInstance().getDisplays() + .getPrimaryDisplay()->totalArea.getWidth(), + maxPopupHeight // Höhe: nur unsere gewünschten Zeilen + ); + + options = options.withTargetScreenArea(constrainedArea) + .withStandardItemHeight(rowHeight); + + return options; + } + }; diff --git a/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp b/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp index ae2aa71..1793c5d 100644 --- a/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp +++ b/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp @@ -11451,8 +11451,8 @@ const char* bell_icon_svg = (const char*) temp_binary_data_8; //================== high_cut_icon.svg ================== static const unsigned char temp_binary_data_9[] = "\n" -"\n" -" \n" +"\n" +" \n" ""; const char* high_cut_icon_svg = (const char*) temp_binary_data_9; @@ -11470,8 +11470,8 @@ const char* high_shelf_icon_svg = (const char*) temp_binary_data_10; //================== low_cut_icon.svg ================== static const unsigned char temp_binary_data_11[] = "\n" -"\n" -" \n" +"\n" +" \n" ""; const char* low_cut_icon_svg = (const char*) temp_binary_data_11; @@ -11687,9 +11687,9 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) case 0x93fe9a1e: numBytes = 146004; return RobotoRegular_ttf; case 0xa297e1b2: numBytes = 1793; return logo_icon_svg; case 0x1024555a: numBytes = 397; return bell_icon_svg; - case 0x46c5a278: numBytes = 316; return high_cut_icon_svg; + case 0x46c5a278: numBytes = 355; return high_cut_icon_svg; case 0x0133c050: numBytes = 420; return high_shelf_icon_svg; - case 0x31532e06: numBytes = 317; return low_cut_icon_svg; + case 0x31532e06: numBytes = 353; return low_cut_icon_svg; case 0x7e8ca05e: numBytes = 410; return low_shelf_icon_svg; case 0x4df4bf1e: numBytes = 350; return preset_menu_icon_svg; case 0xd842aaab: numBytes = 6343; return crystalize_button_active_icon_svg; diff --git a/CrystalizerEQ/JuceLibraryCode/BinaryData.h b/CrystalizerEQ/JuceLibraryCode/BinaryData.h index 1470c75..0afdacd 100644 --- a/CrystalizerEQ/JuceLibraryCode/BinaryData.h +++ b/CrystalizerEQ/JuceLibraryCode/BinaryData.h @@ -36,13 +36,13 @@ namespace BinaryData const int bell_icon_svgSize = 397; extern const char* high_cut_icon_svg; - const int high_cut_icon_svgSize = 316; + const int high_cut_icon_svgSize = 355; extern const char* high_shelf_icon_svg; const int high_shelf_icon_svgSize = 420; extern const char* low_cut_icon_svg; - const int low_cut_icon_svgSize = 317; + const int low_cut_icon_svgSize = 353; extern const char* low_shelf_icon_svg; const int low_shelf_icon_svgSize = 410; diff --git a/CrystalizerEQ/PluginEditor.cpp b/CrystalizerEQ/PluginEditor.cpp index 31fa6ee..47cf609 100644 --- a/CrystalizerEQ/PluginEditor.cpp +++ b/CrystalizerEQ/PluginEditor.cpp @@ -332,17 +332,30 @@ void CrystalizerEQAudioProcessorEditor::disableHighBand(const float target) { //region disableEverything void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) { bool isToggled = target <= 0.5f; + for (auto* s : sliders) { s->setEnabled(isToggled); } for (auto* l : sliderLabels) { l->setEnabled(isToggled); } - //TODO: If band bypass is active prior to master bypass, upon reactivating master the bypassed band is getting active. - peak1BypassButton.setEnabled(isToggled); - peak2BypassButton.setEnabled(isToggled); - peak3BypassButton.setEnabled(isToggled); + + for (auto*b : peakBypassButtons) { + b->setEnabled(isToggled); + const auto bypassTarget = b->getToggleState() ? 1.0f : 0.0f; + if (b == &peak1BypassButton && isToggled) { + disableLowMidBand(bypassTarget); + } + else if (b == &peak2BypassButton && isToggled) { + disableMidBand(bypassTarget); + } + else if (b == &peak3BypassButton && isToggled) { + disableHighMidBand(bypassTarget); + } + } + resetButton.setEnabled(isToggled); + crystalizeButton.setEnabled(isToggled); lowBandModeBox.setEnabled(isToggled); highBandModeBox.setEnabled(isToggled); @@ -382,11 +395,7 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() { presetArea.addAndMakeVisible(presetMenuButton); headerBar.addAndMakeVisible(presetMenu); - presetMenu.addAndMakeVisible(presetNameInput); - presetMenu.addAndMakeVisible(savePresetButton); - presetMenu.addAndMakeVisible(deletePresetButton); - presetMenu.toFront(true); - presetMenu.setVisible(false); + addAndMakeVisible(mainPanel); @@ -406,8 +415,8 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() { lowBandModeBox.addAndMakeVisible(lowBell); lowBandModeBox.addAndMakeVisible(lowShelf); lowFilterArea.addAndMakeVisible(lowBandModeBox); - lowBandModeButtons.add(&lowBypass, &lowCut, &lowBell, &lowShelf); - lowBandBools = {false, false, false, true}; + lowBandModeButtons.add(&lowBypass, &lowCut, &lowShelf, &lowBell); + lowBandBools = {false, false, true, false}; lowFilterArea.addAndMakeVisible(lowBandSlopeLabel); @@ -470,8 +479,8 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() { highBandModeBox.addAndMakeVisible(highBell); highBandModeBox.addAndMakeVisible(highShelf); highFilterArea.addAndMakeVisible(highBandModeBox); - highBandModeButtons.add(&highBypass, &highCut, &highBell, &highShelf); - highBandBools = {false, false, false, true}; + highBandModeButtons.add(&highBypass, &highCut, &highShelf, &highBell); + highBandBools = {false, false, true, false}; highFilterArea.addAndMakeVisible(highBandSlopeLabel); highFilterArea.addAndMakeVisible(highBandGainLabel); @@ -494,8 +503,6 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() { } - - globalControlArea.addAndMakeVisible(inputSlider); globalControlArea.addAndMakeVisible(outputSlider); globalControlArea.addAndMakeVisible(masterBypassButton); @@ -526,7 +533,7 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() { setupLabel(low24, "24"); setupLabel(low36, "36"); setupLabel(low48, "48"); - setupLabel(lowdBOctLabel, "db/Oct"); + setupLabel(lowdBOctLabel, "dB/Oct"); //PEAK 1 setupLabel (peak1FreqLabel,"Low-Mid\nHz"); @@ -553,7 +560,7 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() { setupLabel(high24, "24"); setupLabel(high36, "36"); setupLabel(high48, "48"); - setupLabel(highdBOctLabel, "db/Oct"); + setupLabel(highdBOctLabel, "dB/Oct"); setupLabel(presetBoxLabel, "Presets"); @@ -568,6 +575,9 @@ void CrystalizerEQAudioProcessorEditor::setupFontsWithColours() { Typography::applyToLabel(titleLabel, Typography::Style::Display, 1.f); titleLabel.setColour(juce::Label::textColourId, Colours::ACCENTCOLOUR); + Typography::applyToLabel(presetBoxLabel, Typography::Style::H3, 1.f); + presetBoxLabel.setColour(juce::Label::textColourId, Colours::FOREGROUNDCOLOUR); + for (auto* l : sliderLabels) { l->setColour(juce::Label::textColourId, Colours::FOREGROUNDCOLOUR); Typography::applyToLabel(*l, Typography::Style::Mono, 1.f); @@ -606,7 +616,6 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() { lowBandModeButtons[j]->setToggleState(false, juce::dontSendNotification); } } - // Aktuellen aktivieren lowBandBools[i] = true; param->setValueNotifyingHost(param->convertTo0to1((float)i)); @@ -660,8 +669,26 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() { //region setupEventListeners void CrystalizerEQAudioProcessorEditor::setupEventListeners() { presetMenuButton.onClick = [this]() { - bool menuOpened = presetMenuButton.getToggleState(); - presetMenu.setVisible(menuOpened); + + auto* menu = new DesignSystem::PresetMenu(&presetNameInput, &savePresetButton, &deletePresetButton); + const auto presetMenuBounds = presetMenu.getLocalBounds(); + menu->setBounds(presetMenuBounds); + + presetMenuLookAndFeel = std::make_unique(); + + juce::Rectangle areaToPointTo = presetMenuButton.getScreenBounds(); + + auto* menuRaw = menu; + presetMenuSafePtr = menuRaw; + + auto& box = juce::CallOutBox::launchAsynchronously( + std::unique_ptr(menu), + areaToPointTo, + nullptr + ); + box.setLookAndFeel(presetMenuLookAndFeel.get()); + + }; savePresetButton.onClick = [this]() { @@ -716,8 +743,21 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() { resetButton.onClick = [this]() { audioProcessor.resetAllParameters(); + if (crystalizeButton.getToggleState()) { + isAnimatingCrystalize = true; + isFadingToActive = (svgToggleButtonLookAndFeel->activeIconOpacity < 1.0f); + } resetAllCheckboxes(); + for (auto* s : sliders) { + s->setEnabled(true); + } + for (auto* l : sliderLabels) { + l->setEnabled(true); + } presetBox.setSelectedId(1, juce::dontSendNotification); + + + }; handleLowBandModes(); @@ -735,6 +775,7 @@ void CrystalizerEQAudioProcessorEditor::initPresetSystem() { presetBox.addItemList(presets, 1); presetBox.setSelectedId(1, juce::dontSendNotification); + presetMenuButton.setName("PresetMenuButton"); presetMenuButton.setColour (juce::ToggleButton::textColourId, juce::Colours::black); presetMenuButton.setColour(juce::ToggleButton::tickColourId, juce::Colours::black); @@ -744,6 +785,14 @@ void CrystalizerEQAudioProcessorEditor::initPresetSystem() { presetNameInput.setJustification(juce::Justification::centred); presetNameInput.setColour(juce::TextEditor::backgroundColourId, juce::Colours::black); presetNameInput.setColour(juce::TextEditor::textColourId, juce::Colours::white); + + presetButtonLookAndFeel = std::make_unique(); + presetComboBoxLookAndFeel = std::make_unique(); + + savePresetButton.setLookAndFeel(presetButtonLookAndFeel.get()); + deletePresetButton.setLookAndFeel(presetButtonLookAndFeel.get()); + resetButton.setLookAndFeel(presetButtonLookAndFeel.get()); + // TODO: presetBox.setLookAndFeel(presetComboBoxLookAndFeel.get()); } //endregion initPresetSystem @@ -766,6 +815,7 @@ CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor (Crystalize initPresetSystem(); + auto logoIcon = juce::XmlDocument::parse (BinaryData::logo_icon_svg); if (logoIcon != nullptr) logoDrawable = juce::Drawable::createFromSVG (*logoIcon); @@ -806,6 +856,7 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() { crystalizeButton.setLookAndFeel(nullptr); presetMenuButton.setLookAndFeel(nullptr); + for (auto* b : lowBandModeButtons) { b->setLookAndFeel(nullptr); } @@ -814,6 +865,37 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() { } }; +float CrystalizerEQAudioProcessorEditor::getInterpolatedDb(float exactBin) { + int bin0 = static_cast(std::floor(exactBin)) - 1; + int bin1 = static_cast(std::floor(exactBin)); + int bin2 = static_cast(std::ceil(exactBin)); + int bin3 = static_cast(std::ceil(exactBin)) + 1; + + const int maxBin = static_cast(spectrumAnalyzer.renderValuesDb.size()) - 1; + bin0 = juce::jlimit(0, maxBin, bin0); + bin1 = juce::jlimit(0, maxBin, bin1); + bin2 = juce::jlimit(0, maxBin, bin2); + bin3 = juce::jlimit(0, maxBin, bin3); + + float y0 = spectrumAnalyzer.renderValuesDb[bin0]; + float y1 = spectrumAnalyzer.renderValuesDb[bin1]; + float y2 = spectrumAnalyzer.renderValuesDb[bin2]; + float y3 = spectrumAnalyzer.renderValuesDb[bin3]; + + float fraction = exactBin - bin1; + + // Catmull-Rom Spline Interpolation + float a0 = -0.5f * y0 + 1.5f * y1 - 1.5f * y2 + 0.5f * y3; + float a1 = y0 - 2.5f * y1 + 2.0f * y2 - 0.5f * y3; + float a2 = -0.5f * y0 + 0.5f * y2; + float a3 = y1; + + return a0 * fraction * fraction * fraction + + a1 * fraction * fraction + + a2 * fraction + + a3; +} + //region paintAnalyzer void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) { analyzerRect = getLocalArea(&analyzerArea, analyzerArea.getLocalBounds()); @@ -822,14 +904,156 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) { auto r = analyzerRect.toFloat(); - // Hintergrund des Analyzer-Bereichs + // Hintergrund g.setColour(juce::Colours::black); g.fillRect(r); - // Rahmen (optional) + // Rahmen g.setColour(juce::Colours::grey); g.drawRect(analyzerRect); + + // === Spektrum zeichnen === + g.setColour(Colours::ACCENTCOLOUR); + + const float analyzerWidth = analyzerRect.getWidth(); + const float analyzerHeight = analyzerRect.getHeight(); + const float analyzerBottom = analyzerRect.getBottom(); + + const float minDb = spectrumAnalyzer.MINDB; + 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 numPoints = static_cast(analyzerWidth) * resolutionMultiplier; + + for (int i = 0; i < numPoints; ++i) { + // Berechne X-Position + float x = (i / static_cast(numPoints)) * analyzerWidth; + + // Berechne Frequenz (logarithmisch) + float normalizedX = i / static_cast(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; + } else { + spectrumPath.lineTo(x + analyzerRect.getX(), y); + } + } + + // Zeichne die Linie mit Anti-Aliasing + if (pathStarted) { + juce::PathStrokeType stroke(2.0f); + stroke.setJointStyle(juce::PathStrokeType::curved); // Runde Ecken + g.strokePath(spectrumPath, stroke); + + spectrumPath.lineTo(analyzerRect.getRight(), analyzerBottom); + spectrumPath.lineTo(analyzerRect.getX(), analyzerBottom); + spectrumPath.closeSubPath(); + + g.setColour(Colours::ACCENTCOLOUR.withAlpha(0.3f)); + 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); + + float normalized = (logFreq - logMin) / (logMax - logMin); + return normalized * width; +} + +void CrystalizerEQAudioProcessorEditor::drawFrequencyGrid(juce::Graphics& g, const float dbRange) { + g.setColour(Colours::FOREGROUNDCOLOUR.withAlpha(0.3f)); + + const float minFreq = spectrumAnalyzer.MINFREQ; + const float maxFreq = spectrumAnalyzer.MAXFREQ; + const float analyzerWidth = analyzerRect.getWidth(); + + const float minDB = spectrumAnalyzer.MINDB; + const float maxDB = spectrumAnalyzer.MAXDB; + const float analyzerHeight = analyzerRect.getHeight(); + const float analyzerBottom = analyzerRect.getBottom(); + + std::vector gridFreqs = {0, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000}; + std::vector gridDB = {0, -12, -24, -36, -48, -60, -72}; + + + 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; + + g.drawHorizontalLine(static_cast(y), static_cast(analyzerRect.getX()), static_cast(analyzerRect.getRight())); + g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f)); + juce::String label = juce::String(gridDB[i]); + g.drawText(label, analyzerRect.getX(), y, 30, 15, + juce::Justification::centred); + + } + + for (float freq : gridFreqs) { + float x = mapFrequencyToX(freq, minFreq, maxFreq, analyzerWidth); + x += analyzerRect.getX(); + + g.drawVerticalLine(static_cast(x), + static_cast(analyzerRect.getY()), + static_cast(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); + g.drawText(label, static_cast(x) - 15, analyzerRect.getY(), 30, 15, + juce::Justification::centred); + } + +} + //endregion paintAnalyzer void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) { @@ -1183,6 +1407,11 @@ void CrystalizerEQAudioProcessorEditor::timerCallback() repaint(analyzerRect); resized(); animateCrystalizeButton(); + + if (presetMenuSafePtr == nullptr) + { + presetMenuButton.setToggleState(false, juce::dontSendNotification); + } } //endregion timerCallback @@ -1200,6 +1429,7 @@ void CrystalizerEQAudioProcessorEditor::resized() + const auto testBounds = mainPanel.getLocalBounds(); const auto testWidth = testBounds.getWidth(); const auto testHeight = testBounds.getHeight(); @@ -1263,6 +1493,8 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() { const float presetAreaWidth = static_cast(bounds.getWidth()) * 0.5f; + const auto titleWidthCentre = titleLabel.getFont().getStringWidth("Crystalizer") / 4; + Layout::GridSpec headerSpec{ /* cols */ { Layout::fr(1), Layout::pxTrack(presetAreaWidth), Layout::fr(1) }, /* rows */ { Layout::fr(1) }, @@ -1271,8 +1503,9 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() { /* pad */ Layout::padding(Spacing::SizeMode::XS) }; Layout::grid(bounds, headerSpec, { - Layout::area(titleLabel, 1, 1, 2, 2), Layout::area(presetArea, 1, 2, 2, 3), + Layout::area(titleLabel, 1, 1, 2, 2) + .withMargin(juce::GridItem::Margin(0, 0, 0, titleWidthCentre)), Layout::area(presetMenu, 1, 3, 2, 4), }); @@ -1294,11 +1527,12 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() { const auto presetAreaBounds = presetArea.getLocalBounds(); const auto presetBoxWidth = static_cast(presetArea.getWidth()); const auto presetBoxHeight = static_cast(presetArea.getHeight()); - + const float rowHeight = presetBoxHeight * 0.25f; + const float iconSize = rowHeight; Layout::GridSpec presetSpec{ /* cols */ { Layout::fr(1), Layout::pxTrack(presetBoxWidth * 0.5f), Layout::fr(1)}, - /* rows */ { Layout::pxTrack(presetBoxHeight * 0.25f), Layout::pxTrack(presetBoxHeight * 0.25f)}, + /* rows */ { Layout::pxTrack(rowHeight), Layout::pxTrack(rowHeight)}, /* colGap */ Spacing::SizeMode::XS, /* rowGap */ Spacing::SizeMode::XS, /* pad */ Layout::padding(Spacing::SizeMode::S) @@ -1311,7 +1545,8 @@ void CrystalizerEQAudioProcessorEditor::setupHeader() { Layout::area(resetButton, 2, 1, 2, 2), // Menütaste unten rechts (row2, col2) Layout::area(presetMenuButton, 2, 3, 3, 4) - , + .withWidth(iconSize) + .withHeight(iconSize) }); @@ -1834,7 +2069,9 @@ void SpectrumAnalyzer::applyWindowOnFftFrame(std::vector &fullFrame) { void SpectrumAnalyzer::processWindowedFrame(std::vector &windowedFrame) { if (windowedFrame.size() != FFTSIZE || fftData.size() != FFTSIZE * 2) return; fillFftDataFromFrame(windowedFrame); - fft.performRealOnlyForwardTransform(fftData.data()); + // fft.performRealOnlyForwardTransform(fftData.data()); + + fft.performFrequencyOnlyForwardTransform(fftData.data(), true); buildMagnitudeSpectrum(); convertToDb(); applySmoothing(); @@ -1846,8 +2083,9 @@ void SpectrumAnalyzer::processWindowedFrame(std::vector &windowedFrame) { //region fillFftDataFromFrame void SpectrumAnalyzer::fillFftDataFromFrame(std::vector &windowedFrame) { for (int n = 0; n < FFTSIZE; ++n) { - fftData[2*n] = windowedFrame[n]; - fftData[2*n + 1] = 0.0f; + /*fftData[2*n] = windowedFrame[n]; + fftData[2*n + 1] = 0.0f;*/ + fftData[n] = windowedFrame[n]; } } //endregion fillFftDataFromFrame @@ -1855,17 +2093,9 @@ void SpectrumAnalyzer::fillFftDataFromFrame(std::vector &windowedFrame) { //region buildMagnitudeSpectrum void SpectrumAnalyzer::buildMagnitudeSpectrum() { for (int k = 0; k < BINS; ++k) { - float re = 0.f; - float im = 0.f; - if (k < BINS / 2) { - re = fftData[k]; - } else { - im = fftData[k]; - } - - float mag = sqrt(re * re + im * im); + float mag = fftData[k]; mag /= (FFTSIZE * 0.5f); - mag = std::max(mag, 1e-12f); + mag = std::max(mag, 1e-24f); magnitudes[k] = mag; } } @@ -1873,9 +2103,14 @@ void SpectrumAnalyzer::buildMagnitudeSpectrum() { //region convertToDb void SpectrumAnalyzer::convertToDb() { + + for (int k = 0; k < magnitudes.size(); ++k) { float mag = magnitudes[k]; float dB = juce::Decibels::gainToDecibels(mag); + + + dB = juce::jlimit(MINDB, MAXDB, dB); magnitudesDb[k] = dB; } @@ -1927,6 +2162,7 @@ void SpectrumAnalyzer::applyFreqSmoothing() { float avg = sum / static_cast(highestBin - lowestBin + 1); freqSmoothedMagnitudesDb[k] = avg; + } } //endregion applyFreqSmoothing @@ -1937,7 +2173,9 @@ void SpectrumAnalyzer::applyPeakHoldAndFalloff() { for (int k = 0; k < BINS; ++k) { float current = freqSmoothedMagnitudesDb[k]; float prev = prevPeak[k]; + peakHoldMagnitudesDb[k] = std::max(current, prev - FALLOFFRATE * DELTAT); + } } //endregion applyPeakHoldAndFalloff diff --git a/CrystalizerEQ/PluginEditor.h b/CrystalizerEQ/PluginEditor.h index f5f8654..19cbba6 100644 --- a/CrystalizerEQ/PluginEditor.h +++ b/CrystalizerEQ/PluginEditor.h @@ -77,8 +77,8 @@ class SpectrumAnalyzer { - const float MINDB = -120.f; - const float MAXDB = 96.f; + const float MINDB = -90.f; + const float MAXDB = 0.f; std::vector magnitudes; @@ -202,10 +202,14 @@ public: &high12, &high24, &high36, &high48 }; + + juce::TextButton testNoiseButton, resetButton, savePresetButton, deletePresetButton; juce::ToggleButton masterBypassButton, crystalizeButton, peak1BypassButton, peak2BypassButton, peak3BypassButton, presetMenuButton; + const juce::Array peakBypassButtons = {&peak1BypassButton, &peak2BypassButton, &peak3BypassButton}; + juce::ComboBox presetBox; juce::Component lowBandModeBox, highBandModeBox; @@ -276,10 +280,10 @@ private: std::unique_ptr presetMenuButtonLookAndFeel; std::unique_ptr lowBandButtonLookAndFeel; std::unique_ptr highBandButtonLookAndFeel; - - - std::unique_ptr svgToggleButtonLookAndFeel; + std::unique_ptr presetMenuLookAndFeel; + std::unique_ptr presetButtonLookAndFeel; + std::unique_ptr presetComboBoxLookAndFeel; //SPECRTRUM ANALYZER @@ -340,5 +344,13 @@ private: std::unique_ptr logoDrawable; + DesignSystem::PresetMenu* test; + juce::Component::SafePointer presetMenuSafePtr; + + float mapFrequencyToX(float freq, float minFreq, float maxFreq, float width); + int getClosestBinForFrequency(float freq); + void drawFrequencyGrid(juce::Graphics& g, const float dbRange); + float getInterpolatedDb(float exactBin); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CrystalizerEQAudioProcessorEditor) }; diff --git a/CrystalizerEQ/PluginProcessor.cpp b/CrystalizerEQ/PluginProcessor.cpp index 07a5967..36befe4 100644 --- a/CrystalizerEQ/PluginProcessor.cpp +++ b/CrystalizerEQ/PluginProcessor.cpp @@ -17,7 +17,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { params.push_back (std::make_unique( "TestNoiseEnabled", "Test Noise Enabled", false)); - params.push_back (std::make_unique("TestNoiseLevel", "Test Noise Level", juce::NormalisableRange(-60.f, 0.f, 0.1f), -18.f)); + params.push_back (std::make_unique("TestNoiseLevel", "Test Noise Level", juce::NormalisableRange(-60.f, 0.f, 0.1f), 0.f)); //LOW-BAND @@ -25,7 +25,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { "LowBandFreq", "LowBand Freq", juce::NormalisableRange(20.f, 20000.f, 1.f, 0.5f), 30.f)); - params.push_back (std::make_unique("LowBandGain", "LowBand Gain", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); + params.push_back (std::make_unique("LowBandGain", "LowBand Gain", juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique("LowBandQ", "LowBand Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); @@ -47,7 +47,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { params.push_back (std::make_unique( "Peak1Gain", "Peak1 Gain (dB)", - juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); + juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak1Q", "Peak1 Q", @@ -64,7 +64,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { params.push_back (std::make_unique( "Peak2Gain", "Peak2 Gain (dB)", - juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); + juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak2Q", "Peak2 Q", @@ -81,7 +81,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { params.push_back (std::make_unique( "Peak3Gain", "Peak3 Gain (dB)", - juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); + juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak3Q", "Peak3 Q", @@ -96,7 +96,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() { "HighBandFreq", "HighBand Freq", juce::NormalisableRange(20.f, 20000.f, 1.f, 0.5f), 17000.f)); - params.push_back (std::make_unique("HighBandGain", "HighBand Gain", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); + params.push_back (std::make_unique("HighBandGain", "HighBand Gain", juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique("HighBandQ", "HighBand Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f));