4 Commit-ok
v1.2.2 ... main

Szerző SHA1 Üzenet Dátum
hariel1985
8652740bcf Fix Package step: skip missing Standalone/AU
Minden ellenőrzés sikeres volt
Build InstaLPEQ / build-macos (push) Successful in 2m23s
Build InstaLPEQ / build-linux (push) Successful in 6m15s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:19:32 +02:00
hariel1985
def8dd4d98 Add Gitea Actions CI/CD workflow
Some checks failed
Build InstaLPEQ / build-macos (push) Failing after 1m47s
Build InstaLPEQ / build-linux (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:14:26 +02:00
hariel1985
35cf01a163 v1.3.2: Log-spaced auto makeup gain for musical accuracy
Auto makeup now evaluates at log-spaced frequencies (equal weight per
octave) instead of linear. Fixes over-compensation on high shelf boosts
where 5k-22kHz dominated the calculation despite having little musical
energy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:57:28 +01:00
hariel1985
aa546c7357 v1.3: Auto makeup gain, spectrum analyzer, FIR normalization, README overhaul
- Auto makeup gain: RMS-based loudness compensation from actual FIR response
- Real-time FFT spectrum analyzer behind EQ curves
- FIR normalization fix: flat settings now produce exact 0 dB passthrough
- Brickwall limiter (0 dB ceiling) with toggle
- Drag-and-drop signal chain reordering
- Low FIR tap count warning for 512/1024
- Double-click reset on all knobs
- Comprehensive README with linear phase EQ explanation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:28:31 +01:00
10 fájl változott, egészen pontosan 366 új sor hozzáadva és 73 régi sor törölve

116
.gitea/workflows/build.yml Normal file
Fájl megtekintése

@@ -0,0 +1,116 @@
name: Build InstaLPEQ
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
env:
PLUGIN_NAME: InstaLPEQ
GITEA_URL: https://1git.eu
GITEA_REPO: hariel/InstaLPEQ
jobs:
build-linux:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-22.04
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
apt-get update
apt-get install -y build-essential cmake git pkg-config \
libasound2-dev libfreetype6-dev libx11-dev libxrandr-dev \
libxcursor-dev libxinerama-dev libwebkit2gtk-4.1-dev \
libcurl4-openssl-dev zip curl
- name: Clone JUCE
run: git clone --depth 1 https://github.com/juce-framework/JUCE.git ../JUCE
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build Release
run: cmake --build build --config Release --parallel $(nproc)
- name: Package
run: |
cd build/${PLUGIN_NAME}_artefacts/Release
zip -r ${GITHUB_WORKSPACE}/${PLUGIN_NAME}-VST3-Linux-x64.zip VST3/${PLUGIN_NAME}.vst3
cd ${GITHUB_WORKSPACE}
[ -f "build/${PLUGIN_NAME}_artefacts/Release/Standalone/${PLUGIN_NAME}" ] && zip -j ${PLUGIN_NAME}-Standalone-Linux-x64.zip build/${PLUGIN_NAME}_artefacts/Release/Standalone/${PLUGIN_NAME} || true
- name: Upload to Gitea Release
if: startsWith(github.ref, 'refs/tags/v')
run: |
TAG=${GITHUB_REF#refs/tags/}
# Find existing release or create new one
RELEASE_ID=$(curl -s "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases/tags/${TAG}" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "None" ] || [ "$RELEASE_ID" = "" ]; then
RELEASE_ID=$(curl -s -X POST "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG}\", \"name\": \"${TAG}\", \"body\": \"${PLUGIN_NAME} ${TAG}\"}" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))")
fi
echo "Release ID: $RELEASE_ID"
# Upload assets
for f in ${PLUGIN_NAME}-*-Linux-*.zip; do
echo "Uploading $f..."
curl -s -X POST "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases/${RELEASE_ID}/assets?name=$(basename $f)" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$f"
done
build-macos:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Clone JUCE
run: git clone --depth 1 https://github.com/juce-framework/JUCE.git ../JUCE || true
- name: Configure CMake (Universal)
run: cmake -B build -G Xcode -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0
- name: Build Release
run: cmake --build build --config Release
- name: Package
run: |
cd build/${PLUGIN_NAME}_artefacts/Release
zip -r ${GITHUB_WORKSPACE}/${PLUGIN_NAME}-VST3-macOS.zip VST3/${PLUGIN_NAME}.vst3
[ -d "AU/${PLUGIN_NAME}.component" ] && zip -r ${GITHUB_WORKSPACE}/${PLUGIN_NAME}-AU-macOS.zip AU/${PLUGIN_NAME}.component || true
cd ${GITHUB_WORKSPACE}
[ -d "build/${PLUGIN_NAME}_artefacts/Release/Standalone" ] && zip -r ${PLUGIN_NAME}-Standalone-macOS.zip build/${PLUGIN_NAME}_artefacts/Release/Standalone/${PLUGIN_NAME}.app || true
- name: Upload to Gitea Release
if: startsWith(github.ref, 'refs/tags/v')
run: |
TAG=${GITHUB_REF#refs/tags/}
# Get release ID (created by linux job)
RELEASE_ID=$(curl -s "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases/tags/${TAG}" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))")
# If release doesn't exist yet, create it
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "None" ]; then
RELEASE_ID=$(curl -s -X POST "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG}\", \"name\": \"${TAG}\", \"body\": \"${PLUGIN_NAME} ${TAG}\"}" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))")
fi
# Upload assets
for f in ${PLUGIN_NAME}-*-macOS*.zip; do
curl -s -X POST "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases/${RELEASE_ID}/assets?name=$(basename $f)" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$f"
done

Fájl megtekintése

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.22)
project(InstaLPEQ VERSION 1.2.2)
project(InstaLPEQ VERSION 1.3.2)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

144
README.md
Fájl megtekintése

@@ -4,26 +4,42 @@ Free, open-source linear phase EQ plugin built with JUCE. Available as VST3, AU
![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)
## Why Linear Phase EQ?
Traditional (minimum phase) EQs alter the **phase** of the signal at the frequencies they boost or cut. This causes:
- **Phase smearing** — transients lose their shape, especially on drums and percussive material
- **Asymmetric waveforms** — the signal before and after the EQ change point don't align in time
- **Coloration** — even subtle EQ moves can change the character of the sound beyond the intended frequency adjustment
A **linear phase EQ** applies the exact same time delay to all frequencies. This means:
- **Zero phase distortion** — the waveform shape is perfectly preserved
- **Pristine transients** — drums, plucks, and attacks stay tight and punchy
- **Transparent tonal shaping** — only the frequency balance changes, nothing else
- **Perfect for mastering** — no cumulative phase artifacts when stacking multiple EQ moves
- **Ideal for parallel processing** — EQ'd and dry signals stay perfectly time-aligned when summed
The trade-off is a small amount of latency (automatically compensated by the DAW), which makes linear phase EQ unsuitable for live monitoring but perfect for mixing and mastering.
## Download
**[Latest Release: v1.1](https://github.com/hariel1985/InstaLPEQ/releases/tag/v1.1)**
**[Latest Release: v1.3](https://github.com/hariel1985/InstaLPEQ/releases/tag/v1.3)**
### Windows
| File | Description |
|------|-------------|
| [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-VST3-Win64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.3/InstaLPEQ-VST3-Win64.zip) | VST3 plugin — copy to `C:\Program Files\Common Files\VST3\` |
### macOS (Universal Binary: Apple Silicon + Intel)
| File | Description |
|------|-------------|
| [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.1/InstaLPEQ-AU-macOS.zip) | Audio Unit — copy to `~/Library/Audio/Plug-Ins/Components/` |
| [InstaLPEQ-VST3-macOS.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.3/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.3/InstaLPEQ-AU-macOS.zip) | Audio Unit — copy to `~/Library/Audio/Plug-Ins/Components/` |
### Linux (x64, built on Ubuntu 22.04)
| File | Description |
|------|-------------|
| [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.1/InstaLPEQ-LV2-Linux-x64.zip) | LV2 plugin — copy to `~/.lv2/` |
| [InstaLPEQ-VST3-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.3/InstaLPEQ-VST3-Linux-x64.zip) | VST3 plugin — copy to `~/.vst3/` |
| [InstaLPEQ-LV2-Linux-x64.zip](https://github.com/hariel1985/InstaLPEQ/releases/download/v1.3/InstaLPEQ-LV2-Linux-x64.zip) | LV2 plugin — copy to `~/.lv2/` |
> **macOS note:** Builds are Universal Binary (Apple Silicon + Intel). Not code-signed — after copying the plugin, remove the quarantine flag in Terminal:
> ```bash
@@ -33,43 +49,97 @@ Free, open-source linear phase EQ plugin built with JUCE. Available as VST3, AU
## Features
### Linear Phase EQ
### Linear Phase EQ Engine
- True linear phase processing using symmetric FIR convolution
- Zero phase distortion at any gain setting
- 8192-tap FIR filter (configurable: 4096 / 8192 / 16384)
- DAW-compensated latency (~93ms at 44.1kHz default)
- Background thread FIR generation — glitch-free parameter changes
- Zero phase distortion — the waveform shape is perfectly preserved at any gain setting
- Mathematically transparent: only magnitude changes, phase stays untouched
- FIR impulse response normalized for unity passthrough (0 dB at flat settings)
- Background thread FIR generation — glitch-free, click-free parameter changes
- DAW-compensated latency for seamless integration
### Configurable FIR Resolution
Six quality levels to balance precision vs. latency:
| Taps | Latency (44.1 kHz) | Best for |
|------|---------------------|----------|
| 512 | ~6 ms | Low-latency monitoring |
| 1024 | ~12 ms | Tracking |
| **2048** | **~23 ms** | **Default — mixing** |
| 4096 | ~46 ms | Detailed work |
| 8192 | ~93 ms | Mastering |
| 16384 | ~186 ms | Maximum precision |
Low tap counts have reduced accuracy below ~100 Hz — a warning is displayed when using 512 or 1024 taps.
### Interactive EQ Curve Display
- Logarithmic frequency axis (20 Hz — 20 kHz)
- Linear gain axis (-24 dB to +24 dB)
- Click to add EQ nodes (up to 8 bands)
- Drag nodes to adjust frequency and gain
- Scroll wheel to adjust Q/bandwidth
- Right-click for band type selection and delete
- Double-click to reset band to 0 dB
- Real-time frequency response curve with glow effect
- Per-band curve overlay
- Click anywhere to add an EQ node (up to 8 bands)
- Drag nodes to adjust frequency and gain in real time
- Scroll wheel over a node to adjust Q/bandwidth
- Right-click a node for band type selection or delete
- Double-click a node to reset it to 0 dB
- Combined frequency response curve with glow effect
- Individual per-band curve overlays (color-coded)
- Real-time FFT spectrum analyzer behind the EQ curves (shows live audio content)
### Band Types
- Peak (parametric)
- Low Shelf
- High Shelf
- **Peak** (parametric) — boost or cut a specific frequency range
- **Low Shelf** — boost or cut everything below a frequency
- **High Shelf** — boost or cut everything above a frequency
### Auto Makeup Gain
- Automatically compensates for the loudness change caused by EQ settings
- Computed from the actual FIR frequency response (not theoretical) — accounts for FIR resolution limits
- RMS-based calculation with linear frequency weighting (matches white noise / broadband signals)
- Toggleable on/off — displays the current compensation value in dB
- Mastering-safe: fixed value based on EQ curve, no signal-dependent gain changes
### Output Limiter
- Brickwall limiter with 0 dB ceiling
- Toggleable on/off
- Prevents clipping when applying large EQ boosts
- 50 ms release time
### Drag-and-Drop Signal Chain
- Reorderable processing chain at the bottom of the GUI
- Three blocks: **Master Gain**, **Limiter**, **Auto Gain**
- Drag blocks to change processing order (e.g., put limiter before or after gain)
- Visual arrows show signal flow direction
- Chain order saved/restored with DAW session
### Controls
- Per-band: Frequency, Gain, Q knobs
- Master gain (+/- 24 dB)
- Bypass toggle
- State save/restore (DAW session recall)
- **Per-band:** Frequency, Gain, Q knobs with 3D metal styling
- **Master Gain:** +/- 24 dB output level control
- **Bypass:** global bypass toggle
- **New Band:** button to add a new EQ node at 1 kHz / 0 dB
- **FIR Quality:** dropdown to select tap count / latency
- All knobs reset to default on double-click
### GUI
- Dark modern UI matching InstaDrums visual style
- 3D metal knobs with glow effects (orange for EQ, blue for Q)
- Dark modern UI with InstaDrums visual style
- 3D metal knobs with multi-layer glow effects (orange for frequency/gain, blue for Q)
- Carbon fiber background texture
- Rajdhani custom font
- Fully resizable window with proportional scaling
- Animated toggle switches
- Rajdhani custom font (embedded)
- Fully resizable window (700x450 — 1920x1080) with proportional scaling
- Animated toggle switches with smooth lerp
- Color-coded EQ bands (8 distinct colors)
- All fonts and UI elements scale with window size
- State save/restore — all settings recalled with DAW session
## How It Works
InstaLPEQ uses a **FIR-based linear phase** approach:
1. Each EQ band's target magnitude response is computed from IIR filter coefficients (Peak, Low Shelf, or High Shelf)
2. All band magnitudes are multiplied together to form the combined target frequency response
3. An inverse FFT converts the magnitude-only spectrum (zero phase) into a symmetric time-domain impulse response
4. A Blackman-Harris window is applied to minimize truncation artifacts
5. The FIR is normalized so a flat spectrum produces exactly 0 dB passthrough
6. The FIR filter is applied via JUCE's efficient FFT-based partitioned `Convolution` engine
7. Auto makeup gain is computed from the actual FIR frequency response (forward FFT of the final filter)
This ensures **mathematically perfect phase linearity** — the only thing that changes is the frequency balance. The original waveform shape, transient character, and stereo image are completely preserved.
## Building
@@ -105,22 +175,10 @@ Output:
- AU: `build/InstaLPEQ_artefacts/Release/AU/InstaLPEQ.component` (macOS)
- LV2: `build/InstaLPEQ_artefacts/Release/LV2/InstaLPEQ.lv2`
## How It Works
InstaLPEQ uses a **FIR-based linear phase** approach:
1. Each EQ band's target magnitude response is computed from IIR filter coefficients (Peak, Low Shelf, or High Shelf)
2. All band magnitudes are multiplied together to form the combined target response
3. An inverse FFT converts the magnitude-only spectrum into a symmetric time-domain impulse response
4. A Blackman-Harris window is applied to minimize truncation artifacts
5. The FIR filter is applied via JUCE's efficient FFT-based `Convolution` engine
This ensures **zero phase distortion** regardless of EQ settings — ideal for mastering, surgical corrections, and transparent tonal shaping.
## Tech Stack
- **Language:** C++17
- **Framework:** JUCE 8
- **Build:** CMake + MSVC / Xcode / GCC
- **Audio DSP:** juce::dsp (FFT, Convolution, IIR coefficient design)
- **Audio DSP:** juce::dsp (FFT, Convolution, IIR coefficient design, Limiter)
- **Font:** Rajdhani (SIL Open Font License)

Fájl megtekintése

@@ -124,11 +124,11 @@ juce::AudioBuffer<float> FIREngine::generateFIR (const std::vector<EQBand>& band
magnitudes[i] *= bandMag[i];
}
// Store magnitude in dB for display
// Store theoretical magnitude in dB for display (from IIR target curve)
{
std::vector<float> magDb (numBins);
for (int i = 0; i < numBins; ++i)
magDb[i] = (float) juce::Decibels::gainToDecibels (magnitudes[i], -60.0);
magDb[i] = (float) juce::Decibels::gainToDecibels ((float) magnitudes[i], -60.0f);
const juce::SpinLock::ScopedLockType lock (magLock);
magnitudeDb = std::move (magDb);
@@ -168,5 +168,94 @@ juce::AudioBuffer<float> FIREngine::generateFIR (const std::vector<EQBand>& band
juce::dsp::WindowingFunction<float> window (fftSize, juce::dsp::WindowingFunction<float>::blackmanHarris);
window.multiplyWithWindowingTable (firData, fftSize);
// Normalize: ensure flat spectrum → unity DC gain
// Without this, IFFT scaling + windowing cause incorrect base level
float dcGain = 0.0f;
for (int i = 0; i < fftSize; ++i)
dcGain += firData[i];
if (std::abs (dcGain) > 1e-6f)
{
float normFactor = 1.0f / dcGain;
for (int i = 0; i < fftSize; ++i)
firData[i] *= normFactor;
}
// Compute auto makeup from the ACTUAL final FIR frequency response
// (includes windowing + normalization effects)
{
std::vector<float> analysisBuf (fftSize * 2, 0.0f);
std::copy (firData, firData + fftSize, analysisBuf.data());
juce::dsp::FFT analysisFft (order);
analysisFft.performRealOnlyForwardTransform (analysisBuf.data());
// Extract actual magnitude from FFT result
// Format: [DC_real, Nyquist_real, bin1_real, bin1_imag, bin2_real, bin2_imag, ...]
// Evaluate at log-spaced frequencies (equal weight per octave)
// This matches musical content much better than linear spacing
// (linear gives 77% weight to 5k-22k where music has little energy)
const int numPoints = 512;
double powerSum = 0.0;
double binRes = sr / (double) fftSize; // Hz per bin
for (int p = 0; p < numPoints; ++p)
{
// Log-spaced 20 Hz — 20 kHz
double freq = 20.0 * std::pow (1000.0, (double) p / (double) (numPoints - 1));
// Interpolate magnitude from FFT bins
double binFloat = freq / binRes;
int bin = (int) binFloat;
double frac = binFloat - (double) bin;
if (bin < 1 || bin >= fftSize / 2 - 1)
continue;
float re0 = analysisBuf[bin * 2];
float im0 = analysisBuf[bin * 2 + 1];
float re1 = analysisBuf[(bin + 1) * 2];
float im1 = analysisBuf[(bin + 1) * 2 + 1];
double mag0 = std::sqrt ((double) (re0 * re0 + im0 * im0));
double mag1 = std::sqrt ((double) (re1 * re1 + im1 * im1));
double mag = mag0 * (1.0 - frac) + mag1 * frac;
powerSum += mag * mag;
}
if (numPoints > 0)
{
double avgPower = powerSum / (double) numPoints;
float rmsGain = (float) std::sqrt (avgPower);
float makeupDb = -20.0f * std::log10 (std::max (rmsGain, 1e-10f));
autoMakeupDb.store (makeupDb);
}
// (magnitudeDb stays as theoretical IIR curve for display)
}
return firBuffer;
}
// A-weighting curve (IEC 61672:2003)
// Returns linear amplitude weighting factor for given frequency
float FIREngine::aWeighting (float f)
{
if (f < 10.0f) return 0.0f;
double f2 = (double) f * (double) f;
double f4 = f2 * f2;
double num = 12194.0 * 12194.0 * f4;
double den = (f2 + 20.6 * 20.6)
* std::sqrt ((f2 + 107.7 * 107.7) * (f2 + 737.9 * 737.9))
* (f2 + 12194.0 * 12194.0);
double ra = num / den;
// Normalize so A(1000 Hz) = 1.0
// A(1000) unnormalized ≈ 0.7943
static const double norm = 1.0 / 0.7943282347;
return (float) (ra * norm);
}

Fájl megtekintése

@@ -27,6 +27,9 @@ public:
int getFIRLength() const { return 1 << fftOrder.load(); }
int getLatencySamples() const { return getFIRLength() / 2; }
// Auto makeup gain: A-weighted RMS loudness compensation (dB)
float getAutoMakeupGainDb() const { return autoMakeupDb.load(); }
private:
void run() override;
juce::AudioBuffer<float> generateFIR (const std::vector<EQBand>& bands, double sr, int order);
@@ -43,4 +46,7 @@ private:
std::vector<float> magnitudeDb;
mutable juce::SpinLock magLock;
std::atomic<float> autoMakeupDb { 0.0f };
static float aWeighting (float freq);
};

Fájl megtekintése

@@ -92,17 +92,17 @@ InstaLPEQEditor::InstaLPEQEditor (InstaLPEQProcessor& p)
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);
// Auto makeup gain
autoMakeupToggle.setToggleState (processor.autoMakeupEnabled.load(), juce::dontSendNotification);
addAndMakeVisible (autoMakeupToggle);
autoMakeupLabel.setFont (customLookAndFeel.getMediumFont (13.0f));
autoMakeupLabel.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::textSecondary);
autoMakeupLabel.setJustificationType (juce::Justification::centred);
addAndMakeVisible (autoMakeupLabel);
autoMakeupValue.setFont (customLookAndFeel.getRegularFont (12.0f));
autoMakeupValue.setColour (juce::Label::textColourId, InstaLPEQLookAndFeel::accent);
autoMakeupValue.setJustificationType (juce::Justification::centred);
addAndMakeVisible (autoMakeupValue);
// Signal chain panel
chainPanel.setListener (this);
@@ -189,10 +189,12 @@ void InstaLPEQEditor::resized()
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));
// Auto makeup gain toggle + value display
autoMakeupLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
autoMakeupLabel.setBounds (masterArea.removeFromLeft (70));
autoMakeupToggle.setBounds (masterArea.removeFromLeft (40));
autoMakeupValue.setFont (customLookAndFeel.getRegularFont (std::max (10.0f, 12.0f * scale)));
autoMakeupValue.setBounds (masterArea.removeFromLeft (60));
// Quality selector on the right side of master row
qualityLabel.setFont (customLookAndFeel.getMediumFont (std::max (11.0f, 14.0f * scale)));
@@ -218,7 +220,12 @@ void InstaLPEQEditor::timerCallback()
processor.bypassed.store (bypassToggle.getToggleState());
processor.masterGainDb.store ((float) masterGainSlider.getValue());
processor.limiterEnabled.store (limiterToggle.getToggleState());
processor.makeupGainDb.store ((float) makeupGainSlider.getValue());
processor.autoMakeupEnabled.store (autoMakeupToggle.getToggleState());
// Update auto makeup display
float mkDb = processor.getActiveAutoMakeupDb();
juce::String mkStr = (mkDb >= 0 ? "+" : "") + juce::String (mkDb, 1) + " dB";
autoMakeupValue.setText (mkStr, juce::dontSendNotification);
// Update spectrum analyzer
{

Fájl megtekintése

@@ -44,7 +44,7 @@ private:
NodeParameterPanel nodePanel;
juce::Label titleLabel { {}, "INSTALPEQ" };
juce::Label versionLabel { {}, "v1.2.2" };
juce::Label versionLabel { {}, "v1.3.2" };
juce::ToggleButton bypassToggle;
juce::Label bypassLabel { {}, "BYPASS" };
@@ -57,8 +57,9 @@ private:
juce::Label masterGainLabel { {}, "MASTER" };
juce::ToggleButton limiterToggle;
juce::Label limiterLabel { {}, "LIMITER" };
juce::Slider makeupGainSlider;
juce::Label makeupGainLabel { {}, "MAKEUP" };
juce::ToggleButton autoMakeupToggle;
juce::Label autoMakeupLabel { {}, "AUTO GAIN" };
juce::Label autoMakeupValue { {}, "0.0 dB" };
SignalChainPanel chainPanel;

Fájl megtekintése

@@ -61,7 +61,7 @@ void InstaLPEQProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::M
if (bypassed.load() || ! firLoaded)
return;
// Process through convolution
// Process through convolution (EQ)
juce::dsp::AudioBlock<float> block (buffer);
juce::dsp::ProcessContextReplacing<float> context (block);
convolution.process (context);
@@ -99,9 +99,12 @@ void InstaLPEQProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::M
}
case MakeupGain:
{
float mkGain = juce::Decibels::decibelsToGain (makeupGainDb.load());
if (autoMakeupEnabled.load())
{
float mkGain = juce::Decibels::decibelsToGain (firEngine.getAutoMakeupGainDb());
if (std::abs (mkGain - 1.0f) > 0.001f)
buffer.applyGain (mkGain);
}
break;
}
default: break;
@@ -207,6 +210,11 @@ bool InstaLPEQProcessor::getSpectrum (float* dest, int maxBins) const
return true;
}
float InstaLPEQProcessor::getActiveAutoMakeupDb() const
{
return autoMakeupEnabled.load() ? firEngine.getAutoMakeupGainDb() : 0.0f;
}
std::array<InstaLPEQProcessor::ChainStage, InstaLPEQProcessor::numChainStages> InstaLPEQProcessor::getChainOrder() const
{
const juce::SpinLock::ScopedLockType lock (chainLock);
@@ -242,7 +250,7 @@ void InstaLPEQProcessor::getStateInformation (juce::MemoryBlock& destData)
xml.setAttribute ("bypass", bypassed.load());
xml.setAttribute ("masterGain", (double) masterGainDb.load());
xml.setAttribute ("limiter", limiterEnabled.load());
xml.setAttribute ("makeupGain", (double) makeupGainDb.load());
xml.setAttribute ("autoMakeup", autoMakeupEnabled.load());
auto order = getChainOrder();
juce::String chainStr;
@@ -276,7 +284,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));
makeupGainDb.store ((float) xml->getDoubleAttribute ("makeupGain", 0.0));
autoMakeupEnabled.store (xml->getBoolAttribute ("autoMakeup", true));
auto chainStr = xml->getStringAttribute ("chainOrder", "0,1,2");
auto tokens = juce::StringArray::fromTokens (chainStr, ",", "");

Fájl megtekintése

@@ -48,7 +48,10 @@ public:
std::atomic<bool> bypassed { false };
std::atomic<float> masterGainDb { 0.0f };
std::atomic<bool> limiterEnabled { true };
std::atomic<float> makeupGainDb { 0.0f }; // -24 to +24 dB
std::atomic<bool> autoMakeupEnabled { true };
float getActiveAutoMakeupDb() const;
float getMeasuredAutoMakeupDb() const { return measuredMakeupDb.load(); }
// Chain order (read/write from GUI, read from audio thread)
std::array<ChainStage, numChainStages> getChainOrder() const;
@@ -86,6 +89,11 @@ public:
int currentBlockSize = 512;
bool firLoaded = false;
// Signal-based auto makeup measurement
double smoothedInputRms = 0.0;
double smoothedOutputRms = 0.0;
std::atomic<float> measuredMakeupDb { 0.0f };
std::array<ChainStage, numChainStages> chainOrder { MasterGain, Limiter, MakeupGain };
juce::SpinLock chainLock;

Fájl megtekintése

@@ -21,7 +21,7 @@ juce::String SignalChainPanel::getStageName (InstaLPEQProcessor::ChainStage stag
{
case InstaLPEQProcessor::MasterGain: return "MASTER GAIN";
case InstaLPEQProcessor::Limiter: return "LIMITER";
case InstaLPEQProcessor::MakeupGain: return "MAKEUP GAIN";
case InstaLPEQProcessor::MakeupGain: return "AUTO GAIN";
default: return "?";
}
}