mirror of
https://codeberg.org/armin/pascalcompressor.git
synced 2026-10-11 16:51:56 +02:00
230 lines
No EOL
8.1 KiB
C++
230 lines
No EOL
8.1 KiB
C++
#include "PluginEditor.h"
|
|
#include "BuildInfo.h"
|
|
|
|
namespace pascal
|
|
{
|
|
|
|
void MinimalLookAndFeel::drawRotarySlider (juce::Graphics& g, int x, int y, int width, int height,
|
|
float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
|
|
juce::Slider& slider)
|
|
{
|
|
const auto centre = juce::Point<float> (x + width * 0.5f, y + height * 0.5f);
|
|
const auto radius = juce::jmin (width, height) * 0.5f - 7.0f;
|
|
const auto angle = rotaryStartAngle + (rotaryEndAngle - rotaryStartAngle) * sliderPos;
|
|
|
|
juce::Path trackPath;
|
|
trackPath.addArc (centre.x - radius, centre.y - radius, radius * 2.0f, radius * 2.0f,
|
|
rotaryStartAngle, rotaryEndAngle, true);
|
|
|
|
juce::Path fillPath;
|
|
fillPath.addArc (centre.x - radius, centre.y - radius, radius * 2.0f, radius * 2.0f,
|
|
rotaryStartAngle, angle, true);
|
|
|
|
const auto thickness = juce::jmax (1.0f, radius * 0.10f);
|
|
juce::PathStrokeType stroke (thickness, juce::PathStrokeType::curved, juce::PathStrokeType::rounded);
|
|
|
|
g.setColour (track);
|
|
g.strokePath (trackPath, stroke);
|
|
|
|
g.setColour (slider.isMouseOverOrDragging() ? juce::Colours::white : accent);
|
|
g.strokePath (fillPath, stroke);
|
|
|
|
const auto indicator = centre + juce::Point<float> (std::cos (angle - juce::MathConstants<float>::halfPi),
|
|
std::sin (angle - juce::MathConstants<float>::halfPi)) * radius;
|
|
g.fillEllipse (indicator.x - 2.0f, indicator.y - 2.0f, 4.0f, 4.0f);
|
|
}
|
|
|
|
KnobComponent::KnobComponent (juce::AudioProcessorValueTreeState& apvts,
|
|
const juce::String& paramId,
|
|
const juce::String& displayName,
|
|
float defaultValue,
|
|
juce::LookAndFeel& lnf,
|
|
std::function<juce::String (double)> fmt)
|
|
: format (std::move (fmt))
|
|
{
|
|
slider.setSliderStyle (juce::Slider::RotaryVerticalDrag);
|
|
slider.setRotaryParameters (juce::MathConstants<float>::pi * 1.25f,
|
|
juce::MathConstants<float>::pi * 2.75f,
|
|
true);
|
|
slider.setTextBoxStyle (juce::Slider::NoTextBox, false, 0, 0);
|
|
slider.setLookAndFeel (&lnf);
|
|
slider.setDoubleClickReturnValue (true, defaultValue);
|
|
slider.setWantsKeyboardFocus (true);
|
|
slider.addListener (this);
|
|
addAndMakeVisible (slider);
|
|
|
|
nameLabel.setText (displayName, juce::dontSendNotification);
|
|
nameLabel.setJustificationType (juce::Justification::centred);
|
|
nameLabel.setColour (juce::Label::textColourId, labelColour);
|
|
nameLabel.setColour (juce::Label::backgroundColourId, juce::Colours::transparentBlack);
|
|
nameLabel.setColour (juce::Label::outlineColourId, juce::Colours::transparentBlack);
|
|
addAndMakeVisible (nameLabel);
|
|
|
|
valueLabel.setJustificationType (juce::Justification::centred);
|
|
valueLabel.setColour (juce::Label::textColourId, valueColour);
|
|
valueLabel.setColour (juce::Label::backgroundColourId, juce::Colours::transparentBlack);
|
|
valueLabel.setColour (juce::Label::outlineColourId, juce::Colours::transparentBlack);
|
|
addAndMakeVisible (valueLabel);
|
|
|
|
attachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment> (
|
|
apvts, paramId, slider);
|
|
|
|
valueLabel.setText (format (slider.getValue()), juce::dontSendNotification);
|
|
}
|
|
|
|
void KnobComponent::resized()
|
|
{
|
|
auto bounds = getLocalBounds();
|
|
auto valueArea = bounds.removeFromBottom (18);
|
|
auto nameArea = bounds.removeFromTop (16);
|
|
|
|
const auto square = juce::jmin (bounds.getWidth(), bounds.getHeight());
|
|
nameLabel.setBounds (nameArea);
|
|
slider.setBounds (bounds.withSizeKeepingCentre (square, square));
|
|
valueLabel.setBounds (valueArea);
|
|
}
|
|
|
|
void KnobComponent::sliderValueChanged (juce::Slider* slider)
|
|
{
|
|
valueLabel.setText (format (slider->getValue()), juce::dontSendNotification);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
juce::String buildArch()
|
|
{
|
|
#if defined (__arm64__) || defined (_M_ARM64) || defined (__aarch64__)
|
|
return "arm64";
|
|
#elif defined (__x86_64__) || defined (_M_X64)
|
|
return "x64";
|
|
#else
|
|
return "x86";
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
FooterBar::FooterBar (PascalcompressorAudioProcessor& p)
|
|
: processor (p)
|
|
{
|
|
leftText = juce::String ("Pascalcompressor 1.0.0")
|
|
+ " \u00B7 " + buildArch()
|
|
+ " \u00B7 VST3 + AU"
|
|
+ " \u00B7 built " + juce::String (PASCAL_BUILD_TIME) + " (local)";
|
|
startTimerHz (2);
|
|
}
|
|
|
|
void FooterBar::timerCallback()
|
|
{
|
|
const auto newRight = juce::String (juce::roundToInt (processor.getSampleRate())) + " Hz"
|
|
+ " \u00B7 " + juce::String (processor.getBlockSize()) + " smp";
|
|
if (newRight != rightText)
|
|
{
|
|
rightText = newRight;
|
|
repaint();
|
|
}
|
|
}
|
|
|
|
void FooterBar::paint (juce::Graphics& g)
|
|
{
|
|
g.fillAll (background);
|
|
g.setColour (ruleColour);
|
|
g.fillRect (0, 0, getWidth(), 1);
|
|
|
|
g.setColour (footerColour);
|
|
g.setFont (juce::FontOptions (11.0f));
|
|
|
|
auto bounds = getLocalBounds().reduced (14, 0);
|
|
g.drawText (leftText, bounds, juce::Justification::centredLeft);
|
|
g.drawText (rightText, bounds, juce::Justification::centredRight);
|
|
}
|
|
|
|
PascalcompressorAudioProcessorEditor::PascalcompressorAudioProcessorEditor (PascalcompressorAudioProcessor& p)
|
|
: AudioProcessorEditor (&p),
|
|
processorRef (p),
|
|
footer (p)
|
|
{
|
|
setOpaque (true);
|
|
setResizeLimits (500, 340, 1100, 860);
|
|
setResizable (true, true);
|
|
|
|
struct Spec
|
|
{
|
|
const char* id;
|
|
const char* name;
|
|
float defaultValue;
|
|
std::function<juce::String (double)> format;
|
|
};
|
|
|
|
const Spec specs[] =
|
|
{
|
|
{ "input", "Input", 0.0f,
|
|
[] (double v) { return juce::String::formatted ("%+.1f dB", v); } },
|
|
{ "threshold", "Threshold", -18.0f,
|
|
[] (double v) { return juce::String::formatted ("%.1f dB", v); } },
|
|
{ "ratio", "Ratio", 2.0f,
|
|
[] (double v) { return juce::String::formatted ("%.2f : 1", v); } },
|
|
{ "attack", "Attack", 10.0f,
|
|
[] (double v) { return juce::String::formatted ("%.1f ms", v); } },
|
|
{ "release", "Release", 250.0f,
|
|
[] (double v) { return juce::String::formatted ("%.0f ms", v); } },
|
|
{ "makeup", "Makeup", 0.0f,
|
|
[] (double v) { return juce::String::formatted ("%+.1f dB", v); } },
|
|
{ "drywet", "Dry/Wet", 100.0f,
|
|
[] (double v) { return juce::String (juce::roundToInt (v)) + " %"; } },
|
|
};
|
|
|
|
for (const auto& spec : specs)
|
|
{
|
|
auto* knob = new KnobComponent (processorRef.apvts, spec.id, spec.name,
|
|
spec.defaultValue, lookAndFeel, spec.format);
|
|
knobs.add (knob);
|
|
addAndMakeVisible (knob);
|
|
}
|
|
|
|
addAndMakeVisible (footer);
|
|
ready = true;
|
|
setSize (620, 420);
|
|
}
|
|
|
|
void PascalcompressorAudioProcessorEditor::paint (juce::Graphics& g)
|
|
{
|
|
jassert (ready);
|
|
g.fillAll (background);
|
|
}
|
|
|
|
void PascalcompressorAudioProcessorEditor::resized()
|
|
{
|
|
if (! ready)
|
|
return;
|
|
|
|
auto bounds = getLocalBounds();
|
|
footer.setBounds (bounds.removeFromBottom (24));
|
|
bounds.reduce (24, 16);
|
|
|
|
const auto knobSize = juce::jmin (bounds.getWidth() / 4 - 42,
|
|
(bounds.getHeight() - 20) / 2 - 40,
|
|
120);
|
|
const auto knobHeight = knobSize + 40;
|
|
const auto rowGap = 20;
|
|
const auto rowsTotal = knobHeight * 2 + rowGap;
|
|
const auto rowsTop = bounds.getY() + (bounds.getHeight() - rowsTotal) / 2;
|
|
|
|
const auto xStepTop = bounds.getWidth() / 4;
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
const auto x = bounds.getX() + xStepTop * i + (xStepTop - knobSize) / 2;
|
|
knobs.getUnchecked (i)->setBounds (x, rowsTop, knobSize, knobHeight);
|
|
}
|
|
|
|
const auto xStepBottom = bounds.getWidth() / 3;
|
|
const auto rowY = rowsTop + knobHeight + rowGap;
|
|
for (int i = 4; i < 7; ++i)
|
|
{
|
|
const auto x = bounds.getX() + xStepBottom * (i - 4) + (xStepBottom - knobSize) / 2;
|
|
knobs.getUnchecked (i)->setBounds (x, rowY, knobSize, knobHeight);
|
|
}
|
|
}
|
|
|
|
} |