Layout: - scripts/ holds the four build scripts plus the new clean.sh - cmake/ holds mingw64-toolchain.cmake - the scripts default to build/linux, build/macos, build/windows, build/harness - docs/ holds the audit documents, with the round 1 task list archived Cleanup: - scripts/clean.sh removes generated output, with --dry-run, --staging, --legacy and --all, and refuses tracked, protected or outside paths - the Windows VST3 manifest now comes from cmake/moduleinfo.json.in, so its version, vendor and class IDs follow cmake/Plugin.cmake instead of a hand-written heredoc that could drift - deleted the unused Source/GUI/Slider, which no code constructed - fixed the hidden double processBlock overload: three warning lines to zero - unslopped the build scripts, the two .cjs helpers and the CMake comments - .gitignore covers the upstream JUCE Projucer and DemoRunner binaries Verified: five scripts pass bash -n; the harness passes CTest with zero warning lines; the Linux VST3 and Standalone link; the Windows cross-build passes its x86-64, runtime-import and entry-point checks; each configure exposes only its own targets; and a full build leaves Source/ and third_party/JUCE unchanged. The generated manifest is identical to the previous hand-written one.
272 lines
12 KiB
Markdown
272 lines
12 KiB
Markdown
# SerumAlt
|
||
|
||
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.
|
||
|
||

|
||
|
||
## Features
|
||
|
||
- 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 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} Seven filter models and cached coefficients
|
||
FilterBank.{h,cpp} Two slots and routing
|
||
Envelope.{h,cpp} ADSR with curve
|
||
LFO.{h,cpp} Continuous phase, sync and shape editing
|
||
ModulationMatrix.{h,cpp} Connections
|
||
FXProcessor.{h,cpp} Independent effect state per rack slot
|
||
EffectUnits/ Hyper, Chorus, Flanger, Phaser, Distortion, EQ,
|
||
Compressor, Delay, Reverb
|
||
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
|
||
```
|
||
|
||
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
|
||
|
||
The scripts live in `scripts/`. Each configures and builds its own directory under
|
||
`build/`. 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
|
||
|
||
The build scripts require Bash, CMake 3.22+, Ninja, and C/C++17 compilers. The
|
||
harness script 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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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 `cmake/mingw64-toolchain.cmake`
|
||
selects the `x86_64-w64-mingw32-*` tools and isolates target library searches.
|
||
|
||
### Production builds
|
||
|
||
| Host | Command | Build directory | Formats |
|
||
| --- | --- | --- | --- |
|
||
| Linux | `./scripts/build_linux.sh` | `build/linux/` | VST3, Standalone |
|
||
| macOS | `./scripts/build_macos.sh` | `build/macos/` | VST3, AU, Standalone |
|
||
| Linux targeting Windows x64 | `./scripts/build_windows.sh` | `build/windows/` | VST3 only |
|
||
|
||
Default Release artifacts:
|
||
|
||
- Linux: `build/linux/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3` and
|
||
`build/linux/SerumAlt_artefacts/Release/Standalone/SerumAlt`.
|
||
- macOS: `build/macos/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3`,
|
||
`build/macos/SerumAlt_artefacts/Release/AU/SerumAlt.component`, and
|
||
`build/macos/SerumAlt_artefacts/Release/Standalone/SerumAlt.app`.
|
||
- Windows: `build/windows/SerumAlt_artefacts/Release/VST3/SerumAlt.vst3`, also
|
||
copied to `SerumAlt_Windows/SerumAlt.vst3`.
|
||
|
||
The Windows script reuses the toolchain file without rewriting it. A cross-build
|
||
cannot run JUCE's manifest helper, because that helper is a host tool, so the
|
||
script copies the `moduleinfo.json` that CMake writes from
|
||
`cmake/moduleinfo.json.in`. The plugin name, version, vendor, class IDs and VST3
|
||
SDK version come from variables in `cmake/Plugin.cmake`, so a version bump reaches
|
||
the manifest on its own. MinGW links its C++ and threading 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:
|
||
|
||
```bash
|
||
CMAKE_OSX_ARCHITECTURES='arm64;x86_64' ./scripts/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:
|
||
|
||
```bash
|
||
./scripts/build_harness.sh
|
||
```
|
||
|
||
Build and run the checks:
|
||
|
||
```bash
|
||
./scripts/build_harness.sh --run
|
||
```
|
||
|
||
Or rerun an already-built Release harness:
|
||
|
||
```bash
|
||
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_TYPE` defaults to `Release`; set it to `Debug` for a debug build.
|
||
- `BUILD_DIR` overrides a script's build directory. Relative paths are resolved
|
||
from the repository root, even when the script is invoked elsewhere.
|
||
- `CMAKE_BUILD_PARALLEL_LEVEL` defaults to `2` to limit memory use, including JUCE's
|
||
configure-time helper build. Increase it if memory permits.
|
||
- Windows-only `OUTPUT_DIR` overrides the staging directory.
|
||
|
||
For example:
|
||
|
||
```bash
|
||
BUILD_TYPE=Debug BUILD_DIR=build/harness_debug ./scripts/build_harness.sh --run
|
||
CMAKE_BUILD_PARALLEL_LEVEL=4 ./scripts/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:
|
||
|
||
```bash
|
||
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. `build/` and `SerumAlt_Windows/` are git-ignored. Leave
|
||
existing builds intact and select an unused build directory for a fresh configure:
|
||
|
||
```bash
|
||
BUILD_DIR=build/linux_fresh ./scripts/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:
|
||
|
||
```bash
|
||
BUILD_DIR=build/windows_fresh OUTPUT_DIR=build/windows_fresh/staged ./scripts/build_windows.sh
|
||
```
|
||
|
||
### Cleaning
|
||
|
||
`scripts/clean.sh` removes generated output. A plain run removes the build trees
|
||
under `build/`; `--staging` adds `SerumAlt_Windows/`, `--legacy` adds the
|
||
`build_*` and `cmake-build-*` trees that earlier versions of the scripts left in
|
||
the repository root, and `--all` is both. `--dry-run` prints every path with its
|
||
size and removes nothing.
|
||
|
||
```bash
|
||
./scripts/clean.sh --dry-run --all
|
||
./scripts/clean.sh --all
|
||
```
|
||
|
||
The script never removes a tracked file, anything under `Source/`, `third_party/`
|
||
or `.git`, or a path outside the repository. It refuses to run when `BUILD_DIR` or
|
||
`OUTPUT_DIR` names a directory outside the repository, because such a directory
|
||
belongs to another checkout.
|
||
|
||
## License
|
||
|
||
JUCE is licensed separately (see `third_party/JUCE/LICENSE.md`). This project is
|
||
provided for evaluation/development purposes.
|