Added analyzer. Fixed some bugs.

This commit is contained in:
Legaeli 2025-11-20 22:24:11 +01:00
parent da00962842
commit 0eb6325e2e
6 changed files with 575 additions and 220 deletions

View File

@ -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<juce::XmlElement> 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<float>(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<juce::XmlElement> passiveSvg;
std::unique_ptr<juce::XmlElement> 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<int> 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<int> 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;
}
};

View File

@ -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[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg id=\"Ebene_1\" data-name=\"Ebene 1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n"
" <path fill=\"black\" d=\"M24,23.02V4C24,1.79,22.21,0,20,0H.98C.44,0,0,.44,0,.98v2.03c0,.54.44.98.98.98h17.02c1.2,0,2,.8,2,2v17.02c0,.54.44.98.98.98h2.05c.54,0,.98-.44.98-.98Z\"/>\n"
"<svg id=\"Ebene_2\" data-name=\"Ebene 2\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 29.49 36.9\">\n"
" <path d=\"M4.68,17.31v-.81c0-.33.27-.59.59-.59h17.58c1.08,0,2.03.72,2.32,1.76l4.3,15.48c.11.38-.18.75-.57.75h-.78c-.26,0-.49-.17-.57-.42l-4.34-13.89c-.31-1.01-1.24-1.69-2.3-1.69H5.27c-.33,0-.59-.27-.59-.59Z\"/>\n"
"</svg>";
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[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg id=\"Ebene_1\" data-name=\"Ebene 1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n"
" <path fill=\"black\" d=\"M0,23.02V4C0,1.79,1.79,0,4,0h19.02c.54,0,.98.44.98.98v2.03c0,.54-.44.98-.98.98H6c-1.2,0-2,.8-2,2v17.02c0,.54-.44.98-.98.98H.98c-.54,0-.98-.44-.98-.98Z\"/>\n"
"<svg id=\"Ebene_2\" data-name=\"Ebene 2\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 29.49 36.9\">\n"
" <path d=\"M29.49,17.31v-.81c0-.33-.27-.59-.59-.59H11.32c-1.08,0-2.03.72-2.32,1.76l-4.3,15.48c-.11.38.18.75.57.75h.78c.26,0,.49-.17.57-.42l4.34-13.89c.31-1.01,1.24-1.69,2.3-1.69h15.64c.33,0,.59-.27.59-.59Z\"/>\n"
"</svg>";
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;

View File

@ -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;

View File

@ -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<DesignSystem::PresetMenuLookAndFeel>();
juce::Rectangle<int> areaToPointTo = presetMenuButton.getScreenBounds();
auto* menuRaw = menu;
presetMenuSafePtr = menuRaw;
auto& box = juce::CallOutBox::launchAsynchronously(
std::unique_ptr<juce::Component>(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<DesignSystem::PresetButtonsLookAndFeel>();
presetComboBoxLookAndFeel = std::make_unique<DesignSystem::PresetComboBoxLookAndFeel>();
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<int>(std::floor(exactBin)) - 1;
int bin1 = static_cast<int>(std::floor(exactBin));
int bin2 = static_cast<int>(std::ceil(exactBin));
int bin3 = static_cast<int>(std::ceil(exactBin)) + 1;
const int maxBin = static_cast<int>(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<int>(analyzerWidth) * resolutionMultiplier;
for (int i = 0; i < numPoints; ++i) {
// Berechne X-Position
float x = (i / static_cast<float>(numPoints)) * analyzerWidth;
// Berechne Frequenz (logarithmisch)
float normalizedX = i / static_cast<float>(numPoints);
float logMin = std::log10(minFreq);
float logMax = std::log10(maxFreq);
float logFreq = logMin + normalizedX * (logMax - logMin);
float freq = std::pow(10.0f, logFreq);
// Finde Bin und interpoliere
float exactBin = freq / deltaF;
float dB = getInterpolatedDb(exactBin);
// Normalisiere
float normalized = (dB - minDb) / dbRange;
normalized = juce::jlimit(0.0f, 1.0f, normalized);
// Berechne Y-Position
float pixelHeight = normalized * analyzerHeight;
float y = analyzerBottom - pixelHeight;
// Füge zum Path hinzu
if (!pathStarted) {
spectrumPath.startNewSubPath(x + analyzerRect.getX(), y);
pathStarted = true;
} 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<float> gridFreqs = {0, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000};
std::vector<float> 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<int>(y), static_cast<float>(analyzerRect.getX()), static_cast<float>(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<int>(x),
static_cast<float>(analyzerRect.getY()),
static_cast<float>(analyzerRect.getBottom()));
// Optional: Frequenz-Label zeichnen
if (freq == 20) continue;
g.setFont(Typography::getFont(Typography::Style::Mono, 1.0f));
juce::String label = (freq >= 1000) ? juce::String(freq / 1000) + "k" : juce::String(freq);
g.drawText(label, static_cast<int>(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<float>(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<float>(presetArea.getWidth());
const auto presetBoxHeight = static_cast<float>(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<float> &fullFrame) {
void SpectrumAnalyzer::processWindowedFrame(std::vector<float> &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<float> &windowedFrame) {
//region fillFftDataFromFrame
void SpectrumAnalyzer::fillFftDataFromFrame(std::vector<float> &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<float> &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<float>(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

View File

@ -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<float> 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<juce::ToggleButton*> peakBypassButtons = {&peak1BypassButton, &peak2BypassButton, &peak3BypassButton};
juce::ComboBox presetBox;
juce::Component lowBandModeBox, highBandModeBox;
@ -276,10 +280,10 @@ private:
std::unique_ptr<Components::PresetMenuButtonLookAndFeel> presetMenuButtonLookAndFeel;
std::unique_ptr<Components::lowBandButtonLookAndFeel> lowBandButtonLookAndFeel;
std::unique_ptr<Components::highBandButtonLookAndFeel> highBandButtonLookAndFeel;
std::unique_ptr<Components::SvgToggleButtonLookAndFeel> svgToggleButtonLookAndFeel;
std::unique_ptr<DesignSystem::PresetMenuLookAndFeel> presetMenuLookAndFeel;
std::unique_ptr<DesignSystem::PresetButtonsLookAndFeel> presetButtonLookAndFeel;
std::unique_ptr<DesignSystem::PresetComboBoxLookAndFeel> presetComboBoxLookAndFeel;
//SPECRTRUM ANALYZER
@ -340,5 +344,13 @@ private:
std::unique_ptr<juce::Drawable> logoDrawable;
DesignSystem::PresetMenu* test;
juce::Component::SafePointer<DesignSystem::PresetMenu> 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)
};

View File

@ -17,7 +17,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
params.push_back (std::make_unique<juce::AudioParameterBool>(
"TestNoiseEnabled", "Test Noise Enabled", false));
params.push_back (std::make_unique<juce::AudioParameterFloat>("TestNoiseLevel", "Test Noise Level", juce::NormalisableRange<float>(-60.f, 0.f, 0.1f), -18.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("TestNoiseLevel", "Test Noise Level", juce::NormalisableRange<float>(-60.f, 0.f, 0.1f), 0.f));
//LOW-BAND
@ -25,7 +25,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
"LowBandFreq", "LowBand Freq",
juce::NormalisableRange<float>(20.f, 20000.f, 1.f, 0.5f), 30.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("LowBandGain", "LowBand Gain", juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("LowBandGain", "LowBand Gain", juce::NormalisableRange<float>(-48.f, 48.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("LowBandQ", "LowBand Q", juce::NormalisableRange<float>(0.1f, 10.f, 0.01f), 1.f));
@ -47,7 +47,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak1Gain", "Peak1 Gain (dB)",
juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
juce::NormalisableRange<float>(-48.f, 48.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak1Q", "Peak1 Q",
@ -64,7 +64,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak2Gain", "Peak2 Gain (dB)",
juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
juce::NormalisableRange<float>(-48.f, 48.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak2Q", "Peak2 Q",
@ -81,7 +81,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak3Gain", "Peak3 Gain (dB)",
juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
juce::NormalisableRange<float>(-48.f, 48.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>(
"Peak3Q", "Peak3 Q",
@ -96,7 +96,7 @@ CrystalizerEQAudioProcessor::createParameterLayout() {
"HighBandFreq", "HighBand Freq",
juce::NormalisableRange<float>(20.f, 20000.f, 1.f, 0.5f), 17000.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("HighBandGain", "HighBand Gain", juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("HighBandGain", "HighBand Gain", juce::NormalisableRange<float>(-48.f, 48.f, 0.1f), 0.f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("HighBandQ", "HighBand Q", juce::NormalisableRange<float>(0.1f, 10.f, 0.01f), 1.f));