Compare commits

...

2 Commits

Author SHA1 Message Date
Legaeli
0eb6325e2e Added analyzer. Fixed some bugs. 2025-11-20 22:24:11 +01:00
Legaeli
da00962842 Finished all sliders. Added logo. 2025-11-13 16:51:12 +01:00
9 changed files with 717 additions and 254 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);
@ -880,6 +873,10 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 {
juce::Slider::SliderLayout getSliderLayout (juce::Slider& slider) override
{
juce::Slider::SliderLayout layout;
if (slider.getName() == "LowBand Slope" || slider.getName() == "HighBand Slope") {
layout.textBoxBounds = {};
return layout;
}
auto r = slider.getLocalBounds();
@ -922,109 +919,110 @@ 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:
static constexpr float labelPadding = 20.0f;
juce::Slider::SliderLayout getSliderLayout(juce::Slider& slider) override
{
juce::Slider::SliderLayout layout;
@ -1119,53 +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;
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;
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);
//g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, 2.0f);
// Einfacher Ring
g.setColour(surfaceCol);
g.fillEllipse(centreX - radius, centreY - radius, radius * 2.0f , radius * 2.0f);
juce::Path valueArc;
valueArc.addCentredArc(centreX, centreY, radius, radius,
0.0f, mid, angle, true);
g.setColour(ringCol);
g.drawEllipse(centreX - radius, centreY - radius, radius * 2.0f, radius * 2.0f, arcPathWidth);
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));
juce::Path valueArc;
valueArc.addCentredArc(centreX, centreY, radius, radius,
0.0f, mid, angle, true);
g.setColour(accentCol);
g.fillPath(p);
}
};
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);
}
};
struct SliderStyles {
struct Size {
@ -1183,7 +1186,7 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 {
case SizeMode::Freq: return 0.8f;
case SizeMode::Q: return 0.8f;
case SizeMode::Slope: return 0.7f;
case SizeMode::Global: return 1.0f;
case SizeMode::Global: return 0.8f;
case SizeMode::Mix: return 0.8f;
default: return 1.0f;
}
@ -1231,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,
@ -1336,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

@ -164,6 +164,7 @@
<None Include="..\..\Resources\Fonts\Orbitron-Bold.ttf"/>
<None Include="..\..\Resources\Fonts\Orbitron-Regular.ttf"/>
<None Include="..\..\Resources\Fonts\Roboto-Regular.ttf"/>
<None Include="..\..\Resources\Icons\logo_icon.svg"/>
<None Include="..\..\Resources\Icons\bell_icon.svg"/>
<None Include="..\..\Resources\Icons\high_cut_icon.svg"/>
<None Include="..\..\Resources\Icons\high_shelf_icon.svg"/>

View File

@ -74,6 +74,9 @@
<None Include="..\..\Resources\Fonts\Roboto-Regular.ttf">
<Filter>CrystalizerEQ\Resources\Fonts</Filter>
</None>
<None Include="..\..\Resources\Icons\logo_icon.svg">
<Filter>CrystalizerEQ\Resources\Icons</Filter>
</None>
<None Include="..\..\Resources\Icons\bell_icon.svg">
<Filter>CrystalizerEQ\Resources\Icons</Filter>
</None>

View File

@ -21,6 +21,7 @@
file="Resources/Fonts/Roboto-Regular.ttf"/>
</GROUP>
<GROUP id="{3DCFA257-AECE-2625-016F-D02D71FFA24B}" name="Icons">
<FILE id="RCNwIt" name="logo_icon.svg" compile="0" resource="1" file="Resources/Icons/logo_icon.svg"/>
<FILE id="waE621" name="bell_icon.svg" compile="0" resource="1" file="Resources/Icons/bell_icon.svg"/>
<FILE id="KOfMYl" name="high_cut_icon.svg" compile="0" resource="1"
file="Resources/Icons/high_cut_icon.svg"/>

View File

@ -11413,63 +11413,88 @@ static const unsigned char temp_binary_data_6[] =
const char* RobotoRegular_ttf = (const char*) temp_binary_data_6;
//================== bell_icon.svg ==================
//================== logo_icon.svg ==================
static const unsigned char temp_binary_data_7[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 3100 3100\">\n"
" <g id=\"Ebene_2\" data-name=\"Ebene 2\">\n"
" <circle cx=\"1550.48\" cy=\"1550\" r=\"64\" fill=\"#333\"/>\n"
" </g>\n"
" <g id=\"Ebene_3\" data-name=\"Ebene 3\">\n"
" <path d=\"M1512.64,1563.07c4.53,0,8.47-4.21,9.59-8.78.37-1.57.45-2.93.45-4.86,0-1.36,0-2.5-.3-3.71-.89-4.64-4.46-8.85-9.22-8.85-6.1,0-10.26,5.78-10.26,13.64,0,6.57,3.49,12.57,9.74,12.57ZM1523.38,1559.03c-2.38,4.14-6.65,9.03-12.38,9.03-8.55,0-14.7"
"2-6.85-14.72-17.35,0-11.28,7.73-18.78,16.06-18.78,11.02,0,11.04,9.03,11.04,9.03l1.81-9.03h7.23l-5.27,18.1,5.27,18.03h-7.23\" fill=\"#fcfaf9\"/>\n"
" <path d=\"M1604.67,1546.39c0-9.91-6.59-14.45-18.06-14.45-11.68.09-18.06,4.49-18.06,14.45,0,8.11,9.03,12.65,9.03,12.65l-9.03,1.76v7.23l13.55-3.96v-5.02c-4.11-2.29-8.85-5.06-8.85-10.84,0-7.24,4.4-9.76,13.37-9.69,9.1,0,13.37,2.53,13.37,9.69,0,5.74-4"
".73,8.53-8.85,10.84v5.04l13.55,3.95v-7.23l-9.03-1.76s9.03-4.55,9.03-12.65Z\" fill=\"#fcfaf9\"/>\n"
" <polygon points=\"1534.67 1572.58 1543.71 1550 1536.93 1527.42 1496.29 1527.42 1496.29 1522.9 1541.45 1522.9 1550.48 1540.97 1559.51 1522.9 1604.67 1522.9 1604.67 1527.42 1564.03 1527.42 1557.25 1550 1566.29 1572.58 1604.67 1572.58 1604.67 1577.1"
" 1561.77 1577.1 1550.48 1554.52 1539.19 1577.1 1496.29 1577.1 1496.29 1572.58 1534.67 1572.58\" fill=\"#56feff\"/>\n"
" </g>\n"
" <g id=\"Ebene_5\" data-name=\"Ebene 5\">\n"
" <rect x=\"1582.09\" y=\"1554.52\" width=\"9.03\" height=\"13.55\" fill=\"#333\"/>\n"
" <rect x=\"1581.53\" y=\"1565.63\" width=\"11.31\" height=\"4.42\" fill=\"#333\"/>\n"
" <rect x=\"1585.12\" y=\"1564.25\" width=\"11.31\" height=\"4.42\" transform=\"translate(1131.12 -618.28) rotate(33.56)\" fill=\"#333\"/>\n"
" <polygon points=\"1587.05 1561.29 1584.91 1557.43 1592.17 1553.63 1591.89 1558.6 1587.05 1561.29\" fill=\"#333\"/>\n"
" </g>\n"
"</svg>";
const char* logo_icon_svg = (const char*) temp_binary_data_7;
//================== bell_icon.svg ==================
static const unsigned char temp_binary_data_8[] =
"<?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 28 12.02\">\n"
" <path fill=\"black\" d=\"M0,8.02h4.82c.75,0,1.45-.34,1.92-.92L11.42,1.24c1.32-1.65,3.84-1.65,5.16,0l4.68,5.86c.47.58,1.17.92,1.92.92h4.82v4h-4.82c-.75,0-1.45-.34-1.92-.92l-5.73-7.16c-.79-.98-2.28-.98-3.07,0l-5.73,7.16c-.47.58-1.17.92-1.92.92H0s0-4,"
"0-4Z\"/>\n"
"</svg>";
const char* bell_icon_svg = (const char*) temp_binary_data_7;
const char* bell_icon_svg = (const char*) temp_binary_data_8;
//================== high_cut_icon.svg ==================
static const unsigned char temp_binary_data_8[] =
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_8;
const char* high_cut_icon_svg = (const char*) temp_binary_data_9;
//================== high_shelf_icon.svg ==================
static const unsigned char temp_binary_data_9[] =
static const unsigned char temp_binary_data_10[] =
"<?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 28 12\">\n"
" <path fill=\"black\" d=\"M11.23,6.39l5.62-4.77c1.23-1.04,2.79-1.61,4.4-1.61h6.04c.4,0,.72.32.72.72v2.56c0,.4-.32.72-.72.72h-6.08c-1.62,0-3.18.58-4.41,1.63l-5.56,4.75c-1.23,1.05-2.79,1.63-4.41,1.63H.72c-.4,0-.72-.32-.72-.72v-2.56c0-.4.32-.72.72-.72h"
"6.12c1.61,0,3.17-.57,4.4-1.61Z\"/>\n"
"</svg>";
const char* high_shelf_icon_svg = (const char*) temp_binary_data_9;
const char* high_shelf_icon_svg = (const char*) temp_binary_data_10;
//================== low_cut_icon.svg ==================
static const unsigned char temp_binary_data_10[] =
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_10;
const char* low_cut_icon_svg = (const char*) temp_binary_data_11;
//================== low_shelf_icon.svg ==================
static const unsigned char temp_binary_data_11[] =
static const unsigned char temp_binary_data_12[] =
"<?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 28 12\">\n"
" <path fill=\"black\" d=\"M16.77,6.39L11.15,1.61C9.92.57,8.36,0,6.75,0H.72C.32,0,0,.32,0,.72v2.56c0,.4.32.72.72.72h6.08c1.62,0,3.18.58,4.41,1.63l5.56,4.75c1.23,1.05,2.79,1.63,4.41,1.63h6.11c.4,0,.72-.32.72-.72v-2.56c0-.4-.32-.72-.72-.72h-6.12c-1.61,"
"0-3.17-.57-4.4-1.61Z\"/>\n"
"</svg>";
const char* low_shelf_icon_svg = (const char*) temp_binary_data_11;
const char* low_shelf_icon_svg = (const char*) temp_binary_data_12;
//================== preset_menu_icon.svg ==================
static const unsigned char temp_binary_data_12[] =
static const unsigned char temp_binary_data_13[] =
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 30 30\" width=\"60px\" height=\"60px\"><path d=\"M 3 7 A 1.0001 1.0001 0 1 0 3 9 L 27 9 A 1.0001 1.0001 0 1 0 27 7 L 3 7 z M 3 14 A 1.0001 1.0001 0 1 0 3 16 L 27 16 A 1.0001 1.0001 0 1 0 27 14 "
"L 3 14 z M 3 21 A 1.0001 1.0001 0 1 0 3 23 L 27 23 A 1.0001 1.0001 0 1 0 27 21 L 3 21 z\" fill=\"black\"/></svg>";
const char* preset_menu_icon_svg = (const char*) temp_binary_data_12;
const char* preset_menu_icon_svg = (const char*) temp_binary_data_13;
//================== crystalize_button_active_icon.svg ==================
static const unsigned char temp_binary_data_13[] =
static const unsigned char temp_binary_data_14[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 32\">\n"
" <g id=\"Ebene_6\" data-name=\"Ebene 6\">\n"
@ -11548,10 +11573,10 @@ static const unsigned char temp_binary_data_13[] =
" </g>\n"
"</svg>";
const char* crystalize_button_active_icon_svg = (const char*) temp_binary_data_13;
const char* crystalize_button_active_icon_svg = (const char*) temp_binary_data_14;
//================== crystalize_button_passive_icon.svg ==================
static const unsigned char temp_binary_data_14[] =
static const unsigned char temp_binary_data_15[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 32\">\n"
" <g id=\"Ebene_6\" data-name=\"Ebene 6\">\n"
@ -11629,17 +11654,17 @@ static const unsigned char temp_binary_data_14[] =
" </g>\n"
"</svg>";
const char* crystalize_button_passive_icon_svg = (const char*) temp_binary_data_14;
const char* crystalize_button_passive_icon_svg = (const char*) temp_binary_data_15;
//================== bypass_icon.svg ==================
static const unsigned char temp_binary_data_15[] =
static const unsigned char temp_binary_data_16[] =
"<?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 22 23\">\n"
" <rect x=\"10\" width=\"2\" height=\"12\"/>\n"
" <path d=\"M13,1.19v1.87c4.1.91,7.17,4.57,7.17,8.94,0,5.06-4.1,9.17-9.17,9.17S1.83,17.06,1.83,12C1.83,7.63,4.9,3.97,9,3.06v-1.87C3.88,2.13,0,6.61,0,12c0,6.08,4.92,11,11,11s11-4.92,11-11c0-5.39-3.88-9.87-9-10.81Z\" fill=\"black\"/>\n"
"</svg>";
const char* bypass_icon_svg = (const char*) temp_binary_data_15;
const char* bypass_icon_svg = (const char*) temp_binary_data_16;
const char* getNamedResource (const char* resourceNameUTF8, int& numBytes);
@ -11660,10 +11685,11 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes)
case 0xcbceafb3: numBytes = 24668; return OrbitronBold_ttf;
case 0x9c8232dc: numBytes = 24716; return OrbitronRegular_ttf;
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;
@ -11685,6 +11711,7 @@ const char* namedResourceList[] =
"OrbitronBold_ttf",
"OrbitronRegular_ttf",
"RobotoRegular_ttf",
"logo_icon_svg",
"bell_icon_svg",
"high_cut_icon_svg",
"high_shelf_icon_svg",
@ -11705,6 +11732,7 @@ const char* originalFilenames[] =
"Orbitron-Bold.ttf",
"Orbitron-Regular.ttf",
"Roboto-Regular.ttf",
"logo_icon.svg",
"bell_icon.svg",
"high_cut_icon.svg",
"high_shelf_icon.svg",

View File

@ -29,17 +29,20 @@ namespace BinaryData
extern const char* RobotoRegular_ttf;
const int RobotoRegular_ttfSize = 146004;
extern const char* logo_icon_svg;
const int logo_icon_svgSize = 1793;
extern const char* bell_icon_svg;
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;
@ -57,7 +60,7 @@ namespace BinaryData
const int bypass_icon_svgSize = 406;
// Number of elements in the namedResourceList and originalFileNames arrays.
const int namedResourceListSize = 16;
const int namedResourceListSize = 17;
// Points to the start of a list of resource names.
extern const char* namedResourceList[];

View File

@ -52,6 +52,9 @@ void CrystalizerEQAudioProcessorEditor::setupSliders() {
&peak3FreqSlider, &peak3QSlider, &highBandFreqSlider, &highBandQSlider}) {
s->setLookAndFeel(freqQLookAndFeel.get());
}
lowBandSlopeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
highBandSlopeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
}
//endregion setupSliders
@ -130,7 +133,7 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
testNoiseSlider.setTextValueSuffix(" Gain");
//lowBandFreqSlider.setTextValueSuffix ("\nHz");
lowBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
//lowBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
lowBandGainSlider.setTextValueSuffix("\ndB");
//lowBandQSlider.setTextValueSuffix ("\nQ");
@ -151,7 +154,7 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() {
//highBandFreqSlider.setTextValueSuffix ("\nHz");
highBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
//highBandSlopeSlider.setTextValueSuffix ("\ndB/Oct");
highBandGainSlider.setTextValueSuffix("\ndB");
// highBandQSlider.setTextValueSuffix ("\nQ");
@ -329,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);
@ -379,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);
@ -403,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);
@ -416,7 +428,9 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
lowBandSlopeSlider.addAndMakeVisible(low24);
lowBandSlopeSlider.addAndMakeVisible(low36);
lowBandSlopeSlider.addAndMakeVisible(low48);
lowFilterArea.addAndMakeVisible(lowBandSlopeSlider);
lowFilterArea.addAndMakeVisible(lowdBOctLabel);
lowFilterArea.addAndMakeVisible(lowBandGainSlider);
lowFilterArea.addAndMakeVisible(lowBandQSlider);
@ -465,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);
@ -478,6 +492,8 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
highBandSlopeSlider.addAndMakeVisible(high36);
highBandSlopeSlider.addAndMakeVisible(high48);
highFilterArea.addAndMakeVisible(highBandSlopeSlider);
highFilterArea.addAndMakeVisible(highdBOctLabel);
highFilterArea.addAndMakeVisible(highBandGainSlider);
highFilterArea.addAndMakeVisible(highBandQSlider);
@ -486,13 +502,12 @@ void CrystalizerEQAudioProcessorEditor::addComponentsToLayout() {
}
addAndMakeVisible(footerBar);
footerBar.addAndMakeVisible(globalControlArea);
globalControlArea.addAndMakeVisible(inputLabel);
globalControlArea.addAndMakeVisible(outputLabel);
globalControlArea.addAndMakeVisible(inputSlider);
globalControlArea.addAndMakeVisible(outputSlider);
globalControlArea.addAndMakeVisible(masterBypassButton);
footerBar.addAndMakeVisible(globalControlArea);
addAndMakeVisible(footerBar);
}
//endregion addComponentsToLayout
@ -518,6 +533,7 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() {
setupLabel(low24, "24");
setupLabel(low36, "36");
setupLabel(low48, "48");
setupLabel(lowdBOctLabel, "dB/Oct");
//PEAK 1
setupLabel (peak1FreqLabel,"Low-Mid\nHz");
@ -544,6 +560,8 @@ void CrystalizerEQAudioProcessorEditor::setupLabels() {
setupLabel(high24, "24");
setupLabel(high36, "36");
setupLabel(high48, "48");
setupLabel(highdBOctLabel, "dB/Oct");
setupLabel(presetBoxLabel, "Presets");
@ -557,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);
@ -595,7 +616,6 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() {
lowBandModeButtons[j]->setToggleState(false, juce::dontSendNotification);
}
}
// Aktuellen aktivieren
lowBandBools[i] = true;
param->setValueNotifyingHost(param->convertTo0to1((float)i));
@ -649,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]() {
@ -705,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();
@ -724,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);
@ -733,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
@ -754,6 +814,12 @@ CrystalizerEQAudioProcessorEditor::CrystalizerEQAudioProcessorEditor (Crystalize
setupSliders();
initPresetSystem();
auto logoIcon = juce::XmlDocument::parse (BinaryData::logo_icon_svg);
if (logoIcon != nullptr)
logoDrawable = juce::Drawable::createFromSVG (*logoIcon);
addAndMakeVisible (testNoiseButton);
testNoiseButton.onClick = [this]() {
@ -790,6 +856,7 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() {
crystalizeButton.setLookAndFeel(nullptr);
presetMenuButton.setLookAndFeel(nullptr);
for (auto* b : lowBandModeButtons) {
b->setLookAndFeel(nullptr);
}
@ -798,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());
@ -806,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) {
@ -861,8 +1101,11 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g)
auto hBH = hB.getHeight();
auto hBPad = ((hBY + mPY) - hBH) / 2;
g.fillRect(hBX, hBY, hBW, mPY - hBPad);
//paintBorderLines(g);
const auto logoArea = hB.toFloat().reduced(hBPad);
logoDrawable->drawWithin(g, logoArea,
juce::RectanglePlacement::xRight, // oder xLeft, yTop etc.
1.0f);
g.setColour(Colours::SURFACECOLOUR);
const auto pA = getLocalArea(&presetArea, presetArea.getLocalBounds());
@ -871,7 +1114,10 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g)
auto pAWidth = pA.getWidth();
auto pAHeight = pA.getHeight();
g.fillRoundedRectangle(pAX, pAY - 10.f, pAWidth, pAHeight + 10.f, 10.0f);
//paintBorderLines(g);
const auto fA = getLocalArea(&filterArea, filterArea.getLocalBounds());
const int fABorderWidth = 3;
@ -888,13 +1134,13 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g)
g.setColour(Colours::BACKGROUNDBYPASS);
g.fillRect(fA);
paintBorderLines(g);
//paintModeBoxBorders(g);
g.setColour(Colours::SURFACEBYPASS);
const auto fB = getLocalArea(&footerBar, footerBar.getLocalBounds());
g.fillRect(fB);
paintBorderLines(g);
if constexpr (false) // -> auf false setzen, wenn nicht gebraucht
@ -1054,6 +1300,25 @@ void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) {
g.drawVerticalLine(x,center - halfLen, center + halfLen);
}
g.setColour(DesignSystem::Colours::SURFACECOLOUR);
auto prevRightGlobal = (float) globalControlArea.getRight();
for (auto* c : globalControlArea.getChildren()) {
if (c == &inputSlider) continue;
if (c == &masterBypassButton) continue;
const auto area = getLocalArea(c, c->getLocalBounds());
const float xAvg = ((float) area.getX() - prevRightGlobal) / 2;
const int x = area.getX() - xAvg;
prevRightGlobal = (float) c->getRight();
const auto top = (float) area.getY();
const auto bot = (float) area.getBottom();
const float center = (top + bot) * 0.5f;
const float halfLen = (bot - top) * 0.375f;
g.drawVerticalLine(x,center - halfLen, center + halfLen);
}
}
//endregion paintBorderLines
@ -1073,6 +1338,7 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
lowBandFreqLabel.setEnabled (lowMode >= 1);
lowBandSlopeSlider.setEnabled (lowMode == 1);
lowBandSlopeLabel.setEnabled (lowMode == 1);
lowdBOctLabel.setEnabled (lowMode == 1);
lowBandGainSlider.setEnabled(lowMode >= 2);
lowBandGainLabel .setEnabled(lowMode >= 2);
lowBandQSlider .setEnabled(lowMode >= 1);
@ -1090,6 +1356,7 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() {
highBandFreqLabel.setEnabled (highMode >= 1);
highBandSlopeSlider.setEnabled (highMode == 1);
highBandSlopeLabel.setEnabled (highMode == 1);
highdBOctLabel.setEnabled (highMode == 1);
highBandGainSlider.setEnabled(highMode >= 2);
highBandGainLabel .setEnabled(highMode >= 2);
highBandQSlider .setEnabled(highMode >= 1);
@ -1140,6 +1407,11 @@ void CrystalizerEQAudioProcessorEditor::timerCallback()
repaint(analyzerRect);
resized();
animateCrystalizeButton();
if (presetMenuSafePtr == nullptr)
{
presetMenuButton.setToggleState(false, juce::dontSendNotification);
}
}
//endregion timerCallback
@ -1157,6 +1429,7 @@ void CrystalizerEQAudioProcessorEditor::resized()
const auto testBounds = mainPanel.getLocalBounds();
const auto testWidth = testBounds.getWidth();
const auto testHeight = testBounds.getHeight();
@ -1220,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) },
@ -1228,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),
});
@ -1251,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)
@ -1268,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)
});
@ -1394,6 +1672,10 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() {
Layout::area(lowBandSlopeLabel, 3, 1, 4, 2)
.withMargin(juce::GridItem::Margin(-offSetToGainTop / 1.5f, 0, 0, 0)),
Layout::area(lowdBOctLabel, 3, 1, 4, 2)
.withAlignSelf(juce::GridItem::AlignSelf::center)
.withJustifySelf(juce::GridItem::JustifySelf::center),
});
@ -1630,6 +1912,10 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() {
Layout::area(highBandSlopeLabel, 3, 3, 4, 4)
.withMargin(juce::GridItem::Margin(-offSetToGainTop / 1.5f, 0, 0, 0)),
Layout::area(highdBOctLabel, 3, 3, 4, 4)
.withAlignSelf(juce::GridItem::AlignSelf::center)
.withJustifySelf(juce::GridItem::JustifySelf::center),
});
const auto modeBoxBounds = lowBandModeBox.getLocalBounds();
@ -1668,7 +1954,7 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
const auto refH = getReferenceCell()[1];
Layout::GridSpec footerSpec{
/* cols */ { Layout::fr(1), Layout::fr(1), Layout::pxTrack(footerWidth / 3.0f) },
/* cols */ { Layout::fr(1), Layout::fr(1), Layout::fr(1) },
/* rows */ { Layout::fr(1) },
/* colGap */ Spacing::SizeMode::XS,
/* rowGap */ Spacing::SizeMode::XS,
@ -1682,10 +1968,12 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
const auto globalControlAreaWidth = static_cast<float>(globalControlArea.getWidth());
const auto globalControlAreaHeight = static_cast<float>(globalControlArea.getHeight());
const auto globalControlColWidth = globalControlAreaWidth / 3.0f;
const auto sliderRadius = outputSlider.getWidth() / 2.0f;
const auto sliderWidth = outputSlider.getWidth();
Layout::GridSpec globalControlAreaSpec{
/* cols */ { Layout::fr(1), Layout::pxTrack(globalControlColWidth), Layout::fr(1) },
/* cols */ { Layout::fr(1), Layout::fr(1), Layout::fr(1) },
/* rows */ { Layout::fr(1)},
/* colGap */ Spacing::SizeMode::XS,
/* rowGap */ Spacing::SizeMode::XS,
@ -1694,19 +1982,26 @@ void CrystalizerEQAudioProcessorEditor::setupFooter() {
Layout::grid(globalControlAreaBounds, globalControlAreaSpec, {
//TODO: Bring components closer together
Layout::area(inputSlider, 1, 2, 3, 3)
.withWidth(refW * globalMod * 0.8f)
.withHeight(refH * globalMod * 0.8f)
Layout::area(outputSlider, 1, 2, 3, 3)
.withWidth(refW * globalMod )
.withHeight(refH * globalMod )
.withMargin(juce::GridItem::Margin(0, 0, 0, sliderRadius * 6))
.withAlignSelf(juce::GridItem::AlignSelf::center)
.withJustifySelf(juce::GridItem::JustifySelf::center),
Layout::area(outputSlider, 1, 3, 3, 4)
.withWidth(refW * globalMod * 0.8f)
.withHeight(refH * globalMod * 0.8f)
Layout::area(inputSlider, 1, 1, 3, 2)
.withWidth(refW * globalMod )
.withHeight(refH * globalMod )
.withMargin(juce::GridItem::Margin(0, 0, 0, sliderRadius * 10))
.withAlignSelf(juce::GridItem::AlignSelf::center)
.withJustifySelf(juce::GridItem::JustifySelf::center),
Layout::area(masterBypassButton, 1, 1, 3, 2),
Layout::area(masterBypassButton, 1, 3, 3, 4)
.withWidth(sliderWidth)
.withMargin(juce::GridItem::Margin(0, 0, 0, sliderRadius * 2)),
});
}
@ -1774,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();
@ -1786,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
@ -1795,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;
}
}
@ -1813,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;
}
@ -1867,6 +2162,7 @@ void SpectrumAnalyzer::applyFreqSmoothing() {
float avg = sum / static_cast<float>(highestBin - lowestBin + 1);
freqSmoothedMagnitudesDb[k] = avg;
}
}
//endregion applyFreqSmoothing
@ -1877,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;
@ -180,21 +180,21 @@ public:
juce::Label titleLabel, testNoiseLabel,
lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel,
low12, low24, low36, low48,
low12, low24, low36, low48, lowdBOctLabel,
peak1FreqLabel, peak1GainLabel, peak1QLabel,
peak2FreqLabel, peak2GainLabel, peak2QLabel,
peak3FreqLabel, peak3GainLabel, peak3QLabel,
highBandFreqLabel, highBandSlopeLabel, highBandGainLabel, highBandQLabel, highBandModeLabel,
high12, high24, high36, high48,
high12, high24, high36, high48, highdBOctLabel,
inputLabel, outputLabel,
presetBoxLabel;
const juce::Array<juce::Label*> sliderLabels = {
&lowBandFreqLabel, &lowBandSlopeLabel, &lowBandGainLabel, &lowBandQLabel, &lowBandModeLabel,
&lowBandFreqLabel, &lowBandSlopeLabel, &lowBandGainLabel, &lowBandQLabel, &lowBandModeLabel, &lowdBOctLabel,
&peak1FreqLabel, &peak1GainLabel, &peak1QLabel,
&peak2FreqLabel, &peak2GainLabel, &peak2QLabel,
&peak3FreqLabel, &peak3GainLabel, &peak3QLabel,
&highBandFreqLabel, &highBandSlopeLabel, &highBandGainLabel, &highBandQLabel, &highBandModeLabel,
&highBandFreqLabel, &highBandSlopeLabel, &highBandGainLabel, &highBandQLabel, &highBandModeLabel, &highdBOctLabel,
&inputLabel, &outputLabel
};
const juce::Array<juce::Label*> slopeLabels = {
@ -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
@ -338,5 +342,15 @@ private:
bool isAnimatingCrystalize = false;
bool isFadingToActive = false;
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));