Anamorph (by RollyTech) is a stereo-field toolkit: it turns mono into stereo, controls stereo width (globally and per band), and provides the full set of stereo tools (MS, mono-maker, channel utilities, monitoring) around a high-end diamond vectorscope. Built with CMake + JUCE only — it configures and builds entirely from the command line on a headless Linux machine, no IDE.
- M/S is now a decoder (Ch1 = Mid, Ch2 = Side → L/R); Swap swaps Mid/Side.
- Frequency knobs are logarithmic (no dead chunk at the bottom). Mono-Maker cutoff is octave-rate-limited so a fast drag can't chirp.
- Velvet play/pause burst masked by a fixed-time gate ramp.
- Knobs/sliders: neon blue→cyan glow (less green), gradient glow, hover/press feedback; slider thumb is neutral until you touch it. Combo hover now lights the whole control; toggles brighten only the switch+label; Input toggle labels line up by construction.
- Meters: dim layer is a fast peak that pushes the peak line, bright is the slow RMS; the RMS number snaps up then holds and falls slowly; scale tuned for the −24..0 mixing range; bars darker at the bottom.
- Vectorscope no longer shaves the image at the rim; Output module dropped lower with a Widen/Output divider; bigger Simple-mode Widen text (names + numbers).
- CI: Linux job stays paused; macOS build is the deliverable.
- M/S is now an input encoder: it encodes the input to Mid/Side so the Input controls (Swap, Balance, Phase) act on Mid & Side; Swap swaps Mid↔Side.
- Enabling Level Match no longer slams loud: the loudness re-arm only happens on real processing/A-B changes, not on the Match or Bypass toggle.
- Hover/press feedback across knobs (arc glow on hover, pointer + halo on press/number-drag), toggles, A/B, combos (open state) and sliders.
- Meters: a non-uniform dB ruler, more distinct & quicker VU/RMS ballistics,
refined bars. Value boxes mirror the displayed value exactly (no rounding;
2kaccepted for the Mid/Hi crossover; Left balance shown negative). - Vectorscope clip ring no longer shaved at the edges; squarer combo corners, narrower pop-up dead-zones, larger Simple-mode Widen text.
- CI: the Linux job is paused (macOS prioritised); the source stays cross-platform (Win/Linux) and still builds.
- Undo/Redo reworked: each A/B slot keeps its own undo history; the Undo/Redo buttons only affect the current slot, an A/B switch is never an undo step, and Bypass / Settings / Meters are excluded from history. Copy is undoable on the slot it changed. (Replaces JUCE's global undo manager.)
- Drive + Mono Maker no longer sounds like a low cut: the mono low band is now driven together with the highs.
- Value box: dragging the number and the bare-number/
2keditor now actually work — they were being created with the wrong look-and-feel before. - Meters tuned to iZotope Insight 2 Levels (300 ms VU body, fast riser that falls with it, 1 s peak hold), richer bar rendering, equal-size Peak/RMS text.
- UI: Input toggle baseline + Balance layout fixed, "Focus"/"Style" captions left-aligned, compact Input-combo lists, bigger Simple-mode Widen labels, shorter A/B oval, tidied tooltips.
- Mono Maker is a proper band-split now: the low band is summed to mono and routed around both the widener and the dry/wet Mix, straight to Output — so the lows are never cut and Level Match measures the full recombined signal before Output gain/balance. (Verified: mono bass preserved, side bass removed.)
- Level Match: freezes during silence so a big boost no longer slams loud on the next play; remembers a value per A/B slot so switching glides; stays independent of the Output knob.
- Level meter overhaul: per-channel L/R Peak + RMS numbers (8 total) with held max-peak, clip colours (red Peak / amber RMS, click or replay to reset), rate-limited hold-then-fall numbers, fast-rise/slow-fall + faster-falling RMS bars, a hold-then-drop peak block, and a richer look.
- Value boxes: drag the number to change it; double-click types a bare
number (units hidden) and accepts
2k/2kHz/2000; balance edits are signed (− = Left); double-click on a knob resets (triple-click no longer re-resets). - Velvet pause/clip-tail gate retuned; Chorus Rate default 0.50 Hz; Settings no longer ride A/B; plus many UI fixes (Adv label fits, A/B oval, Dim-D, Haas "Lean" / Dim-D "Style" captions, bigger ø, uniform pop-up rows, Persist reveal).
- Mono Maker actually works again and is now a true band-split: the low band is summed to mono and bypasses the widener, only the highs are widened (no bass cancellation and the lows really go mono). Verified by a self-test.
- Level Match is now independent of the Output knob, and an A/B swap re-arms the measurement so it glides instead of jumping in level.
- Velvet: per-sample re-weighting kills the Density-drag zipper; an input-presence gate fades the decorrelation tail so pausing no longer bursts.
- Mono Maker Freq glides per sample (no pitch-wobble while dragging).
- UI: drag the value number to change a knob; level-meter glyph instead of "Meters"; premium glowing sliders; compact non-clipping Input toggles; square pop-up lists; uniform combo/list font; Persist has a tooltip and dims the Settings panel while dragging so you see the live scope; re-balanced Simple / Advanced layouts; shorter A/B oval; smaller Apply; nudged Undo/Redo glyphs.
- Click-free everything: a short raised-cosine duck swaps every discrete control (algorithm, M/S, channel, Mono, Swap, Mono-Maker, Oversampling and Bypass) at silence, so switches never pop. Drive now crossfades from clean, so engaging/disengaging it is seamless and 0 dB is truly identity.
- Mono Maker is now BEFORE the widener — collapsing the lows first stops the decorrelators spreading the bass, so an L+R sum can't comb-cancel it.
- Mono-Maker frequency glides (no more "doubled" artefact while dragging).
- Fixed-size window for both modes (no resize flicker when toggling Adv/A-B).
- Simple mode is just the Widen core; an Output module (Mix, Output, Balance, Level Match, Mono-Maker) joins Input and Multiband under Adv — and Advanced-only modules cleanly default-bypass when Advanced is off.
- Level meter reworked: non-uniform scale, numeric Peak / RMS readouts, a slow + fast RMS pair (Ozone-style) and a peak block; it reveals with a small animation. Phase meter gets -1 / +1 labels. Persisted Meters / Tooltips.
- UI polish: redesigned toggles (no clipped text/edges), A/B racetrack frame, bigger Undo/Redo, rounded pop-ups, www.rolly.tech link + fixed copyright, Persist moved into Settings, Haas defaults to the Left perceived side.
- Bug fixes: Level-Match Apply now overrides Output (no more drift on repeated presses); M/S Solo moved to the input stage so soloing Side stays correct; no more pops when toggling polarity / Input Channel / the widening algorithm (even during silence); A/B state persists across editor close and session recall, and A/B / presets no longer flip Advanced / Bypass / Oversampling.
- Drive is now peak-preserving (driving harder no longer drops the level).
- Level meters (Input/Output L/R, peak + RMS) — toggle in the top bar.
- Vectorscope L/R mirroring fixed; Haas side now means the perceived side.
- UI polish: rounder controls, subtle glow/glass, single A / B indicator, bypass goes red and leaves the toolbar lit, tooltips default off, "Adv".
- Transparent on load: a fresh instance does nothing to the sound. Widening is driven by a single Amount control (0% = bypass-clean); Width 100% is also identity. (Verified by a self-test.)
- Click-free knobs: every continuous control is smoothed; Velvet Noise no longer regenerates its random taps while you drag (the old crackle).
- Restructured UI: grouped INPUT / WIDEN / OUTPUT modules, single-button A/B
- Copy, in-window About & Settings overlays, styled tooltips (toggle), bypass dimming (controls stay live), undo/redo, balance/correlation meters, clip-red scope rim, Output Balance, a Zero-Latency (live) mode, and oversampling (default 1x) in Settings.
- Versioning:
MAJOR.MINOR.PATCH(pre-1.0) plus a CI build number (About box).
Primary build target: VST3 (+ a Standalone target for convenience). AU (Audio Unit) is built on macOS — the
macosCI job produces a universal VST3 and.component(AU), so Logic/GarageBand are covered. AAX is still out of scope (needs an Avid account + PACE/iLok signing). The DSP core is fully decoupled from the plugin wrapper, so adding AAX later is near-zero work.
Every push runs GitHub Actions, which uploads ready-to-use plugins as build artifacts (Actions run → Artifacts):
Anamorph-macOS— universal (Apple Silicon + Intel) VST3 + AU, with anINSTALL.txt. AU is what Logic Pro / GarageBand load.Anamorph-VST3-Linux— Linux x86-64 VST3.
macOS plugins are ad-hoc signed but not notarized, so after downloading run
xattr -dr com.apple.quarantine on the bundles (see the bundled INSTALL.txt).
Windows is not yet wired into CI — say the word and it's a small addition.
# 1. Install build dependencies (Ubuntu; safe to re-run)
scripts/setup-linux.sh
# 2. Configure + build (fetches a pinned JUCE tag via CMake FetchContent)
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
# ...or simply: scripts/build.sh
# 3. Run the DSP self-tests
scripts/run-tests.sh
# 4. Validate the VST3 with pluginval (strictness 8 = the standard gate)
scripts/run-pluginval.sh 8The build prints the path to the produced Anamorph.vst3. It is typically at:
build/Anamorph_artefacts/Release/VST3/Anamorph.vst3
- Ubuntu apt mirrors —
archive.ubuntu.com/ports.ubuntu.com(or your mirror) github.com— JUCE source (pinned tag, via CMakeFetchContent)github.com— pluginval release download (only forrun-pluginval.sh)
To build without network (JUCE already on disk), point CMake at a local
checkout: cmake -B build -DANAMORPH_JUCE_PATH=/path/to/JUCE ...
The project is split into a format-agnostic DSP core and a thin plugin wrapper + GUI, so the same DSP could back AU/AAX later unchanged.
src/
dsp/ Format-agnostic DSP core (no plugin/JUCE-wrapper deps)
EngineParameters.h POD snapshot of all DSP params (wrapper -> engine)
AnamorphEngine.{h,cpp} Orchestrates the full processing chain
MidSide.h Gain-correct (1/sqrt2) MS matrix + width helper
Saturation.h Drive (tanh waveshaper, unity small-signal gain)
HaasProcessor.{h,cpp} Haas precedence delay (1..35 ms, L/R side)
VelvetNoise.{h,cpp} Sparse velvet-noise decorrelation (mono->stereo)
ChorusEngine.{h,cpp} Chorus + Dimension-D (anti-phase = no pitch wobble)
MonoMaker.{h,cpp} Linkwitz-Riley low-freq mono
MultibandWidth.{h,cpp} 3-band phase-coherent per-band width (Advanced)
LoudnessMatch.{h,cpp} BS.1770 K-weighted Auto-Gain (Match/Apply)
Correlation.h -1..+1 phase-correlation meter (fast + slow)
ScopeBuffer.h Lock-free stereo ring buffer for the vectorscope
PluginParameters.{h,cpp} APVTS layout + raw-pointer cache + -> EngineParameters
PluginProcessor.{h,cpp} VST3/Standalone wrapper, bus layouts, state, PDC
PluginEditor.{h,cpp} Simple/Advanced UI, A/B compare, OpenGL context
gui/
LookAndFeel.{h,cpp} Premium dark "digital" look (no skeuomorphism)
Vectorscope.{h,cpp} Diamond/Lissajous goniometer (GPU-composited)
CorrelationMeter.{h,cpp} Horizontal + vertical correlation meters
tests/dsp_tests.cpp Headless acceptance self-tests
scripts/ setup / build / test / pluginval
- Input conditioning — channel kill (L/R/Mono), Swap, Input Balance, polarity.
- Mono Maker — lows → mono, deliberately before widening, so the decorrelators never spread the bass (no L+R comb-cancellation).
- MS encode (only if MS mode) — wraps Drive + the algorithm.
- Effect engine — Drive → algorithm (Haas / Velvet / Chorus / Dimension-D) → global Width.
- Multiband Width (Advanced Mode only).
- MS decode (only if MS mode).
- Mix (Dry/Wet) — the dry path is delay-compensated to the wet latency.
- Output Gain / Auto Gain.
- Metering tap — always taps the final output (scope + correlation).
Every discrete switch (algorithm/routing/bypass) is applied at the silent bottom of a ~4 ms raised-cosine duck, so toggling is click-free even during playback.
- Oversampling wraps only the nonlinear/modulation stages (Drive, Chorus, Dimension-D). Linear stages (Haas, Velvet, Width, MS, Mono-Maker, crossovers) stay outside it — no needless CPU or latency. If a chain has nothing nonlinear to oversample, the oversampler is bypassed and reports zero latency.
- Oversampling uses JUCE's minimum-phase polyphase IIR half-band filters: low latency and — importantly — no linear-phase pre-ringing / waveform misalignment. Integer latency is requested so PDC is exact. (Trade-off: IIR has mild phase response vs. a linear-phase FIR; per the spec we prioritise no high latency and no pre-ringing.)
- Mono compatibility by construction: Width and decorrelation only ever
modify the Side;
L + R = 2·Midalways, so summing to mono never collapses the signal. - Dimension-D has no audible pitch wobble: each channel sums two delay taps modulated in anti-phase, so the Doppler pitch shifts cancel — width and spaciousness without the "seasick" vibrato.
- Auto-Gain is perceptual (ITU-R BS.1770 K-weighting), not peak/RMS. Match level-matches in real time for fair A/B; Apply locks the measured gain into Output Gain as a fixed value so exports stay consistent (Kraftur-style). It is deliberately not a continuously-adapting AGC.
- Real-time safe: no allocation / locking / file IO on the audio thread; the scope uses a lock-free ring buffer; the GUI redraws at 60 fps.
- stereo → stereo (default, for already-stereo material)
- mono → stereo (the headline "turn Mono into Stereo" — the host instantiates this on a mono track). Output is always stereo. Mono → mono is not offered.
- DSP self-tests (
tests/dsp_tests.cpp): MS round-trip is bit-exact; no NaN/Inf/denormals across every algorithm × oversampling × feature combination; reported latency matches the actual chain delay; true bypass is a null. - pluginval: target strictness 8 as the standard gate; aim for 10
before release. Editor open/close tests run under
xvfb-runon headless Linux.
The audio sound quality (how the widening / Dimension-D / chorus actually
sound) and the visual appearance of the GUI/vectorscope can't be judged in a
headless sandbox. Load the built .vst3 in a DAW (e.g. Reaper) on a machine with
audio + display to audition. Treat a green build + pluginval pass as
"ready to audition," not final sign-off.
Simple Mode (default) is just the Widen core around the vectorscope: algorithm, Drive, Amount, Width. Advanced Mode (toggle in the top bar) adds the Output module (Mix, Output, Balance, Level Match, Mono-Maker), the Input module (channel mode, swap, mono, input balance, polarity, M/S, M/S solo) and the Multiband width module. Advanced-only modules default-bypass when Advanced is off, while their knob positions are remembered. Oversampling, tooltips and scope persistence live in Settings.