mirror of
https://codeberg.org/armin/gelyk.git
synced 2026-10-11 17:01:53 +02:00
Fix live waveform scaling, drop GTK deps, and document the project
- Tune the live spectrum display to a fixed range matched to real per-FFT-bin levels (-40..0 dB) so typical content sits mid-screen instead of flying to the top or hugging the bottom; widen the SENS offset control to -48..+24 dB - Disable JUCE_WEB_BROWSER so the WebKitGTK WebBrowserComponent (unused) is compiled out, letting the plugin build on Artix/Arch without GTK/WebKit dev headers - Add a verbose README covering the bands, parameters, controls, structure, DSP, build steps, and the Linux/Artix note
This commit is contained in:
parent
6e723ee565
commit
585f8d43dd
4 changed files with 183 additions and 9 deletions
|
|
@ -102,6 +102,12 @@ target_link_libraries(GelykEQ PRIVATE juce::juce_dsp)
|
||||||
|
|
||||||
target_compile_definitions(GelykEQ PRIVATE JUCE_MODULE_AVAILABLE_juce_dsp=1)
|
target_compile_definitions(GelykEQ PRIVATE JUCE_MODULE_AVAILABLE_juce_dsp=1)
|
||||||
|
|
||||||
|
# Gelyk never uses the embedded web browser, so keep JUCE_WEB_BROWSER off.
|
||||||
|
# Otherwise juce_gui_extra's WebBrowserComponent pulls in WebKitGTK + GTK dev
|
||||||
|
# headers (webkit2gtk, gtk, jsc, libsoup), which would break headless builds
|
||||||
|
# like Arch/Artix where those aren't installed.
|
||||||
|
target_compile_definitions(GelykEQ PRIVATE JUCE_WEB_BROWSER=0)
|
||||||
|
|
||||||
target_compile_options(GelykEQ PRIVATE
|
target_compile_options(GelykEQ PRIVATE
|
||||||
$<$<CXX_COMPILER_ID:AppleClang,Clang>:-Wall -Wextra>
|
$<$<CXX_COMPILER_ID:AppleClang,Clang>:-Wall -Wextra>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
165
README.md
165
README.md
|
|
@ -1 +1,166 @@
|
||||||
|
# Gelyk EQ
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
A minimalist **7-band parametric equalizer** with a cyber/neon UI, built on
|
||||||
|
[JUCE](https://juce.com) (JUCE 8). It ships as an **AU** and **VST3** plugin.
|
||||||
|
|
||||||
|
This README is intentionally verbose: it covers what the plugin *is*, how it is
|
||||||
|
structured, how every band behaves, and how to build it from source on any
|
||||||
|
platform — including Linux/Artix without the WebKitGTK/GTK developer headers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
**Gelyk EQ** is an audio effects plugin for mixing and mastering. It provides
|
||||||
|
seven independent EQ bands covering the full ~20 Hz – 20 kHz spectrum, plus a
|
||||||
|
master output gain, wrapped in a dark, glowing "neon" interface.
|
||||||
|
|
||||||
|
Rather than a dense wall of identical knobs, each band is laid out as a large
|
||||||
|
**drag-to-shape fader** (the "gain curve") in a shared visualiser. Dragging
|
||||||
|
gives you one-glance sight of the summed response you are actually shaping.
|
||||||
|
|
||||||
|
### The band layout
|
||||||
|
|
||||||
|
Seven filters run in series, logarithmically spaced across the spectrum:
|
||||||
|
|
||||||
|
| Band | Filter type | Default frequency | Default Q |
|
||||||
|
|------|------------------|-------------------|-----------|
|
||||||
|
| 0 | Low shelf | 60 Hz | 0.71 |
|
||||||
|
| 1 | Peak/parametric | 160 Hz | 1.0 |
|
||||||
|
| 2 | Peak/parametric | 400 Hz | 1.0 |
|
||||||
|
| 3 | Peak/parametric | 800 Hz | 1.0 |
|
||||||
|
| 4 | Peak/parametric | 1.6 kHz | 1.0 |
|
||||||
|
| 5 | Peak/parametric | 4 kHz | 1.0 |
|
||||||
|
| 6 | High shelf | 10 kHz | 0.71 |
|
||||||
|
|
||||||
|
### Parameter ranges
|
||||||
|
|
||||||
|
| Parameter | Range | Default |
|
||||||
|
|----------------|------------------|---------|
|
||||||
|
| Gain (per band)| −24 … +24 dB | 0 dB |
|
||||||
|
| Frequency | 20 … 20,000 Hz | per band (skewed, centre 1 kHz) |
|
||||||
|
| Q (per band) | 0.1 … 6.0 | per band |
|
||||||
|
| Master | −24 … +24 dB | 0 dB |
|
||||||
|
|
||||||
|
All parameters are smoothed per-block to avoid zipper noise, and remember their
|
||||||
|
state between sessions via a JUCE `AudioProcessorValueTreeState`.
|
||||||
|
|
||||||
|
### On-screen controls
|
||||||
|
|
||||||
|
- **The visualiser** — the main field shows:
|
||||||
|
- the **live input spectrum** (a realtime FFT analyser), drawn as a glowing,
|
||||||
|
filled waveform;
|
||||||
|
- the **static summed EQ curve** (the actual filter response), overlaid on the
|
||||||
|
same log-frequency axis so peaks line up with their bands;
|
||||||
|
- the neon **0 dB centre line** and reference grid.
|
||||||
|
- **Per-band faders** — drag a band vertically to change its gain, horizontally
|
||||||
|
to move its frequency, or use the mouse wheel to nudge its Q. The knobs and
|
||||||
|
numeric readouts below each fader are permanently linked to it.
|
||||||
|
- **LEVEL / FREQ / Q knobs** — an alternative, precise way to edit the currently
|
||||||
|
hovered band.
|
||||||
|
- **MASTER fader** — output trim.
|
||||||
|
- **SENS slider** — vertical offset for the live spectrum trace (how visually
|
||||||
|
"hot" the analyser appears; the display is pre-tuned to real per-FFT-bin
|
||||||
|
levels, so it reads meaningfully without touching this).
|
||||||
|
- **WAVE slider** — opacity of the live spectrum waveform.
|
||||||
|
- **HUE / SAT / BRI sliders** — recolor the entire palette (colour-shift, global
|
||||||
|
saturation and brightness) for a custom neon look.
|
||||||
|
- **SCALE selector** — fractional UI scaling for high-DPI or small screens.
|
||||||
|
- A **status bar** shows the build version and, when the plugin is built from a
|
||||||
|
git checkout, the current branch/commit and dirty state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Controls at a glance
|
||||||
|
|
||||||
|
| Control | Mouse down / drag | Mouse wheel |
|
||||||
|
|---------------|--------------------------|--------------------------|
|
||||||
|
| Band fader | Vertical = gain | Adjusts band Q |
|
||||||
|
| | Horizontal = frequency | |
|
||||||
|
| Per-band knobs| Rotate = value | — |
|
||||||
|
| Master fader | Vertical = output gain | — |
|
||||||
|
|
||||||
|
Double-click any knob or fader to reset it to its default value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Source/
|
||||||
|
PluginProcessor.h/cpp AudioProcessor, parameter layout, state + audio pipeline
|
||||||
|
PluginEditor.h/cpp The neon UI: visualiser, faders, knobs, color controls
|
||||||
|
EqualizerDSP.h/cpp FilterBand IIR biquad + the background SpectrumAnalyser (FFT)
|
||||||
|
GitInfo.h.in Template for the git build-info header, filled by CMake
|
||||||
|
CMakeLists.txt Build configuration (JUCE 8, AU + VST3, CMake 3.15+)
|
||||||
|
Makefile Convenience wrapper: build, install, codesign
|
||||||
|
```
|
||||||
|
|
||||||
|
### DSP highlights (`EqualizerDSP`)
|
||||||
|
|
||||||
|
Each `FilterBand` is a set of JUCE IIR biquad coefficients, recalculated
|
||||||
|
cheaply whenever gain/frequency/Q change and continuously smoothed toward the
|
||||||
|
target. The channels run stereo-locked so the summed response is identical on
|
||||||
|
left and right.
|
||||||
|
|
||||||
|
The **`SpectrumAnalyser`** runs on a background thread: it fills a circular ring
|
||||||
|
buffer from the input, and periodically Hann-windows the most recent 4096
|
||||||
|
samples and runs a realtime FFT to produce the magnitude spectrum the editor
|
||||||
|
renders (aggregated into log-spaced bins from 20 Hz to 20 kHz).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **CMake 3.15+** and a C++17 compiler (Clang, GCC, or MSVC).
|
||||||
|
- [JUCE 8.0.7](https://github.com/juce-framework/JUCE) — fetched automatically
|
||||||
|
by CMake via `FetchContent` when `JUCE_ROOT` is not set. To reuse an existing
|
||||||
|
checkout, configure with `-DJUCE_ROOT=/path/to/JUCE`.
|
||||||
|
|
||||||
|
### Configure & build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build --config Release -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugins are placed in `build/GelykEQ_artefacts/<config>/AU` (and `VST3`).
|
||||||
|
|
||||||
|
On macOS the bundled `Makefile` also installs both formats into your user plugin
|
||||||
|
folders and refreshes the system registry:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make # configure + build
|
||||||
|
make install # copy into ~/Library/Audio/Plug-Ins and codesign
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux / Artix note (no GTK or WebKitGTK headers required)
|
||||||
|
|
||||||
|
The plugin **does not depend on GTK or WebKitGTK developer headers**, so it
|
||||||
|
builds cleanly on minimal/headless-flavored distros such as **Artix** — no
|
||||||
|
`webkit2gtk`, `gtk3`-devel, `libsoup` or `jsc` packages needed.
|
||||||
|
|
||||||
|
This is guaranteed in `CMakeLists.txt`:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
target_compile_definitions(GelykEQ PRIVATE JUCE_WEB_BROWSER=0)
|
||||||
|
```
|
||||||
|
|
||||||
|
JUCE's Linux `FileChooser` in JUCE 8 no longer links GTK either — it shells out
|
||||||
|
to `zenity`/`kdialog` at runtime, and Gelyk never opens a native file dialog.
|
||||||
|
Disabling `JUCE_WEB_BROWSER` removes the WebKitGTK-backed `WebBrowserComponent`
|
||||||
|
(which Gelyk does not use), eliminating the entire GTK/WebKit include chain.
|
||||||
|
|
||||||
|
> If you ever do need native file dialogs at runtime (outside Gelyk), install
|
||||||
|
> `zenity` or `kdialog`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See the JUCE license for the framework; the Gelyk EQ source follows the same
|
||||||
|
licensing model. (Adjust per your chosen licensing.)
|
||||||
|
|
@ -298,7 +298,7 @@ GelykEQAudioProcessorEditor::GelykEQAudioProcessorEditor(GelykEQAudioProcessor&
|
||||||
double min, max, def;
|
double min, max, def;
|
||||||
const char* tip;
|
const char* tip;
|
||||||
} colorControls[] = {
|
} colorControls[] = {
|
||||||
{ &waveSensLabel, &waveSensSlider, "SENS", -12.0, 12.0, 0.0, "Live waveform sensitivity (dB)" },
|
{ &waveSensLabel, &waveSensSlider, "SENS", -48.0, 24.0, 0.0, "Live waveform sensitivity (dB offset)" },
|
||||||
{ &colorHueLabel, &colorHueSlider, "HUE", -0.5, 0.5, 0.0, "Master colour hue shift" },
|
{ &colorHueLabel, &colorHueSlider, "HUE", -0.5, 0.5, 0.0, "Master colour hue shift" },
|
||||||
{ &colorSatLabel, &colorSatSlider, "SAT", 0.0, 1.0, 1.0, "Master colour saturation" },
|
{ &colorSatLabel, &colorSatSlider, "SAT", 0.0, 1.0, 1.0, "Master colour saturation" },
|
||||||
{ &colorBriLabel, &colorBriSlider, "BRI", 0.5, 1.5, 1.0, "Master colour brightness" },
|
{ &colorBriLabel, &colorBriSlider, "BRI", 0.5, 1.5, 1.0, "Master colour brightness" },
|
||||||
|
|
@ -801,21 +801,24 @@ void GelykEQAudioProcessorEditor::paint(juce::Graphics& g)
|
||||||
g.setColour(lookAndFeel.cyan().withAlpha(0.55f));
|
g.setColour(lookAndFeel.cyan().withAlpha(0.55f));
|
||||||
g.drawHorizontalLine((int) centreLineY, (float) mainArea.getX(), (float) mainArea.getRight());
|
g.drawHorizontalLine((int) centreLineY, (float) mainArea.getX(), (float) mainArea.getRight());
|
||||||
|
|
||||||
// Spectrum overlay.
|
// Spectrum overlay. The live waveform uses a fixed range tuned to real
|
||||||
|
// per-FFT-bin levels: a single loud bin reads ~0 dB at the top edge, typical
|
||||||
|
// content (~-20 dB) sits mid-screen, and -40 dB is the floor. SENS shifts the
|
||||||
|
// whole trace up/down as a dB offset.
|
||||||
if (spectrum.size() > 2)
|
if (spectrum.size() > 2)
|
||||||
{
|
{
|
||||||
const float waveAlpha = lookAndFeel.waveformOpacity;
|
const float waveAlpha = lookAndFeel.waveformOpacity;
|
||||||
const float sensFactor = std::pow(10.0f, lookAndFeel.waveSensitivity / 20.0f);
|
const float sensDb = lookAndFeel.waveSensitivity; // dB offset applied to the trace
|
||||||
|
const float dbMax = 0.0f; // full-scale bin → top of the plot
|
||||||
|
const float dbMin = -40.0f; // silence floor → bottom of the plot
|
||||||
juce::Path path;
|
juce::Path path;
|
||||||
const float x0 = (float) mainArea.getX();
|
const float x0 = (float) mainArea.getX();
|
||||||
const float width = (float) mainArea.getWidth();
|
const float width = (float) mainArea.getWidth();
|
||||||
|
|
||||||
for (int i = 0; i < spectrum.size(); ++i)
|
for (int i = 0; i < spectrum.size(); ++i)
|
||||||
{
|
{
|
||||||
float db = juce::Decibels::gainToDecibels(
|
float db = juce::jlimit(dbMin, dbMax, spectrum[i] + sensDb);
|
||||||
juce::Decibels::decibelsToGain(spectrum[i]) * sensFactor);
|
float norm = (db - dbMin) / (dbMax - dbMin); // 0..1 up
|
||||||
db = juce::jlimit(-72.0f, 12.0f, db);
|
|
||||||
float norm = (db + 72.0f) / 84.0f; // 0..1 up
|
|
||||||
float xx = (i < freqBins.size())
|
float xx = (i < freqBins.size())
|
||||||
? bandFreqToX(freqBins[i], x0, width)
|
? bandFreqToX(freqBins[i], x0, width)
|
||||||
: x0 + (float) i / (float) (spectrum.size() - 1) * width;
|
: x0 + (float) i / (float) (spectrum.size() - 1) * width;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public:
|
||||||
float saturation = 1.0f;
|
float saturation = 1.0f;
|
||||||
float brightness = 1.0f;
|
float brightness = 1.0f;
|
||||||
float waveformOpacity = 0.2f;
|
float waveformOpacity = 0.2f;
|
||||||
float waveSensitivity = 0.0f; // in dB, added to the spectrum display level
|
float waveSensitivity = 0.0f; // in dB, vertical offset added to the live waveform trace
|
||||||
float uiScale = 1.0f;
|
float uiScale = 1.0f;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue