Add .claude/ config (settings, graft statusline and hooks helpers, graft skill), .mcp.json and opencode.json MCP server entries, and AGENTS.md with graft usage instructions. Add .ignore to re-admit graft/ to ripgrep search while excluding its cache. Add /graft/ to .gitignore since the graph is regenerable.
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.
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.
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
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
Envelope.{h,cpp} ADSR with curve
LFO.{h,cpp} Synced LFO + shape editing
ModulationMatrix.{h,cpp} Connections
FXProcessor.{h,cpp} Reorderable rack
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
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.
Building
Each script configures and builds its own directory. Production builds disable the
QA harness explicitly; the harness script creates no plugin or standalone targets.
JUCE 7.0.12 is vendored under third_party/JUCE and is not downloaded by the scripts.
Prerequisites
All scripts require Bash, CMake 3.22+, Ninja, and C/C++17 compilers. The harness
also uses CTest, included with CMake, when invoked with --run.
On Linux, install GCC or Clang, pkg-config, and the ALSA, FreeType, X11 and OpenGL
development libraries. For Debian/Ubuntu:
sudo apt update
sudo apt install build-essential cmake ninja-build pkg-config \
libasound2-dev libfreetype6-dev libx11-dev libxcomposite-dev \
libxcursor-dev libxext-dev libxinerama-dev libxrandr-dev libxrender-dev \
libgl1-mesa-dev
For Arch/CachyOS:
sudo pacman -S --needed base-devel cmake ninja pkgconf alsa-lib freetype2 \
libx11 libxcomposite libxcursor libxext libxinerama libxrandr libxrender libglvnd
JACK is disabled by JUCE's default JUCE_JACK=0. Curl and the web browser are
explicitly disabled in both project targets. Their development packages are not
required for these builds, even though JUCE probes for them during configuration.
See third_party/JUCE/docs/Linux Dependencies.md if enabling additional JUCE modules.
On macOS, install Apple's Command Line Tools with xcode-select --install, or
select an installed Xcode toolchain. Install CMake and Ninja, for example with
brew install cmake ninja if Homebrew is already installed. The macOS script checks
that xcrun can locate the macOS SDK. It must run on a Mac; JUCE does not provide
an Apple cross-compilation toolchain for Linux.
For Windows cross-compilation, also install MinGW-w64 on the Linux host:
sudo apt install gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64
On Arch/CachyOS, use sudo pacman -S --needed mingw-w64-gcc instead. Keep the
native Linux tools and libraries installed: JUCE builds juceaide for the host
before compiling the Windows plugin. The checked-in mingw64-toolchain.cmake
selects the x86_64-w64-mingw32-* tools and isolates target library searches.
Production builds
| Host | Command | Build directory | Formats |
|---|---|---|---|
| Linux | ./build_linux.sh |
build_linux/ |
VST3, Standalone |
| macOS | ./build_macos.sh |
build_macos/ |
VST3, AU, Standalone |
| Linux targeting Windows x64 | ./build_windows.sh |
build_windows/ |
VST3 only |
Default Release artifacts:
- Linux:
build_linux/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3andbuild_linux/SerumAlt_artefacts/Release/Standalone/SerumAlt. - macOS:
build_macos/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3,build_macos/SerumAlt_artefacts/Release/AU/SerumAlt.component, andbuild_macos/SerumAlt_artefacts/Release/Standalone/SerumAlt.app. - Windows:
build_windows/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3, also copied toSerumAlt_Windows/SerumAlt.vst3.
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.
macOS builds target the host architecture by default. To request a universal binary:
CMAKE_OSX_ARCHITECTURES='arm64;x86_64' ./build_macos.sh
CMAKE_OSX_DEPLOYMENT_TARGET can also be passed to that script. Architecture and
SDK settings persist in CMake's cache. Use a separate build directory when changing
toolchains. These scripts build local artifacts, not signed or notarized releases,
and do not install plugins into a DAW's plugin directories.
Independent QA harness
On Linux or macOS, build the console harness without production targets:
./build_harness.sh
Build and run the checks:
./build_harness.sh --run
Or rerun an already-built Release harness:
ctest --test-dir build_harness --build-config Release --output-on-failure --no-tests=error
The executable is build_harness/SerumAltTest_artefacts/Release/SerumAltTest.
It renders the initial state and every factory preset, checks for non-finite
samples and aggregate non-silence, and exercises the RAVE toggle. A failed check
returns a nonzero exit status. This is a processor integration smoke test, not a
complete DSP, GUI, or real-time safety test suite.
The harness does not open an editor or audio device, but it still compiles the real processor, editor and JUCE GUI dependencies. Its separate build directory and CMake options isolate its outputs from production bundles; it is not a DSP-only library. It is built and run natively, not as a Windows cross-compiled executable.
Build options and layout
BUILD_TYPEdefaults toRelease; set it toDebugfor a debug build.BUILD_DIRoverrides a script's build directory. Relative paths are resolved from the repository root, even when the script is invoked elsewhere.CMAKE_BUILD_PARALLEL_LEVELdefaults to2to limit memory use, including JUCE's configure-time helper build. Increase it if memory permits.- Windows-only
OUTPUT_DIRoverrides the staging directory.
For example:
BUILD_TYPE=Debug BUILD_DIR=build_harness_debug ./build_harness.sh --run
CMAKE_BUILD_PARALLEL_LEVEL=4 ./build_linux.sh
CMakeLists.txt owns the shared source list and target configuration.
cmake/Plugin.cmake owns production formats and platform-specific linking.
cmake/Harness.cmake owns the test executable and its CTest registration.
For direct CMake use, SERUMALT_BUILD_PLUGIN defaults to ON and
SERUMALT_BUILD_TESTS defaults to OFF. A harness-only configure is:
cmake -S . -B build_harness -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DSERUMALT_BUILD_PLUGIN=OFF -DSERUMALT_BUILD_TESTS=ON
cmake --build build_harness --target SerumAltTest
An existing CMake cache retains its old option values; changing the defaults does
not turn tests off in an old build/ directory. Use the scripts to set the flags
explicitly. An alternate JUCE checkout can be selected with -DJUCE_ROOT=/path/to/JUCE
when configuring directly with CMake.
Fresh builds
The scripts reuse their build directories without deleting files, resetting Git,
or installing packages. Generated build/, build_*/, and SerumAlt_Windows/
directories are git-ignored. Leave existing builds intact and select an unused
build directory for a fresh configure:
BUILD_DIR=build_linux_fresh ./build_linux.sh
Windows staging updates generated files without deleting extra files already in the destination. For a clean distribution bundle, choose unused build and output paths together:
BUILD_DIR=build_windows_fresh OUTPUT_DIR=build_windows_fresh/staged ./build_windows.sh
License
JUCE is licensed separately (see third_party/JUCE/LICENSE.md). This project is
provided for evaluation/development purposes.