mirror of
https://codeberg.org/armin/gelyk.git
synced 2026-10-11 17:01:53 +02:00
Expand to 7 bands; add labeled rotary Q knobs and gain dB readouts
This commit is contained in:
parent
580fcf7fec
commit
10feb7a7fe
5 changed files with 126 additions and 49 deletions
|
|
@ -102,13 +102,6 @@ target_link_libraries(GelykEQ PRIVATE juce::juce_dsp)
|
|||
|
||||
target_compile_definitions(GelykEQ PRIVATE JUCE_MODULE_AVAILABLE_juce_dsp=1)
|
||||
|
||||
target_compile_definitions(GelykEQ PRIVATE
|
||||
JucePlugin_Build_VST=1
|
||||
JucePlugin_Build_AU=1
|
||||
JucePlugin_Build_AAX=0
|
||||
JucePlugin_Build_Standalone=0
|
||||
)
|
||||
|
||||
target_compile_options(GelykEQ PRIVATE
|
||||
$<$<CXX_COMPILER_ID:AppleClang,Clang>:-Wall -Wextra>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,46 @@ void NeonLookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, int widt
|
|||
}
|
||||
}
|
||||
|
||||
void NeonLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
|
||||
float sliderPos, float startAngle, float endAngle, juce::Slider& slider)
|
||||
{
|
||||
const float cx = x + width * 0.5f;
|
||||
const float cy = y + height * 0.5f;
|
||||
const float radius = juce::jmin(width, height) * 0.5f;
|
||||
const float toAngle = startAngle + sliderPos * (endAngle - startAngle);
|
||||
const float lineW = juce::jmax(2.0f, radius * 0.10f);
|
||||
const float arcRadius = radius - lineW * 0.5f;
|
||||
|
||||
// Background disc.
|
||||
g.setColour(panel);
|
||||
g.fillEllipse(cx - radius, cy - radius, radius * 2.0f, radius * 2.0f);
|
||||
g.setColour(line);
|
||||
g.drawEllipse(cx - radius, cy - radius, radius * 2.0f, radius * 2.0f, 1.0f);
|
||||
|
||||
// Filled arc (progress).
|
||||
juce::Path filledArc;
|
||||
filledArc.addCentredArc(cx, cy, arcRadius, arcRadius, 0.0f,
|
||||
startAngle, toAngle, true);
|
||||
juce::PathStrokeType stroke(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded);
|
||||
g.setColour(slider.isMouseOverOrDragging(true) ? magenta : cyan);
|
||||
g.strokePath(filledArc, stroke);
|
||||
|
||||
// Pointer line.
|
||||
juce::Path pointer;
|
||||
const float pointerLength = radius * 0.68f;
|
||||
pointer.startNewSubPath(cx, cy);
|
||||
pointer.lineTo(cx + pointerLength * std::cos(toAngle),
|
||||
cy + pointerLength * std::sin(toAngle));
|
||||
g.setColour(slider.isMouseOverOrDragging(true) ? violet : bg);
|
||||
g.strokePath(pointer, juce::PathStrokeType(juce::jmax(1.5f, radius * 0.07f),
|
||||
juce::PathStrokeType::curved,
|
||||
juce::PathStrokeType::rounded));
|
||||
|
||||
// Centre hub.
|
||||
g.setColour(slider.isMouseOverOrDragging(true) ? cyan : line);
|
||||
g.fillEllipse(cx - 2.0f, cy - 2.0f, 4.0f, 4.0f);
|
||||
}
|
||||
|
||||
void NeonLookAndFeel::drawComboBox(juce::Graphics& g, int width, int height, bool,
|
||||
int, int, int, int, juce::ComboBox& box)
|
||||
{
|
||||
|
|
@ -136,39 +176,68 @@ GelykEQAudioProcessorEditor::GelykEQAudioProcessorEditor(GelykEQAudioProcessor&
|
|||
masterAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
processor.getAPVTS(), "master", *master);
|
||||
// Band faders.
|
||||
const char* const names[numBands] = { "LOW", "LOW-MID", "MID", "HIGH-MID", "HIGH" };
|
||||
const char* const freqs[numBands] = { "60", "200", "1K", "4K", "10K" };
|
||||
const char* const names[numBands] = { "LOW SHELF", "LOW", "LOW-MID", "MID", "HIGH-MID", "HIGH", "HIGH SHELF" };
|
||||
const char* const freqs[numBands] = { "60", "160", "400", "800", "1.6K", "4K", "10K" };
|
||||
for (int i = 0; i < numBands; ++i)
|
||||
{
|
||||
auto& w = bands[i];
|
||||
const juce::Colour accent = (i % 2 == 0) ? lookAndFeel.cyan : lookAndFeel.magenta;
|
||||
|
||||
w.freqLabel = std::make_unique<juce::Label>("freq", freqs[i]);
|
||||
w.freqLabel->setFont(monoFont(13.0f));
|
||||
w.freqLabel->setColour(juce::Label::textColourId, lookAndFeel.textDim);
|
||||
w.freqLabel->setFont(monoFont(14.0f).boldened());
|
||||
w.freqLabel->setColour(juce::Label::textColourId, accent);
|
||||
w.freqLabel->setJustificationType(juce::Justification::centred);
|
||||
|
||||
w.gainLabel = std::make_unique<juce::Label>("gname", names[i]);
|
||||
w.gainLabel->setFont(monoFont(11.0f));
|
||||
w.gainLabel->setColour(juce::Label::textColourId,
|
||||
i % 2 == 0 ? lookAndFeel.cyan : lookAndFeel.magenta);
|
||||
w.gainLabel->setJustificationType(juce::Justification::centred);
|
||||
w.gainCaption = std::make_unique<juce::Label>("gname", names[i]);
|
||||
w.gainCaption->setFont(monoFont(10.0f));
|
||||
w.gainCaption->setColour(juce::Label::textColourId, lookAndFeel.textDim);
|
||||
w.gainCaption->setJustificationType(juce::Justification::centred);
|
||||
|
||||
w.gain = std::make_unique<juce::Slider>(juce::Slider::LinearVertical, juce::Slider::TextBoxBelow);
|
||||
w.gainValue = std::make_unique<juce::Label>("gval", "0.0");
|
||||
w.gainValue->setFont(monoFont(11.0f));
|
||||
w.gainValue->setColour(juce::Label::textColourId, accent);
|
||||
w.gainValue->setJustificationType(juce::Justification::centred);
|
||||
|
||||
w.qCaption = std::make_unique<juce::Label>("qcap", "Q");
|
||||
w.qCaption->setFont(monoFont(10.0f));
|
||||
w.qCaption->setColour(juce::Label::textColourId, lookAndFeel.textDim);
|
||||
w.qCaption->setJustificationType(juce::Justification::centred);
|
||||
|
||||
w.qValue = std::make_unique<juce::Label>("qval", "1.00");
|
||||
w.qValue->setFont(monoFont(10.0f));
|
||||
w.qValue->setColour(juce::Label::textColourId, lookAndFeel.violet);
|
||||
w.qValue->setJustificationType(juce::Justification::centred);
|
||||
|
||||
w.gain = std::make_unique<juce::Slider>(juce::Slider::LinearVertical, juce::Slider::NoTextBox);
|
||||
w.gain->setLookAndFeel(&lookAndFeel);
|
||||
w.gain->setSliderStyle(juce::Slider::LinearVertical);
|
||||
w.gain->setTextBoxStyle(juce::Slider::TextBoxBelow, false, 40, 14);
|
||||
w.gain->setRange(-24.0, 24.0, 0.01);
|
||||
w.gain->setDoubleClickReturnValue(true, 0.0);
|
||||
w.gain->setValue(0.0, juce::dontSendNotification);
|
||||
w.gain->onValueChange = [this, i]
|
||||
{
|
||||
bands[i].gainValue->setText(juce::String(bands[i].gain->getValue(), 1), juce::dontSendNotification);
|
||||
};
|
||||
|
||||
w.q = std::make_unique<juce::Slider>(juce::Slider::LinearHorizontal, juce::Slider::NoTextBox);
|
||||
w.q = std::make_unique<juce::Slider>(juce::Slider::RotaryVerticalDrag, juce::Slider::NoTextBox);
|
||||
w.q->setLookAndFeel(&lookAndFeel);
|
||||
w.q->setSliderStyle(juce::Slider::RotaryVerticalDrag);
|
||||
w.q->setRotaryParameters(2.4f, 6.9f, true); // ~270° sweep
|
||||
w.q->setRange(0.1, 6.0, 0.01);
|
||||
w.q->setDoubleClickReturnValue(true, 1.0);
|
||||
w.q->setValue(1.0, juce::dontSendNotification);
|
||||
w.q->onValueChange = [this, i]
|
||||
{
|
||||
bands[i].qValue->setText(juce::String(bands[i].q->getValue(), 2), juce::dontSendNotification);
|
||||
};
|
||||
|
||||
addAndMakeVisible(*w.freqLabel);
|
||||
addAndMakeVisible(*w.gainLabel);
|
||||
addAndMakeVisible(*w.gainCaption);
|
||||
addAndMakeVisible(*w.gainValue);
|
||||
addAndMakeVisible(*w.gain);
|
||||
addAndMakeVisible(*w.qCaption);
|
||||
addAndMakeVisible(*w.q);
|
||||
addAndMakeVisible(*w.qValue);
|
||||
|
||||
gainAttachments[i] = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
processor.getAPVTS(), "gain" + juce::String(i), *w.gain);
|
||||
|
|
@ -217,7 +286,7 @@ void GelykEQAudioProcessorEditor::scaleUI(float percent)
|
|||
{
|
||||
const float s = percent / 100.0f;
|
||||
scaling.store(s);
|
||||
setSize((int) (920 * s), (int) (540 * s));
|
||||
setSize((int) (1180 * s), (int) (640 * s));
|
||||
resized();
|
||||
}
|
||||
|
||||
|
|
@ -226,13 +295,13 @@ void GelykEQAudioProcessorEditor::resized()
|
|||
juce::Rectangle<int> area = getLocalBounds();
|
||||
const float s = scaling.load();
|
||||
|
||||
const int headerH = (int) (46 * s);
|
||||
const int headerH = (int) (48 * s);
|
||||
const int footerH = (int) (26 * s);
|
||||
const int pad = (int) (12 * s);
|
||||
|
||||
// Header.
|
||||
juce::Rectangle<int> header = area.removeFromTop(headerH);
|
||||
titleLabel->setBounds(header.removeFromLeft((int) (170 * s)).reduced(pad, 0));
|
||||
titleLabel->setBounds(header.removeFromLeft((int) (180 * s)).reduced(pad, 0));
|
||||
scaleBox->setBounds(header.removeFromRight((int) (90 * s)).reduced((int)(6*s), (int)(9*s)));
|
||||
|
||||
// Footer.
|
||||
|
|
@ -241,7 +310,7 @@ void GelykEQAudioProcessorEditor::resized()
|
|||
statusBar->setBounds(footer.removeFromLeft(footer.getWidth() / 2));
|
||||
statusGit->setBounds(footer);
|
||||
|
||||
// Main grid.
|
||||
// Master column (right).
|
||||
const int masterW = (int) (64 * s);
|
||||
juce::Rectangle<int> masterArea = area.removeFromRight(masterW);
|
||||
|
||||
|
|
@ -250,7 +319,7 @@ void GelykEQAudioProcessorEditor::resized()
|
|||
const int totalW = area.getWidth();
|
||||
const int height = area.getHeight();
|
||||
|
||||
const int gap = (int) (10 * s);
|
||||
const int gap = (int) (12 * s);
|
||||
const int colW = (totalW - gap * (numBands - 1)) / numBands;
|
||||
|
||||
for (int i = 0; i < numBands; ++i)
|
||||
|
|
@ -258,22 +327,27 @@ void GelykEQAudioProcessorEditor::resized()
|
|||
auto& w = bands[i];
|
||||
juce::Rectangle<int> col(x0 + i * (colW + gap), y0, colW, height);
|
||||
|
||||
const int topLabelH = (int) (20 * s);
|
||||
const int freqH = (int) (16 * s);
|
||||
const int gainSliderH = (int) (58 * s);
|
||||
const int qH = (int) (18 * s);
|
||||
const int captionH = (int) (18 * s);
|
||||
const int valueH = (int) (15 * s);
|
||||
const int freqH = (int) (20 * s);
|
||||
const int qValueH = (int) (14 * s);
|
||||
const int qKnobH = (int) (58 * s);
|
||||
const int qCapH = (int) (14 * s);
|
||||
|
||||
w.gainLabel->setBounds(col.removeFromTop(topLabelH));
|
||||
w.gainCaption->setBounds(col.removeFromTop(captionH));
|
||||
w.gainValue->setBounds(col.removeFromTop(valueH));
|
||||
|
||||
const int analyserH = col.getHeight() - freqH - gainSliderH - qH - (int)(8*s);
|
||||
// reserve analyser space by subtracting it (faders sit on top of the spectrum)
|
||||
col.removeFromTop(juce::jmax(0, analyserH));
|
||||
// The gain fader fills the large analyser area (spectrum drawn behind it).
|
||||
w.gain->setBounds(col.removeFromTop(col.getHeight()
|
||||
- freqH - qValueH - qKnobH - qCapH - (int)(6*s)));
|
||||
|
||||
w.freqLabel->setBounds(col.removeFromTop(freqH));
|
||||
w.gain->setBounds(col.removeFromTop(gainSliderH));
|
||||
|
||||
juce::Rectangle<int> qArea = col.removeFromBottom(qH);
|
||||
w.q->setBounds(qArea.reduced((int)(12*s), 0));
|
||||
// Q rotary control: value label, knob, caption.
|
||||
w.qValue->setBounds(col.removeFromTop(qValueH));
|
||||
juce::Rectangle<int> knobArea = col.removeFromTop(qKnobH);
|
||||
w.q->setBounds(knobArea.reduced((int)(colW * 0.22f), 0));
|
||||
w.qCaption->setBounds(col.removeFromTop(qCapH));
|
||||
}
|
||||
|
||||
// Master column.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ public:
|
|||
void drawLinearSlider(juce::Graphics&, int x, int y, int width, int height,
|
||||
float sliderPos, float minSliderPos, float maxSliderPos,
|
||||
const juce::Slider::SliderStyle, juce::Slider&) override;
|
||||
void drawRotarySlider(juce::Graphics&, int x, int y, int width, int height,
|
||||
float sliderPosProportional, float rotaryStartAngle,
|
||||
float rotaryEndAngle, juce::Slider&) override;
|
||||
void drawComboBox(juce::Graphics&, int width, int height, bool isButtonDown,
|
||||
int buttonX, int buttonY, int buttonW, int buttonH,
|
||||
juce::ComboBox&) override;
|
||||
|
|
@ -48,13 +51,16 @@ private:
|
|||
|
||||
NeonLookAndFeel lookAndFeel;
|
||||
|
||||
static constexpr int numBands = 5;
|
||||
static constexpr int numBands = 7;
|
||||
struct BandWidget
|
||||
{
|
||||
std::unique_ptr<juce::Slider> gain;
|
||||
std::unique_ptr<juce::Slider> q;
|
||||
std::unique_ptr<juce::Label> freqLabel;
|
||||
std::unique_ptr<juce::Label> gainLabel;
|
||||
std::unique_ptr<juce::Label> gainCaption;
|
||||
std::unique_ptr<juce::Label> gainValue;
|
||||
std::unique_ptr<juce::Label> qCaption;
|
||||
std::unique_ptr<juce::Label> qValue;
|
||||
};
|
||||
BandWidget bands[numBands];
|
||||
|
||||
|
|
|
|||
|
|
@ -24,16 +24,20 @@ juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
|
|||
};
|
||||
|
||||
addGain("gain0", "Low Shelf Gain", 0.0f);
|
||||
addGain("gain1", "Low-Mid Gain", 0.0f);
|
||||
addGain("gain2", "Mid Gain", 0.0f);
|
||||
addGain("gain3", "High-Mid Gain", 0.0f);
|
||||
addGain("gain4", "High Shelf Gain", 0.0f);
|
||||
addGain("gain1", "Low Gain", 0.0f);
|
||||
addGain("gain2", "Low-Mid Gain", 0.0f);
|
||||
addGain("gain3", "Mid Gain", 0.0f);
|
||||
addGain("gain4", "High-Mid Gain", 0.0f);
|
||||
addGain("gain5", "High Gain", 0.0f);
|
||||
addGain("gain6", "High Shelf Gain", 0.0f);
|
||||
|
||||
addQ("q0", "Low Shelf Q", 0.71f);
|
||||
addQ("q1", "Low-Mid Q", 1.0f);
|
||||
addQ("q2", "Mid Q", 1.0f);
|
||||
addQ("q3", "High-Mid Q", 1.0f);
|
||||
addQ("q4", "High Shelf Q", 0.71f);
|
||||
addQ("q1", "Low Q", 1.0f);
|
||||
addQ("q2", "Low-Mid Q", 1.0f);
|
||||
addQ("q3", "Mid Q", 1.0f);
|
||||
addQ("q4", "High-Mid Q", 1.0f);
|
||||
addQ("q5", "High Q", 1.0f);
|
||||
addQ("q6", "High Shelf Q", 0.71f);
|
||||
|
||||
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||
"master", "Master", juce::NormalisableRange<float>(-24.0f, 24.0f, 0.01f), 0.0f));
|
||||
|
|
@ -44,8 +48,8 @@ juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
|
|||
return layout;
|
||||
}
|
||||
|
||||
const char* const bandTypes[5] = { "lowShelf", "peak", "peak", "peak", "highShelf" };
|
||||
const float bandFreqs[5] = { 60.0f, 200.0f, 1000.0f, 4000.0f, 10000.0f };
|
||||
const char* const bandTypes[7] = { "lowShelf", "peak", "peak", "peak", "peak", "peak", "highShelf" };
|
||||
const float bandFreqs[7] = { 60.0f, 160.0f, 400.0f, 800.0f, 1600.0f, 4000.0f, 10000.0f };
|
||||
} // namespace
|
||||
|
||||
GelykEQAudioProcessor::GelykEQAudioProcessor()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public:
|
|||
private:
|
||||
juce::AudioProcessorValueTreeState apvts;
|
||||
|
||||
static constexpr int numBands = 5;
|
||||
static constexpr int numBands = 7;
|
||||
FilterBand bands[numBands];
|
||||
double currentSampleRate = 44100.0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue