5 Commit-ok
v1.0 ... v1.2.2

Szerző SHA1 Üzenet Dátum
hariel1985
9c5b5a3957 v1.2.2: Live spectrum analyzer, makeup gain, drag-and-drop signal chain
- Real-time FFT spectrum analyzer drawn behind EQ curves
- Makeup gain knob (+/- 24 dB) after limiter
- Draggable signal chain panel: reorder Master Gain / Limiter / Makeup Gain
- Chain order saved/restored with DAW session
- Scaled fonts in signal chain panel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:44:27 +01:00
hariel1985
72c7958d98 v1.1.3: Brickwall limiter + double-click reset on all knobs
- Output brickwall limiter (0 dB ceiling) with toggle in master row
- All sliders reset to default on double-click:
  Master gain → 0 dB, Freq → 1000 Hz, Gain → 0 dB, Q → 1.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:06:22 +01:00
hariel1985
2c440d8deb v1.1.2: More FIR latency options, lower default
FIR quality selector now offers 6 choices:
512 (~6ms), 1024 (~12ms), 2048 (~23ms), 4096 (~46ms), 8192 (~93ms), 16384 (~186ms)
Default changed to 2048 (~23ms) for lower latency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:54:57 +01:00
hariel1985
f95c6e4b17 v1.1: Add New Band button and FIR quality selector
- New Band button in header bar for adding EQ nodes
- FIR latency dropdown: 4096 (~46ms), 8192 (~93ms), 16384 (~186ms)
- Version bump to 1.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:37:07 +01:00
hariel1985
6a5f331185 Remove standalone build — no practical use for an EQ plugin
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:25:09 +01:00
14 fájl változott, egészen pontosan 579 új sor hozzáadva és 57 régi sor törölve

Fájl megtekintése

@@ -25,21 +25,12 @@ jobs:
- name: Package VST3 - name: Package VST3
run: Compress-Archive -Path "build/InstaLPEQ_artefacts/Release/VST3/InstaLPEQ.vst3" -DestinationPath "InstaLPEQ-VST3-Win64.zip" run: Compress-Archive -Path "build/InstaLPEQ_artefacts/Release/VST3/InstaLPEQ.vst3" -DestinationPath "InstaLPEQ-VST3-Win64.zip"
- name: Package Standalone
run: Compress-Archive -Path "build/InstaLPEQ_artefacts/Release/Standalone/InstaLPEQ.exe" -DestinationPath "InstaLPEQ-Standalone-Win64.zip"
- name: Upload VST3 - name: Upload VST3
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: InstaLPEQ-VST3-Win64 name: InstaLPEQ-VST3-Win64
path: InstaLPEQ-VST3-Win64.zip path: InstaLPEQ-VST3-Win64.zip
- name: Upload Standalone
uses: actions/upload-artifact@v4
with:
name: InstaLPEQ-Standalone-Win64
path: InstaLPEQ-Standalone-Win64.zip
build-macos: build-macos:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
@@ -62,10 +53,6 @@ jobs:
working-directory: build/InstaLPEQ_artefacts/Release working-directory: build/InstaLPEQ_artefacts/Release
run: zip -r $GITHUB_WORKSPACE/InstaLPEQ-AU-macOS.zip AU/InstaLPEQ.component run: zip -r $GITHUB_WORKSPACE/InstaLPEQ-AU-macOS.zip AU/InstaLPEQ.component
- name: Package Standalone
working-directory: build/InstaLPEQ_artefacts/Release
run: zip -r $GITHUB_WORKSPACE/InstaLPEQ-Standalone-macOS.zip Standalone/InstaLPEQ.app
- name: Upload VST3 - name: Upload VST3
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@@ -78,12 +65,6 @@ jobs:
name: InstaLPEQ-AU-macOS name: InstaLPEQ-AU-macOS
path: InstaLPEQ-AU-macOS.zip path: InstaLPEQ-AU-macOS.zip
- name: Upload Standalone
uses: actions/upload-artifact@v4
with:
name: InstaLPEQ-Standalone-macOS
path: InstaLPEQ-Standalone-macOS.zip
build-linux: build-linux:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
@@ -113,9 +94,6 @@ jobs:
working-directory: build/InstaLPEQ_artefacts/Release working-directory: build/InstaLPEQ_artefacts/Release
run: zip -r $GITHUB_WORKSPACE/InstaLPEQ-LV2-Linux-x64.zip LV2/InstaLPEQ.lv2 run: zip -r $GITHUB_WORKSPACE/InstaLPEQ-LV2-Linux-x64.zip LV2/InstaLPEQ.lv2
- name: Package Standalone
run: zip -j InstaLPEQ-Standalone-Linux-x64.zip build/InstaLPEQ_artefacts/Release/Standalone/InstaLPEQ
- name: Upload VST3 - name: Upload VST3
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@@ -128,12 +106,6 @@ jobs:
name: InstaLPEQ-LV2-Linux-x64 name: InstaLPEQ-LV2-Linux-x64
path: InstaLPEQ-LV2-Linux-x64.zip path: InstaLPEQ-LV2-Linux-x64.zip
- name: Upload Standalone
uses: actions/upload-artifact@v4
with:
name: InstaLPEQ-Standalone-Linux-x64
path: InstaLPEQ-Standalone-Linux-x64.zip
release: release:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
needs: [build-windows, build-macos, build-linux] needs: [build-windows, build-macos, build-linux]
@@ -151,11 +123,8 @@ jobs:
with: with:
files: | files: |
artifacts/InstaLPEQ-VST3-Win64/InstaLPEQ-VST3-Win64.zip artifacts/InstaLPEQ-VST3-Win64/InstaLPEQ-VST3-Win64.zip
artifacts/InstaLPEQ-Standalone-Win64/InstaLPEQ-Standalone-Win64.zip
artifacts/InstaLPEQ-VST3-macOS/InstaLPEQ-VST3-macOS.zip artifacts/InstaLPEQ-VST3-macOS/InstaLPEQ-VST3-macOS.zip
artifacts/InstaLPEQ-AU-macOS/InstaLPEQ-AU-macOS.zip artifacts/InstaLPEQ-AU-macOS/InstaLPEQ-AU-macOS.zip
artifacts/InstaLPEQ-Standalone-macOS/InstaLPEQ-Standalone-macOS.zip
artifacts/InstaLPEQ-VST3-Linux-x64/InstaLPEQ-VST3-Linux-x64.zip artifacts/InstaLPEQ-VST3-Linux-x64/InstaLPEQ-VST3-Linux-x64.zip
artifacts/InstaLPEQ-LV2-Linux-x64/InstaLPEQ-LV2-Linux-x64.zip artifacts/InstaLPEQ-LV2-Linux-x64/InstaLPEQ-LV2-Linux-x64.zip
artifacts/InstaLPEQ-Standalone-Linux-x64/InstaLPEQ-Standalone-Linux-x64.zip
generate_release_notes: true generate_release_notes: true

Fájl megtekintése

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.22) cmake_minimum_required(VERSION 3.22)
project(InstaLPEQ VERSION 1.0.0) project(InstaLPEQ VERSION 1.2.2)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -13,7 +13,7 @@ juce_add_plugin(InstaLPEQ
NEEDS_MIDI_OUTPUT FALSE NEEDS_MIDI_OUTPUT FALSE
PLUGIN_MANUFACTURER_CODE Inst PLUGIN_MANUFACTURER_CODE Inst
PLUGIN_CODE Ilpe PLUGIN_CODE Ilpe
FORMATS VST3 AU LV2 Standalone FORMATS VST3 AU LV2
LV2URI "https://github.com/hariel1985/InstaLPEQ" LV2URI "https://github.com/hariel1985/InstaLPEQ"
PRODUCT_NAME "InstaLPEQ" PRODUCT_NAME "InstaLPEQ"
COPY_PLUGIN_AFTER_BUILD FALSE COPY_PLUGIN_AFTER_BUILD FALSE
@@ -35,6 +35,7 @@ target_sources(InstaLPEQ
Source/EQCurveDisplay.cpp Source/EQCurveDisplay.cpp
Source/FIREngine.cpp Source/FIREngine.cpp
Source/NodeParameterPanel.cpp Source/NodeParameterPanel.cpp
Source/SignalChainPanel.cpp
) )
target_compile_definitions(InstaLPEQ target_compile_definitions(InstaLPEQ

Fájl megtekintése

@@ -1,32 +1,29 @@
# InstaLPEQ # InstaLPEQ
Free, open-source linear phase EQ plugin built with JUCE. Available as VST3, AU, LV2 and Standalone. Free, open-source linear phase EQ plugin built with JUCE. Available as VST3, AU and LV2.
![VST3](https://img.shields.io/badge/format-VST3-blue) ![AU](https://img.shields.io/badge/format-AU-blue) ![LV2](https://img.shields.io/badge/format-LV2-blue) ![C++](https://img.shields.io/badge/language-C%2B%2B17-orange) ![JUCE](https://img.shields.io/badge/framework-JUCE-green) ![License](https://img.shields.io/badge/license-GPL--3.0-lightgrey) ![Build](https://github.com/hariel1985/InstaLPEQ/actions/workflows/build.yml/badge.svg) ![VST3](https://img.shields.io/badge/format-VST3-blue) ![AU](https://img.shields.io/badge/format-AU-blue) ![LV2](https://img.shields.io/badge/format-LV2-blue) ![C++](https://img.shields.io/badge/language-C%2B%2B17-orange) ![JUCE](https://img.shields.io/badge/framework-JUCE-green) ![License](https://img.shields.io/badge/license-GPL--3.0-lightgrey) ![Build](https://github.com/hariel1985/InstaLPEQ/actions/workflows/build.yml/badge.svg)
## Download ## Download
**[Latest Release: v1.0](https://github.com/hariel1985/InstaLPEQ/releases/tag/v1.0)** **[Latest Release: v1.1](https://github.com/hariel1985/InstaLPEQ/releases/tag/v1.1)**
### Windows ### Windows
| File | Description | | File | Description |
|------|-------------| |------|-------------|
| [InstaLPEQ-VST3-Win64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-VST3-Win64.zip) | VST3 plugin — copy to `C:\Program Files\Common Files\VST3\` | | [InstaLPEQ-VST3-Win64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.1/InstaLPEQ-VST3-Win64.zip) | VST3 plugin — copy to `C:\Program Files\Common Files\VST3\` |
| [InstaLPEQ-Standalone-Win64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-Standalone-Win64.zip) | Standalone application |
### macOS (Universal Binary: Apple Silicon + Intel) ### macOS (Universal Binary: Apple Silicon + Intel)
| File | Description | | File | Description |
|------|-------------| |------|-------------|
| [InstaLPEQ-VST3-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-VST3-macOS.zip) | VST3 plugin — copy to `~/Library/Audio/Plug-Ins/VST3/` | | [InstaLPEQ-VST3-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.1/InstaLPEQ-VST3-macOS.zip) | VST3 plugin — copy to `~/Library/Audio/Plug-Ins/VST3/` |
| [InstaLPEQ-AU-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-AU-macOS.zip) | Audio Unit — copy to `~/Library/Audio/Plug-Ins/Components/` | | [InstaLPEQ-AU-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.1/InstaLPEQ-AU-macOS.zip) | Audio Unit — copy to `~/Library/Audio/Plug-Ins/Components/` |
| [InstaLPEQ-Standalone-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-Standalone-macOS.zip) | Standalone application |
### Linux (x64, built on Ubuntu 22.04) ### Linux (x64, built on Ubuntu 22.04)
| File | Description | | File | Description |
|------|-------------| |------|-------------|
| [InstaLPEQ-VST3-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-VST3-Linux-x64.zip) | VST3 plugin — copy to `~/.vst3/` | | [InstaLPEQ-VST3-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.1/InstaLPEQ-VST3-Linux-x64.zip) | VST3 plugin — copy to `~/.vst3/` |
| [InstaLPEQ-LV2-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-LV2-Linux-x64.zip) | LV2 plugin — copy to `~/.lv2/` | | [InstaLPEQ-LV2-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.1/InstaLPEQ-LV2-Linux-x64.zip) | LV2 plugin — copy to `~/.lv2/` |
| [InstaLPEQ-Standalone-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.0/InstaLPEQ-Standalone-Linux-x64.zip) | Standalone application |
> **macOS note:** Builds are Universal Binary (Apple Silicon + Intel). Not code-signed — after copying the plugin, remove the quarantine flag in Terminal: > **macOS note:** Builds are Universal Binary (Apple Silicon + Intel). Not code-signed — after copying the plugin, remove the quarantine flag in Terminal:
> ```bash > ```bash
@@ -107,7 +104,6 @@ Output:
- VST3: `build/InstaLPEQ_artefacts/Release/VST3/InstaLPEQ.vst3` - VST3: `build/InstaLPEQ_artefacts/Release/VST3/InstaLPEQ.vst3`
- AU: `build/InstaLPEQ_artefacts/Release/AU/InstaLPEQ.component` (macOS) - AU: `build/InstaLPEQ_artefacts/Release/AU/InstaLPEQ.component` (macOS)
- LV2: `build/InstaLPEQ_artefacts/Release/LV2/InstaLPEQ.lv2` - LV2: `build/InstaLPEQ_artefacts/Release/LV2/InstaLPEQ.lv2`
- Standalone: `build/InstaLPEQ_artefacts/Release/Standalone/InstaLPEQ.exe`
## How It Works ## How It Works

Fájl megtekintése

@@ -17,6 +17,13 @@ void EQCurveDisplay::setMagnitudeResponse (const std::vector<float>& magnitudesD
repaint(); repaint();
} }
void EQCurveDisplay::setSpectrum (const float* data, int numBins, double sampleRate, int fftSize)
{
spectrumDb.assign (data, data + numBins);
spectrumSampleRate = sampleRate;
spectrumFftSize = fftSize;
}
void EQCurveDisplay::setSelectedBand (int index) void EQCurveDisplay::setSelectedBand (int index)
{ {
if (selectedBand != index) if (selectedBand != index)
@@ -93,6 +100,7 @@ void EQCurveDisplay::paint (juce::Graphics& g)
g.drawRoundedRectangle (bounds, 4.0f, 1.0f); g.drawRoundedRectangle (bounds, 4.0f, 1.0f);
drawGrid (g); drawGrid (g);
drawSpectrum (g);
drawPerBandCurves (g); drawPerBandCurves (g);
drawResponseCurve (g); drawResponseCurve (g);
drawNodes (g); drawNodes (g);
@@ -140,6 +148,55 @@ void EQCurveDisplay::drawGrid (juce::Graphics& g)
} }
} }
void EQCurveDisplay::drawSpectrum (juce::Graphics& g)
{
if (spectrumDb.empty())
return;
auto area = getPlotArea();
int numBins = (int) spectrumDb.size();
juce::Path specPath;
specPath.startNewSubPath (area.getX(), area.getBottom());
bool hasPoints = false;
for (float px = area.getX(); px <= area.getRight(); px += 1.5f)
{
float freq = xToFreq (px);
if (freq < 1.0f || freq > spectrumSampleRate * 0.5)
continue;
float binFloat = freq * (float) spectrumFftSize / (float) spectrumSampleRate;
int bin = (int) binFloat;
float frac = binFloat - (float) bin;
if (bin < 0 || bin >= numBins - 1)
continue;
float dbVal = spectrumDb[bin] * (1.0f - frac) + spectrumDb[bin + 1] * frac;
// Map dB range: -100 dB = bottom, 0 dB = top area
// Shift up so typical audio is visible
float mapped = juce::jmap (dbVal, -80.0f, 0.0f, minDb, maxDb);
mapped = juce::jlimit (minDb - 6.0f, maxDb, mapped);
float yPos = dbToY (mapped);
specPath.lineTo (px, yPos);
hasPoints = true;
}
if (! hasPoints)
return;
specPath.lineTo (area.getRight(), area.getBottom());
specPath.closeSubPath();
// Fill with subtle gradient
juce::ColourGradient specGrad (juce::Colour (0xff4488ff).withAlpha (0.12f), 0, area.getY(),
juce::Colour (0xff4488ff).withAlpha (0.03f), 0, area.getBottom(), false);
g.setGradientFill (specGrad);
g.fillPath (specPath);
}
void EQCurveDisplay::drawResponseCurve (juce::Graphics& g) void EQCurveDisplay::drawResponseCurve (juce::Graphics& g)
{ {
if (magnitudeResponseDb.empty()) if (magnitudeResponseDb.empty())

Fájl megtekintése

@@ -19,6 +19,7 @@ public:
void setListener (Listener* l) { listener = l; } void setListener (Listener* l) { listener = l; }
void setBands (const std::vector<EQBand>& bands); void setBands (const std::vector<EQBand>& bands);
void setMagnitudeResponse (const std::vector<float>& magnitudesDb, double sampleRate, int fftSize); void setMagnitudeResponse (const std::vector<float>& magnitudesDb, double sampleRate, int fftSize);
void setSpectrum (const float* data, int numBins, double sampleRate, int fftSize);
int getSelectedBandIndex() const { return selectedBand; } int getSelectedBandIndex() const { return selectedBand; }
void setSelectedBand (int index); void setSelectedBand (int index);
@@ -32,6 +33,9 @@ public:
private: private:
std::vector<EQBand> bands; std::vector<EQBand> bands;
std::vector<float> magnitudeResponseDb; std::vector<float> magnitudeResponseDb;
std::vector<float> spectrumDb;
double spectrumSampleRate = 44100.0;
int spectrumFftSize = 2048;
double responseSampleRate = 44100.0; double responseSampleRate = 44100.0;
int responseFftSize = 8192; int responseFftSize = 8192;
int selectedBand = -1; int selectedBand = -1;
@@ -62,6 +66,7 @@ private:
float yToDb (float y) const; float yToDb (float y) const;
void drawGrid (juce::Graphics& g); void drawGrid (juce::Graphics& g);
void drawSpectrum (juce::Graphics& g);
void drawResponseCurve (juce::Graphics& g); void drawResponseCurve (juce::Graphics& g);
void drawPerBandCurves (juce::Graphics& g); void drawPerBandCurves (juce::Graphics& g);
void drawNodes (juce::Graphics& g); void drawNodes (juce::Graphics& g);

Fájl megtekintése

@@ -33,7 +33,7 @@ void FIREngine::setBands (const std::vector<EQBand>& newBands)
void FIREngine::setFFTOrder (int order) void FIREngine::setFFTOrder (int order)
{ {
fftOrder.store (juce::jlimit (12, 14, order)); fftOrder.store (juce::jlimit (9, 14, order));
needsUpdate.store (true); needsUpdate.store (true);
notify(); notify();
} }

Fájl megtekintése

@@ -5,7 +5,7 @@
class FIREngine : private juce::Thread class FIREngine : private juce::Thread
{ {
public: public:
static constexpr int defaultFFTOrder = 13; // 8192 taps static constexpr int defaultFFTOrder = 11; // 2048 taps
static constexpr int maxBands = 8; static constexpr int maxBands = 8;
FIREngine(); FIREngine();

Fájl megtekintése

@@ -5,11 +5,14 @@ NodeParameterPanel::NodeParameterPanel()
{ {
setupSlider (freqSlider, freqLabel, 20.0, 20000.0, 1.0, " Hz"); setupSlider (freqSlider, freqLabel, 20.0, 20000.0, 1.0, " Hz");
freqSlider.setSkewFactorFromMidPoint (1000.0); freqSlider.setSkewFactorFromMidPoint (1000.0);
freqSlider.setDoubleClickReturnValue (true, 1000.0);
setupSlider (gainSlider, gainLabel, -24.0, 24.0, 0.1, " dB"); setupSlider (gainSlider, gainLabel, -24.0, 24.0, 0.1, " dB");
gainSlider.setDoubleClickReturnValue (true, 0.0);
setupSlider (qSlider, qLabel, 0.1, 18.0, 0.01, ""); setupSlider (qSlider, qLabel, 0.1, 18.0, 0.01, "");
qSlider.setSkewFactorFromMidPoint (1.0); qSlider.setSkewFactorFromMidPoint (1.0);
qSlider.setDoubleClickReturnValue (true, 1.0);
qSlider.getProperties().set (InstaLPEQLookAndFeel::knobTypeProperty, "dark"); qSlider.getProperties().set (InstaLPEQLookAndFeel::knobTypeProperty, "dark");
typeSelector.addItem ("Peak", 1); typeSelector.addItem ("Peak", 1);

Fájl megtekintése

@@ -23,6 +23,47 @@ InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p)
bypassLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary); bypassLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary);
addAndMakeVisible (bypassLabel); addAndMakeVisible (bypassLabel);
// New Band button
newBandButton.onClick = [this]
{
if (processor.getNumBands() < InstaLPEQProcessor::maxBands)
{
processor.addBand (1000.0f, 0.0f);
syncDisplayFromProcessor();
curveDisplay.setSelectedBand (processor.getNumBands() - 1);
}
};
addAndMakeVisible (newBandButton);
// Quality selector (FIR latency)
qualitySelector.addItem ("512 (~6ms)", 1);
qualitySelector.addItem ("1024 (~12ms)", 2);
qualitySelector.addItem ("2048 (~23ms)", 3);
qualitySelector.addItem ("4096 (~46ms)", 4);
qualitySelector.addItem ("8192 (~93ms)", 5);
qualitySelector.addItem ("16384 (~186ms)", 6);
qualitySelector.setSelectedId (3, juce::dontSendNotification); // default 2048
qualitySelector.onChange = [this]
{
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 // EQ curve
curveDisplay.setListener (this); curveDisplay.setListener (this);
addAndMakeVisible (curveDisplay); addAndMakeVisible (curveDisplay);
@@ -37,11 +78,37 @@ InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p)
masterGainSlider.setRange (-24.0, 24.0, 0.1); masterGainSlider.setRange (-24.0, 24.0, 0.1);
masterGainSlider.setValue (0.0); masterGainSlider.setValue (0.0);
masterGainSlider.setTextValueSuffix (" dB"); masterGainSlider.setTextValueSuffix (" dB");
masterGainSlider.setDoubleClickReturnValue (true, 0.0);
addAndMakeVisible (masterGainSlider); addAndMakeVisible (masterGainSlider);
masterGainLabel.setFont (customLookAndFeel.getMediumFont (13.0f)); masterGainLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
masterGainLabel.setJustificationType (juce::Justification::centred); masterGainLabel.setJustificationType (juce::Justification::centred);
addAndMakeVisible (masterGainLabel); 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);
// Makeup gain
makeupGainSlider.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
makeupGainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 60, 16);
makeupGainSlider.setRange (-24.0, 24.0, 0.1);
makeupGainSlider.setValue (0.0);
makeupGainSlider.setTextValueSuffix (" dB");
makeupGainSlider.setDoubleClickReturnValue (true, 0.0);
addAndMakeVisible (makeupGainSlider);
makeupGainLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
makeupGainLabel.setJustificationType (juce::Justification::centred);
addAndMakeVisible (makeupGainLabel);
// Signal chain panel
chainPanel.setListener (this);
chainPanel.setOrder (processor.getChainOrder());
addAndMakeVisible (chainPanel);
// Sizing // Sizing
constrainer.setMinimumSize (700, 450); constrainer.setMinimumSize (700, 450);
constrainer.setMaximumSize (1920, 1080); constrainer.setMaximumSize (1920, 1080);
@@ -102,18 +169,39 @@ void InstaLPEQEditor::resized()
bypassLabel.setBounds (bypassArea.removeFromLeft (50)); bypassLabel.setBounds (bypassArea.removeFromLeft (50));
bypassToggle.setBounds (bypassArea); bypassToggle.setBounds (bypassArea);
// Bottom master row newBandButton.setBounds (header.removeFromRight ((int) (90 * scale)).reduced (2));
// Signal chain panel (bottom-most)
int chainH = (int) std::max (28.0f, 36.0f * scale);
chainPanel.setBounds (bounds.removeFromBottom (chainH).reduced (pad, 2));
// Master controls row (above chain)
int masterH = (int) std::max (50.0f, 65.0f * scale); int masterH = (int) std::max (50.0f, 65.0f * scale);
auto masterArea = bounds.removeFromBottom (masterH).reduced (pad, 2); auto masterArea = bounds.removeFromBottom (masterH).reduced (pad, 2);
// Divider above master
// (painted in paint())
masterGainLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale))); masterGainLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
auto labelArea = masterArea.removeFromLeft (60); auto labelArea = masterArea.removeFromLeft (60);
masterGainLabel.setBounds (labelArea); masterGainLabel.setBounds (labelArea);
masterGainSlider.setBounds (masterArea.removeFromLeft (masterH)); 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));
// Makeup gain knob
makeupGainLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
makeupGainLabel.setBounds (masterArea.removeFromLeft (55));
makeupGainSlider.setBounds (masterArea.removeFromLeft (masterH));
// 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) // Node parameter panel (15% of remaining height)
int nodePanelH = (int) (bounds.getHeight() * 0.18f); int nodePanelH = (int) (bounds.getHeight() * 0.18f);
auto nodePanelArea = bounds.removeFromBottom (nodePanelH).reduced (pad, 2); auto nodePanelArea = bounds.removeFromBottom (nodePanelH).reduced (pad, 2);
@@ -126,9 +214,19 @@ void InstaLPEQEditor::resized()
void InstaLPEQEditor::timerCallback() void InstaLPEQEditor::timerCallback()
{ {
// Sync bypass // Sync bypass & limiter
processor.bypassed.store (bypassToggle.getToggleState()); processor.bypassed.store (bypassToggle.getToggleState());
processor.masterGainDb.store ((float) masterGainSlider.getValue()); processor.masterGainDb.store ((float) masterGainSlider.getValue());
processor.limiterEnabled.store (limiterToggle.getToggleState());
processor.makeupGainDb.store ((float) makeupGainSlider.getValue());
// Update spectrum analyzer
{
std::array<float, 1024> specData {};
if (processor.getSpectrum (specData.data(), (int) specData.size()))
curveDisplay.setSpectrum (specData.data(), (int) specData.size(),
processor.getCurrentSampleRate(), 2048);
}
// Update display with latest magnitude response // Update display with latest magnitude response
auto magDb = processor.getFIREngine().getMagnitudeResponseDb(); auto magDb = processor.getFIREngine().getMagnitudeResponseDb();
@@ -204,6 +302,11 @@ void InstaLPEQEditor::nodeDeleteRequested (int bandIndex)
syncDisplayFromProcessor(); syncDisplayFromProcessor();
} }
void InstaLPEQEditor::chainOrderChanged (const std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages>& order)
{
processor.setChainOrder (order);
}
void InstaLPEQEditor::syncDisplayFromProcessor() void InstaLPEQEditor::syncDisplayFromProcessor()
{ {
auto currentBands = processor.getBands(); auto currentBands = processor.getBands();

Fájl megtekintése

@@ -4,11 +4,13 @@
#include "LookAndFeel.h" #include "LookAndFeel.h"
#include "EQCurveDisplay.h" #include "EQCurveDisplay.h"
#include "NodeParameterPanel.h" #include "NodeParameterPanel.h"
#include "SignalChainPanel.h"
class InstaLPEQEditor : public juce::AudioProcessorEditor, class InstaLPEQEditor : public juce::AudioProcessorEditor,
private juce::Timer, private juce::Timer,
private EQCurveDisplay::Listener, private EQCurveDisplay::Listener,
private NodeParameterPanel::Listener private NodeParameterPanel::Listener,
private SignalChainPanel::Listener
{ {
public: public:
explicit InstaLPEQEditor (InstaLPEQProcessor& p); explicit InstaLPEQEditor (InstaLPEQProcessor& p);
@@ -30,6 +32,9 @@ private:
void nodeParameterChanged (int bandIndex, const EQBand& band) override; void nodeParameterChanged (int bandIndex, const EQBand& band) override;
void nodeDeleteRequested (int bandIndex) override; void nodeDeleteRequested (int bandIndex) override;
// SignalChainPanel::Listener
void chainOrderChanged (const std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages>& order) override;
void syncDisplayFromProcessor(); void syncDisplayFromProcessor();
InstaLPEQProcessor& processor; InstaLPEQProcessor& processor;
@@ -39,12 +44,23 @@ private:
NodeParameterPanel nodePanel; NodeParameterPanel nodePanel;
juce::Label titleLabel { {}, "INSTALPEQ" }; juce::Label titleLabel { {}, "INSTALPEQ" };
juce::Label versionLabel { {}, "v1.0" }; juce::Label versionLabel { {}, "v1.2.2" };
juce::ToggleButton bypassToggle; juce::ToggleButton bypassToggle;
juce::Label bypassLabel { {}, "BYPASS" }; juce::Label bypassLabel { {}, "BYPASS" };
juce::TextButton newBandButton { "NEW BAND" };
juce::ComboBox qualitySelector;
juce::Label qualityLabel { {}, "FIR" };
juce::Label qualityWarning { {}, "" };
juce::Slider masterGainSlider; juce::Slider masterGainSlider;
juce::Label masterGainLabel { {}, "MASTER" }; juce::Label masterGainLabel { {}, "MASTER" };
juce::ToggleButton limiterToggle;
juce::Label limiterLabel { {}, "LIMITER" };
juce::Slider makeupGainSlider;
juce::Label makeupGainLabel { {}, "MAKEUP" };
SignalChainPanel chainPanel;
juce::ComponentBoundsConstrainer constrainer; juce::ComponentBoundsConstrainer constrainer;

Fájl megtekintése

@@ -28,6 +28,9 @@ void InstaLPEQProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
juce::dsp::ProcessSpec spec { sampleRate, (juce::uint32) samplesPerBlock, 2 }; juce::dsp::ProcessSpec spec { sampleRate, (juce::uint32) samplesPerBlock, 2 };
convolution.prepare (spec); convolution.prepare (spec);
limiter.prepare (spec);
limiter.setThreshold (0.0f);
limiter.setRelease (50.0f);
firEngine.start (sampleRate); firEngine.start (sampleRate);
updateFIR(); updateFIR();
@@ -63,10 +66,81 @@ void InstaLPEQProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::M
juce::dsp::ProcessContextReplacing<float> context (block); juce::dsp::ProcessContextReplacing<float> context (block);
convolution.process (context); convolution.process (context);
// Apply master gain // Apply chain in configured order
float gain = juce::Decibels::decibelsToGain (masterGainDb.load()); std::array<ChainStage, numChainStages> order;
if (std::abs (gain - 1.0f) > 0.001f) {
buffer.applyGain (gain); const juce::SpinLock::ScopedTryLockType lock (chainLock);
if (lock.isLocked())
order = chainOrder;
else
order = { MasterGain, Limiter, MakeupGain };
}
for (auto stage : order)
{
switch (stage)
{
case MasterGain:
{
float gain = juce::Decibels::decibelsToGain (masterGainDb.load());
if (std::abs (gain - 1.0f) > 0.001f)
buffer.applyGain (gain);
break;
}
case Limiter:
{
if (limiterEnabled.load())
{
juce::dsp::AudioBlock<float> limBlock (buffer);
juce::dsp::ProcessContextReplacing<float> limContext (limBlock);
limiter.process (limContext);
}
break;
}
case MakeupGain:
{
float mkGain = juce::Decibels::decibelsToGain (makeupGainDb.load());
if (std::abs (mkGain - 1.0f) > 0.001f)
buffer.applyGain (mkGain);
break;
}
default: break;
}
}
// Feed spectrum analyzer (mono mix of output)
const int numSamples = buffer.getNumSamples();
const int numChannels = buffer.getNumChannels();
for (int i = 0; i < numSamples; ++i)
{
float sample = 0.0f;
for (int ch = 0; ch < numChannels; ++ch)
sample += buffer.getSample (ch, i);
sample /= (float) numChannels;
fifoBuffer[fifoIndex++] = sample;
if (fifoIndex >= spectrumFFTSize)
{
fifoIndex = 0;
std::copy (fifoBuffer.begin(), fifoBuffer.end(), fftData.begin());
std::fill (fftData.begin() + spectrumFFTSize, fftData.end(), 0.0f);
spectrumWindow.multiplyWithWindowingTable (fftData.data(), spectrumFFTSize);
spectrumFFT.performFrequencyOnlyForwardTransform (fftData.data());
{
const juce::SpinLock::ScopedLockType lock (spectrumLock);
for (int b = 0; b < spectrumFFTSize / 2; ++b)
{
float mag = fftData[b] / (float) spectrumFFTSize;
float dbVal = juce::Decibels::gainToDecibels (mag, -100.0f);
// Smooth: 70% old + 30% new
spectrumMagnitude[b] = spectrumMagnitude[b] * 0.7f + dbVal * 0.3f;
}
}
spectrumReady.store (true);
}
}
} }
// ============================================================ // ============================================================
@@ -119,12 +193,45 @@ int InstaLPEQProcessor::getNumBands() const
return (int) bands.size(); return (int) bands.size();
} }
bool InstaLPEQProcessor::getSpectrum (float* dest, int maxBins) const
{
if (! spectrumReady.load())
return false;
const juce::SpinLock::ScopedTryLockType lock (spectrumLock);
if (! lock.isLocked())
return false;
int bins = std::min (maxBins, spectrumFFTSize / 2);
std::copy (spectrumMagnitude.begin(), spectrumMagnitude.begin() + bins, dest);
return true;
}
std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages> InstaLPEQProcessor::getChainOrder() const
{
const juce::SpinLock::ScopedLockType lock (chainLock);
return chainOrder;
}
void InstaLPEQProcessor::setChainOrder (const std::array<ChainStage, numChainStages>& order)
{
const juce::SpinLock::ScopedLockType lock (chainLock);
chainOrder = order;
}
void InstaLPEQProcessor::updateFIR() void InstaLPEQProcessor::updateFIR()
{ {
auto currentBands = getBands(); auto currentBands = getBands();
firEngine.setBands (currentBands); firEngine.setBands (currentBands);
} }
void InstaLPEQProcessor::setQuality (int fftOrder)
{
firEngine.setFFTOrder (fftOrder);
setLatencySamples (firEngine.getLatencySamples());
updateFIR();
}
// ============================================================ // ============================================================
// State save/restore // State save/restore
// ============================================================ // ============================================================
@@ -134,6 +241,17 @@ void InstaLPEQProcessor::getStateInformation (juce::MemoryBlock& destData)
juce::XmlElement xml ("InstaLPEQ"); juce::XmlElement xml ("InstaLPEQ");
xml.setAttribute ("bypass", bypassed.load()); xml.setAttribute ("bypass", bypassed.load());
xml.setAttribute ("masterGain", (double) masterGainDb.load()); xml.setAttribute ("masterGain", (double) masterGainDb.load());
xml.setAttribute ("limiter", limiterEnabled.load());
xml.setAttribute ("makeupGain", (double) makeupGainDb.load());
auto order = getChainOrder();
juce::String chainStr;
for (int i = 0; i < numChainStages; ++i)
{
if (i > 0) chainStr += ",";
chainStr += juce::String ((int) order[i]);
}
xml.setAttribute ("chainOrder", chainStr);
auto currentBands = getBands(); auto currentBands = getBands();
for (int i = 0; i < (int) currentBands.size(); ++i) for (int i = 0; i < (int) currentBands.size(); ++i)
@@ -157,6 +275,18 @@ void InstaLPEQProcessor::setStateInformation (const void* data, int sizeInBytes)
bypassed.store (xml->getBoolAttribute ("bypass", false)); bypassed.store (xml->getBoolAttribute ("bypass", false));
masterGainDb.store ((float) xml->getDoubleAttribute ("masterGain", 0.0)); masterGainDb.store ((float) xml->getDoubleAttribute ("masterGain", 0.0));
limiterEnabled.store (xml->getBoolAttribute ("limiter", true));
makeupGainDb.store ((float) xml->getDoubleAttribute ("makeupGain", 0.0));
auto chainStr = xml->getStringAttribute ("chainOrder", "0,1,2");
auto tokens = juce::StringArray::fromTokens (chainStr, ",", "");
if (tokens.size() == numChainStages)
{
std::array<ChainStage, numChainStages> order;
for (int i = 0; i < numChainStages; ++i)
order[i] = static_cast<ChainStage> (tokens[i].getIntValue());
setChainOrder (order);
}
std::vector<EQBand> loadedBands; std::vector<EQBand> loadedBands;
for (auto* bandXml : xml->getChildIterator()) for (auto* bandXml : xml->getChildIterator())

Fájl megtekintése

@@ -40,9 +40,21 @@ public:
void removeBand (int index); void removeBand (int index);
int getNumBands() const; int getNumBands() const;
// Signal chain stages
enum ChainStage { MasterGain = 0, Limiter, MakeupGain, NumStages };
static constexpr int numChainStages = (int) NumStages;
// Settings // Settings
std::atomic<bool> bypassed { false }; std::atomic<bool> bypassed { false };
std::atomic<float> masterGainDb { 0.0f }; std::atomic<float> masterGainDb { 0.0f };
std::atomic<bool> limiterEnabled { true };
std::atomic<float> makeupGainDb { 0.0f }; // -24 to +24 dB
// Chain order (read/write from GUI, read from audio thread)
std::array<ChainStage, numChainStages> getChainOrder() const;
void setChainOrder (const std::array<ChainStage, numChainStages>& order);
void setQuality (int fftOrder);
FIREngine& getFIREngine() { return firEngine; } FIREngine& getFIREngine() { return firEngine; }
double getCurrentSampleRate() const { return currentSampleRate; } double getCurrentSampleRate() const { return currentSampleRate; }
@@ -53,11 +65,30 @@ private:
FIREngine firEngine; FIREngine firEngine;
juce::dsp::Convolution convolution; juce::dsp::Convolution convolution;
juce::dsp::Limiter<float> limiter;
// Spectrum analyzer
static constexpr int spectrumFFTOrder = 11; // 2048-point FFT
static constexpr int spectrumFFTSize = 1 << spectrumFFTOrder;
juce::dsp::FFT spectrumFFT { spectrumFFTOrder };
juce::dsp::WindowingFunction<float> spectrumWindow { spectrumFFTSize, juce::dsp::WindowingFunction<float>::hann };
std::array<float, spectrumFFTSize> fifoBuffer {};
int fifoIndex = 0;
std::array<float, spectrumFFTSize * 2> fftData {};
std::array<float, spectrumFFTSize / 2> spectrumMagnitude {};
juce::SpinLock spectrumLock;
std::atomic<bool> spectrumReady { false };
public:
bool getSpectrum (float* dest, int maxBins) const;
double currentSampleRate = 44100.0; double currentSampleRate = 44100.0;
int currentBlockSize = 512; int currentBlockSize = 512;
bool firLoaded = false; bool firLoaded = false;
std::array<ChainStage, numChainStages> chainOrder { MasterGain, Limiter, MakeupGain };
juce::SpinLock chainLock;
void updateFIR(); void updateFIR();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InstaLPEQProcessor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InstaLPEQProcessor)

165
Source/SignalChainPanel.cpp Normal file
Fájl megtekintése

@@ -0,0 +1,165 @@
#include "SignalChainPanel.h"
#include "LookAndFeel.h"
SignalChainPanel::SignalChainPanel()
{
setMouseCursor (juce::MouseCursor::DraggingHandCursor);
}
void SignalChainPanel::setOrder (const std::array<InstaLPEQProcessor::ChainStage, numBlocks>& order)
{
if (currentOrder != order)
{
currentOrder = order;
repaint();
}
}
juce::String SignalChainPanel::getStageName (InstaLPEQProcessor::ChainStage stage) const
{
switch (stage)
{
case InstaLPEQProcessor::MasterGain: return "MASTER GAIN";
case InstaLPEQProcessor::Limiter: return "LIMITER";
case InstaLPEQProcessor::MakeupGain: return "MAKEUP GAIN";
default: return "?";
}
}
juce::Colour SignalChainPanel::getStageColour (InstaLPEQProcessor::ChainStage stage) const
{
switch (stage)
{
case InstaLPEQProcessor::MasterGain: return juce::Colour (0xffff8833);
case InstaLPEQProcessor::Limiter: return juce::Colour (0xffff4455);
case InstaLPEQProcessor::MakeupGain: return juce::Colour (0xff44bbff);
default: return InstaLPEQLookAndFeel::textSecondary;
}
}
juce::Rectangle<float> SignalChainPanel::getBlockRect (int index) const
{
auto bounds = getLocalBounds().toFloat().reduced (2);
float gap = 6.0f;
float blockW = (bounds.getWidth() - gap * (numBlocks - 1)) / numBlocks;
float x = bounds.getX() + index * (blockW + gap);
return { x, bounds.getY(), blockW, bounds.getHeight() };
}
int SignalChainPanel::getBlockAtX (float x) const
{
for (int i = 0; i < numBlocks; ++i)
{
if (getBlockRect (i).contains (x, getHeight() * 0.5f))
return i;
}
return -1;
}
void SignalChainPanel::paint (juce::Graphics& g)
{
auto bounds = getLocalBounds().toFloat();
// Background
g.setColour (InstaLPEQLookAndFeel::bgDark.darker (0.3f));
g.fillRoundedRectangle (bounds, 4.0f);
g.setColour (InstaLPEQLookAndFeel::bgLight.withAlpha (0.2f));
g.drawRoundedRectangle (bounds, 4.0f, 1.0f);
auto* lf = dynamic_cast<InstaLPEQLookAndFeel*> (&getLookAndFeel());
// Draw arrows between blocks (scale with height)
float arrowScale = bounds.getHeight() / 30.0f;
for (int i = 0; i < numBlocks - 1; ++i)
{
auto r1 = getBlockRect (i);
auto r2 = getBlockRect (i + 1);
float arrowX = (r1.getRight() + r2.getX()) * 0.5f;
float arrowY = bounds.getCentreY();
float aw = 5.0f * arrowScale;
float ah = 6.0f * arrowScale;
g.setColour (InstaLPEQLookAndFeel::textSecondary.withAlpha (0.5f));
juce::Path arrow;
arrow.addTriangle (arrowX - aw, arrowY - ah, arrowX - aw, arrowY + ah, arrowX + aw, arrowY);
g.fillPath (arrow);
}
// Draw blocks
for (int i = 0; i < numBlocks; ++i)
{
bool isDragged = (i == draggedIndex);
auto rect = getBlockRect (i);
// If this block is being dragged, offset it
if (isDragged)
{
float offset = dragCurrentX - dragOffsetX;
rect = rect.withX (rect.getX() + offset);
}
auto colour = getStageColour (currentOrder[i]);
// Block background
g.setColour (isDragged ? colour.withAlpha (0.25f) : colour.withAlpha (0.12f));
g.fillRoundedRectangle (rect, 4.0f);
// Block border
g.setColour (isDragged ? colour.withAlpha (0.8f) : colour.withAlpha (0.4f));
g.drawRoundedRectangle (rect, 4.0f, isDragged ? 2.0f : 1.0f);
// Label — scale with block height
juce::Font font = lf ? lf->getBoldFont (std::max (12.0f, rect.getHeight() * 0.45f))
: juce::Font (juce::FontOptions (14.0f));
g.setFont (font);
g.setColour (isDragged ? colour : colour.withAlpha (0.8f));
g.drawText (getStageName (currentOrder[i]), rect.reduced (4), juce::Justification::centred, false);
}
// "SIGNAL CHAIN" label on the left
if (lf)
{
g.setFont (lf->getRegularFont (10.0f));
g.setColour (InstaLPEQLookAndFeel::textSecondary.withAlpha (0.5f));
}
}
void SignalChainPanel::resized() {}
void SignalChainPanel::mouseDown (const juce::MouseEvent& e)
{
draggedIndex = getBlockAtX (e.position.x);
if (draggedIndex >= 0)
{
dragOffsetX = e.position.x;
dragCurrentX = e.position.x;
}
}
void SignalChainPanel::mouseDrag (const juce::MouseEvent& e)
{
if (draggedIndex < 0)
return;
dragCurrentX = e.position.x;
// Check if we should swap with a neighbor
int targetIndex = getBlockAtX (e.position.x);
if (targetIndex >= 0 && targetIndex != draggedIndex)
{
std::swap (currentOrder[draggedIndex], currentOrder[targetIndex]);
draggedIndex = targetIndex;
dragOffsetX = e.position.x;
if (listener)
listener->chainOrderChanged (currentOrder);
}
repaint();
}
void SignalChainPanel::mouseUp (const juce::MouseEvent&)
{
draggedIndex = -1;
repaint();
}

46
Source/SignalChainPanel.h Normal file
Fájl megtekintése

@@ -0,0 +1,46 @@
#pragma once
#include <JuceHeader.h>
#include "PluginProcessor.h"
class SignalChainPanel : public juce::Component
{
public:
struct Listener
{
virtual ~Listener() = default;
virtual void chainOrderChanged (const std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages>& order) = 0;
};
SignalChainPanel();
void setListener (Listener* l) { listener = l; }
void setOrder (const std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages>& order);
std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages> getOrder() const { return currentOrder; }
void paint (juce::Graphics& g) override;
void resized() override;
void mouseDown (const juce::MouseEvent& e) override;
void mouseDrag (const juce::MouseEvent& e) override;
void mouseUp (const juce::MouseEvent& e) override;
private:
static constexpr int numBlocks = InstaLPEQProcessor::numChainStages;
std::array<InstaLPEQProcessor::ChainStage, numBlocks> currentOrder {
InstaLPEQProcessor::MasterGain,
InstaLPEQProcessor::Limiter,
InstaLPEQProcessor::MakeupGain
};
int draggedIndex = -1;
float dragOffsetX = 0.0f;
float dragCurrentX = 0.0f;
Listener* listener = nullptr;
juce::Rectangle<float> getBlockRect (int index) const;
int getBlockAtX (float x) const;
juce::String getStageName (InstaLPEQProcessor::ChainStage stage) const;
juce::Colour getStageColour (InstaLPEQProcessor::ChainStage stage) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SignalChainPanel)
};