chore: land janitor round 1 and graft integration

Round 1 findings and fixes, all previously uncommitted:
- FXProcessor per-slot DSP history repair (HEAD does not compile without it)
- SynthVoice::getModulationValue, editor control-lock guard
- ExactCompare.h naming the deliberate exact float comparisons
- KnobLookAndFeel replacing the never-installed SerumLookAndFeel
- 55 first-party compiler warnings to 0
- AGENT.md renamed to AGENTS.md across Source/
- graft repository context graph and its hooks
- JANITOR_TASKS.md and UI_UX_AUDIT.md records
This commit is contained in:
biggy committed 2026-10-06 08:50:45 +02:00
1 parent 5c942cad0c
commit e4cea300db
63 files changed
+2360 -805

No files matched your search

+54 -45
View File
@@ -1,70 +1,76 @@
# SerumAlt
A modern, commercial-quality wavetable synthesiser plugin (VST3 / AU / Standalone)
built with **JUCE 7**, **CMake** and **C++17**. SerumAlt is a self-contained synth
inspired by the workflow of Serum/Vital: two morphing wavetable oscillators, a full
filter section, four envelopes and LFOs, a modulation matrix, a reorderable FX rack,
four macros and a one-click **RAVE** button.
A wavetable synthesiser experiment built with JUCE 7, CMake and C++17. It targets
VST3, AU and Standalone formats, with a workflow inspired by Serum and Vital.
This is a fun proof of concept, not a commercial release. Development favors
working builds and hands-on listening over elaborate test infrastructure.
![SerumAlt](Source/Resources/logo.svg)
## Features
- **Two wavetable oscillators** — 256 frames × 2048 samples, bilinear morphing,
8 warp modes (Bend+/-, Sync, PWM, Asym, Mirror, Fold), unison 1–16 with phase
spread/detune/width.
- **10 factory wavetables** (Basic Shapes, Saw PWM, Square Sync, Triangle Fold,
Vowel, Organ, Warm Saw, Digital, Glass, Bass), generated lazily and RAM-resident.
- **Sub oscillator** (sine/triangle, −2/−1/0 oct) and **noise** (white/pink).
- **Filter section** — two slots, 7 models (Ladder LP/HP/BP, Diode, Comb, Formant,
Screamer), 6/12/24 dB slopes, drive, keytrack, and serial/parallel/split routing.
- **4 ADSR envelopes** with curve tension and live preview.
- **4 LFOs** — tempo-sync, 7 shapes (sine/tri/saw/square/S&H/step/freehand),
editable step-sequencer and freehand drawing, fade-in and start delay.
- **Modulation matrix** — direct source→destination connections with bipolar depth.
- **FX rack** — 9 effects (Hyper/OTT, Chorus, Flanger, Phaser, Distortion, EQ,
Compressor, Delay, Reverb) in 8 reorderable slots.
- **4 macros** (Energy, Width, Drive, Atmosphere) with assignable destinations.
- **RAVE** — one-click boost (unison, width, drive, OTT, reverb) with snapshot/restore.
- **16 factory presets** with production-quality values.
- **Scalable GUI** (75% / 100% / 125% / 150% / 200%) — dark modern vector look,
Path-drawn controls, SVG logo/background, real-time waveform/filter/envelope/LFO
displays.
- Two wavetable oscillators, each with 256 frames of 2048 samples, bilinear
morphing, seven warp effects plus bypass, and up to 16 unison voices.
- Ten factory wavetables, built before audio rendering and kept in memory.
- Sine/triangle sub oscillator and white/pink noise.
- Two filter slots with seven models, drive, key tracking and serial, parallel
or split routing.
- Four ADSR envelopes and four free-running or tempo-synced LFOs, including
editable step and freehand shapes.
- A modulation matrix and four assignable macros.
- Nine effect types in eight reorderable slots, with independent state per slot.
- A non-destructive RAVE boost for unison, spread, drive, Hyper and reverb.
- Sixteen factory presets and a scalable vector GUI with waveform, envelope,
LFO and schematic filter previews.
## Architecture
```
Source/
PluginProcessor.{h,cpp} APVTS, presets, state, RAVE/UI-scale glue
PluginEditor.{h,cpp} Tabbed dark GUI, scaling, visualisations
Params.{h,cpp} Parameter IDs, enums, mod sources/targets, mappings
Wavetable.{h,cpp} 256×2048 tables + lazy library
PluginEditor.{h,cpp} Tabbed GUI, owned controls, scaling, previews
Params.{h,cpp} Parameter IDs, indexed groups, enums, mappings
Wavetable.{h,cpp} 256×2048 tables, prebuilt by the engine
Oscillator.{h,cpp} Morph/warp/unison oscillator
SubOscillator.{h,cpp} Sine/triangle sub
NoiseOscillator.{h,cpp} White/pink noise
Filter.{h,cpp} 7 filter models
FilterBank.{h,cpp} 2 slots + routing
Filter.{h,cpp} Seven filter models and cached coefficients
FilterBank.{h,cpp} Two slots and routing
Envelope.{h,cpp} ADSR with curve
LFO.{h,cpp} Synced LFO + shape editing
LFO.{h,cpp} Continuous phase, sync and shape editing
ModulationMatrix.{h,cpp} Connections
FXProcessor.{h,cpp} Reorderable rack
FXProcessor.{h,cpp} Independent effect state per rack slot
EffectUnits/ Hyper, Chorus, Flanger, Phaser, Distortion, EQ,
Compressor, Delay, Reverb
MacroControls.{h,cpp} 4 assignable macros
RAVEButton.{h,cpp} RAVE snapshot/boost
SynthVoice.{h,cpp} 32-voice pool render path
Engine.{h,cpp} Per-block DSP + MIDI + FX + limiting
GUI/ Knob, Slider, ToggleButton, Display, Panel,
Waveform/Filter/Envelope/LFO displays
Presets/FactoryPresets.cpp 16 factory presets
MacroControls.{h,cpp} Four assignable macros
RAVEButton.{h,cpp} Render-time boost, without rewriting parameters
SynthVoice.{h,cpp} Per-voice oscillators, filters and envelopes
Engine.{h,cpp} 32 voices, MIDI timing, control snapshots, FX
GUI/ Controls and previews
Presets/FactoryPresets.cpp Factory preset definitions
Resources/ SVG assets
```
Key architectural decisions: a fixed 32-voice pool with pre-allocated unison
sub-voices (max 16/osc), scalar processing (SIMD deferred), control-rate modulation
(one level, no modulation-of-modulation), and a `juce::AudioProcessorValueTreeState`
backed parameter store that survives preset save/load together with the mod matrix,
macro assignments and LFO shape data.
The engine splits rendering at MIDI event positions and into segments of at most
64 samples. Buffers and effect instances are prepared ahead of time. GUI edits
use a control-side copy; audio tries to capture that copy once per host block and
keeps its previous settings if an edit is still in progress.
### Behavior notes
- Loading a factory preset resets sound parameters before applying its settings,
while preserving UI scale.
- RAVE is automatable and saved with the project. It changes the rendered sound
without moving the underlying knobs; turning it off removes the boost.
- Modulation of Master and FX mixes uses global sources directly. Envelope,
velocity, note and random sources for these destinations use the newest active voice.
- Effect types can repeat across slots. Reordering swaps settings, not running
delay or reverb tails. Preconstructing effects uses more memory but avoids
allocating effect instances while audio is running.
- Filter graphs are schematic previews, not measurements of the nonlinear DSP.
- LFO shape data is stored as JSON text inside the project's XML state. Older
saves containing only the literal `[Array]` did not preserve the shape data
and fall back to the default pattern.
## Building
@@ -139,7 +145,10 @@ The Windows script reuses the toolchain file without rewriting it. It injects
`moduleinfo.json` because the cross-compiled JUCE manifest helper cannot run on
Linux. Keep this manifest's metadata in sync with `CMakeLists.txt`,
`cmake/Plugin.cmake`, and the vendored VST3 SDK. MinGW links its C++ and threading
runtimes statically via `-static-libgcc -static-libstdc++ -static`.
runtimes statically via `-static-libgcc -static-libstdc++ -static`. Before staging,
the script verifies the linked module is x86-64, imports none of those MinGW
runtimes, and exports the VST3 entry points, so a module that a stock Windows host
could not load fails the build instead of being staged silently.
macOS builds target the host architecture by default. To request a universal binary: