diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ce789..948670e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.22) -project(InstaLPEQ VERSION 1.1.2) +project(InstaLPEQ VERSION 1.1.3) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/Source/NodeParameterPanel.cpp b/Source/NodeParameterPanel.cpp index 4193357..cd5c76d 100644 --- a/Source/NodeParameterPanel.cpp +++ b/Source/NodeParameterPanel.cpp @@ -5,11 +5,14 @@ NodeParameterPanel::NodeParameterPanel() { setupSlider (freqSlider, freqLabel, 20.0, 20000.0, 1.0, " Hz"); freqSlider.setSkewFactorFromMidPoint (1000.0); + freqSlider.setDoubleClickReturnValue (true, 1000.0); setupSlider (gainSlider, gainLabel, -24.0, 24.0, 0.1, " dB"); + gainSlider.setDoubleClickReturnValue (true, 0.0); setupSlider (qSlider, qLabel, 0.1, 18.0, 0.01, ""); qSlider.setSkewFactorFromMidPoint (1.0); + qSlider.setDoubleClickReturnValue (true, 1.0); qSlider.getProperties().set (InstaLPEQLookAndFeel::knobTypeProperty, "dark"); typeSelector.addItem ("Peak", 1); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 2b0f75a..2b7d0df 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -48,12 +48,22 @@ InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p) int sel = qualitySelector.getSelectedId(); int order = sel + 8; // 1->9, 2->10, 3->11, 4->12, 5->13, 6->14 processor.setQuality (order); + + if (sel <= 2) // 512 or 1024 + qualityWarning.setText ("Low freq accuracy reduced", juce::dontSendNotification); + else + qualityWarning.setText ("", juce::dontSendNotification); }; addAndMakeVisible (qualitySelector); qualityLabel.setFont (customLookAndFeel.getMediumFont (13.0f)); qualityLabel.setJustificationType (juce::Justification::centredRight); addAndMakeVisible (qualityLabel); + qualityWarning.setFont (customLookAndFeel.getRegularFont (11.0f)); + qualityWarning.setColour (juce::Label::textColourId, juce::Colour (0xffff6644)); + qualityWarning.setJustificationType (juce::Justification::centredRight); + addAndMakeVisible (qualityWarning); + // EQ curve curveDisplay.setListener (this); addAndMakeVisible (curveDisplay); @@ -68,11 +78,20 @@ InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p) masterGainSlider.setRange (-24.0, 24.0, 0.1); masterGainSlider.setValue (0.0); masterGainSlider.setTextValueSuffix (" dB"); + masterGainSlider.setDoubleClickReturnValue (true, 0.0); addAndMakeVisible (masterGainSlider); masterGainLabel.setFont (customLookAndFeel.getMediumFont (13.0f)); masterGainLabel.setJustificationType (juce::Justification::centred); addAndMakeVisible (masterGainLabel); + // Limiter toggle + limiterToggle.setToggleState (processor.limiterEnabled.load(), juce::dontSendNotification); + addAndMakeVisible (limiterToggle); + limiterLabel.setFont (customLookAndFeel.getMediumFont (13.0f)); + limiterLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary); + limiterLabel.setJustificationType (juce::Justification::centred); + addAndMakeVisible (limiterLabel); + // Sizing constrainer.setMinimumSize (700, 450); constrainer.setMaximumSize (1920, 1080); @@ -144,11 +163,18 @@ void InstaLPEQEditor::resized() masterGainLabel.setBounds (labelArea); masterGainSlider.setBounds (masterArea.removeFromLeft (masterH)); + // Limiter toggle next to master gain + limiterLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale))); + limiterLabel.setBounds (masterArea.removeFromLeft (55)); + limiterToggle.setBounds (masterArea.removeFromLeft (40)); + // Quality selector on the right side of master row qualityLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale))); auto qLabelArea = masterArea.removeFromRight (30); qualityLabel.setBounds (qLabelArea); qualitySelector.setBounds (masterArea.removeFromRight ((int) (130 * scale)).reduced (2, (masterH - 24) / 2)); + qualityWarning.setFont (customLookAndFeel.getRegularFont (std::max (9.0f, 11.0f * scale))); + qualityWarning.setBounds (masterArea.removeFromRight ((int) (170 * scale))); // Node parameter panel (15% of remaining height) int nodePanelH = (int) (bounds.getHeight() * 0.18f); @@ -162,9 +188,10 @@ void InstaLPEQEditor::resized() void InstaLPEQEditor::timerCallback() { - // Sync bypass + // Sync bypass & limiter processor.bypassed.store (bypassToggle.getToggleState()); processor.masterGainDb.store ((float) masterGainSlider.getValue()); + processor.limiterEnabled.store (limiterToggle.getToggleState()); // Update display with latest magnitude response auto magDb = processor.getFIREngine().getMagnitudeResponseDb(); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index eb524da..133b181 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -39,16 +39,19 @@ private: NodeParameterPanel nodePanel; juce::Label titleLabel { {}, "INSTALPEQ" }; - juce::Label versionLabel { {}, "v1.1.2" }; + juce::Label versionLabel { {}, "v1.1.3" }; juce::ToggleButton bypassToggle; juce::Label bypassLabel { {}, "BYPASS" }; juce::TextButton newBandButton { "NEW BAND" }; juce::ComboBox qualitySelector; juce::Label qualityLabel { {}, "FIR" }; + juce::Label qualityWarning { {}, "" }; juce::Slider masterGainSlider; juce::Label masterGainLabel { {}, "MASTER" }; + juce::ToggleButton limiterToggle; + juce::Label limiterLabel { {}, "LIMITER" }; juce::ComponentBoundsConstrainer constrainer; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index caf64ad..2f8a198 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -28,6 +28,9 @@ void InstaLPEQProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) juce::dsp::ProcessSpec spec { sampleRate, (juce::uint32) samplesPerBlock, 2 }; convolution.prepare (spec); + limiter.prepare (spec); + limiter.setThreshold (0.0f); + limiter.setRelease (50.0f); firEngine.start (sampleRate); updateFIR(); @@ -67,6 +70,14 @@ void InstaLPEQProcessor::processBlock (juce::AudioBuffer& buffer, juce::M float gain = juce::Decibels::decibelsToGain (masterGainDb.load()); if (std::abs (gain - 1.0f) > 0.001f) buffer.applyGain (gain); + + // Brickwall limiter (0 dB ceiling) + if (limiterEnabled.load()) + { + juce::dsp::AudioBlock limBlock (buffer); + juce::dsp::ProcessContextReplacing limContext (limBlock); + limiter.process (limContext); + } } // ============================================================ @@ -141,6 +152,7 @@ void InstaLPEQProcessor::getStateInformation (juce::MemoryBlock& destData) juce::XmlElement xml ("InstaLPEQ"); xml.setAttribute ("bypass", bypassed.load()); xml.setAttribute ("masterGain", (double) masterGainDb.load()); + xml.setAttribute ("limiter", limiterEnabled.load()); auto currentBands = getBands(); for (int i = 0; i < (int) currentBands.size(); ++i) @@ -164,6 +176,7 @@ void InstaLPEQProcessor::setStateInformation (const void* data, int sizeInBytes) bypassed.store (xml->getBoolAttribute ("bypass", false)); masterGainDb.store ((float) xml->getDoubleAttribute ("masterGain", 0.0)); + limiterEnabled.store (xml->getBoolAttribute ("limiter", true)); std::vector loadedBands; for (auto* bandXml : xml->getChildIterator()) diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index e4b2b7c..e8cdc97 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -43,6 +43,7 @@ public: // Settings std::atomic bypassed { false }; std::atomic masterGainDb { 0.0f }; + std::atomic limiterEnabled { true }; void setQuality (int fftOrder); @@ -55,6 +56,7 @@ private: FIREngine firEngine; juce::dsp::Convolution convolution; + juce::dsp::Limiter limiter; double currentSampleRate = 44100.0; int currentBlockSize = 512;