Skip to content

Releases: stoatworks-labs/zero-eq

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 30 Jul 15:32

What's Changed

New Contributors

Full Changelog: v0.7.0...v0.7.1

v0.7.0 - Harmonic character is now genuinely frequency-selective

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 24 Jul 23:33

The fix

The Harmonic band character was running its waveshaper across the entire broadband signal passing through the band. A Harmonic bell at 5 kHz saturated your bass, your mids, everything — it was a full-range saturator sitting behind an EQ filter, not a harmonic EQ.

A harmonic EQ's defining behaviour is frequency-selectivity: isolate the region the band targets, generate harmonics from only that, mix them back. That's now what it does.

  • The band's region is split off through a region-isolation filter (band-pass at freq/Q for Bell/Tilt, high-pass at the corner for High Shelf, low-pass for Low Shelf — the same shape the dynamic detector uses).
  • Only that isolated region is shaped.
  • Only the generated harmonics — shaped signal minus isolated region — are summed back in parallel with the dry path. Because the difference is what's summed, the band's own level is unchanged and drive=0 is exactly transparent: it adds harmonics without adding gain.

Why this wasn't caught in v0.3.0

The original verification FFT'd a single tone at the band's own frequency. That test is structurally incapable of distinguishing "saturate only this region" from "saturate everything" — with one tone present, both produce identical output. It confirmed the waveshaper maths (which was, and still is, correct) and nothing whatsoever about the routing.

The new test is two-tone, which separates them immediately.

Verified

Two-tone harness: ~100 Hz well outside the band + ~5 kHz inside it; Harmonic bell at 5 kHz, Q=1, +12 dB.

Measurement Result
In-band 2nd harmonic vs out-of-band 2nd harmonic 91 dB selectivity
Out-of-band fundamental level change within 0.01 dB of input
Pure-odd blend, 2nd harmonic 121 dB down (tanh is an odd function)
Harmonic band at 0 dB gain transparent to -180 dB
Dynamic EQ after shared-filter refactor still ducks correctly

The old implementation could not have produced the first two numbers.

pluginval strictness 5: VST3 clean. AU passes with two benign, pre-existing warnings unrelated to this DSP-only change (sidechain bus-disable quirk, and the AU wrapper reporting program -1) — both now documented accurately in the README, which previously undercounted them.

Also in this release

The "which filter isolates this band's region" logic existed only inside DynamicEQDetector. It's now a single shared EQBand::designRegionIsolationFilter used by both the harmonic stage and the dynamic detector, so the two can't drift apart. Isolation coefficients are cached and redesigned only when the region actually moves, since coefficient construction allocates and this runs on the audio thread.

Known limitations

  • Not yet tested against real (non-silent) audio hardware in a live signal chain
  • No linear-phase mode — intentionally out of scope (zero latency is the point)

Full Changelog: v0.6.0...v0.7.0

v0.6.0 - External sidechain input for dynamic EQ

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 17 Jul 09:50

What's new

Every dynamic EQ band can now detect against an external stereo sidechain signal instead of the internal chain:

  • A new optional stereo Sidechain input bus (disabled by default; hosts enable/disable it per the normal JUCE/DAW bus workflow)
  • A per-band Sidechain toggle in the Dynamic EQ section — leave it off to keep detecting against the signal already in the chain (the original behaviour), or turn it on to duck/boost that band off a separate input entirely (e.g. duck a music bed off a voice track)
  • If a band asks for sidechain detection but the host hasn't actually connected the bus, it falls back to internal detection automatically rather than detecting against silence — and the toggle's label switches to "Sidechain (n/c)" so that's visible at a glance
  • Purely a read-only detection input: it never touches the main signal path, so the plugin's zero-added-latency guarantee is untouched

Verified

  • Real-audio test harness: fed a main signal that never changes alongside a sidechain signal that pulses loud/quiet. With the toggle on, the dynamic band's gain tracked the sidechain pulses even though the main signal was constant — confirming detection genuinely comes from the sidechain, not the main buffer. With the toggle off, or with sidechain requested but disconnected, the band produced a steady duck driven by the main signal instead, confirming both the default path and the fallback path are correct.
  • pluginval strictness level 5: VST3 clean, including its own bus-enumeration checks (mono/stereo sidechain layouts, enable-all, disable-non-main, restore-default all pass). AU passes with one known benign warning — see below.
  • Hosted successfully in REAPER.

Known benign AU warning

pluginval's "Disabling non-main buses" check fails on the AU build. This is a documented JUCE/AudioUnit interaction — AUv2's bus-disable semantics for auxiliary/sidechain input buses don't round-trip the way pluginval expects, independent of what the plugin itself does. It's a widely reported quirk affecting AU plugins with a sidechain bus generally, not specific to Zero EQ. Apple's own auval validator still exits 0, and every other AU check (including enabling all buses and restoring the default layout) passes clean.

Known limitations

  • Not yet tested against real (non-silent) audio hardware in a live signal chain
  • No linear-phase mode — intentionally out of scope (zero latency is the point)

Full Changelog: v0.5.0...v0.6.0

v0.5.0 - Ballistics-accurate metering

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 17 Jul 09:38

What's new

Input and output meters now show real ballistics instead of a raw peak read:

  • Peak — instant attack, ~20dB/s exponential release
  • VU — one-pole RMS integrator, ~300ms to settle on a step (classic VU-style ballistics)
  • True peak — 4x-oversampled estimate (Catmull-Rom interpolation) catching inter-sample peaks a plain sample read misses
  • Clip indicator — held ~1.5s, triggered at a small headroom margin below 0dBFS

All of it runs on a read-only side path off a copy of the buffer — it can't add latency to the actual signal chain, keeping the plugin's zero-added-latency guarantee intact.

Bugs found and fixed during verification

Both caught by the project's real-audio test harness, not by pluginval or GUI-only checks:

  • Clip threshold never triggered. An exact >= 1.0f check missed genuinely full-scale samples because normalized-parameter round-tripping through the "0dB" gain stage doesn't always land on bit-exact 1.0f. Fixed with a -0.1dBFS headroom threshold — also just how real clip indicators are normally built.
  • Clip hold timing was wrong. The hold counter was tracked in "blocks remaining," decremented once per process() call, but the hold duration was computed assuming a specific block size. Different block sizes on later calls threw off the real elapsed hold time. Fixed by tracking samples remaining instead.

Verified

  • pluginval strictness level 5: clean on both VST3 and AU
  • Real-audio test harness: peak attack/release timing, VU ballistics, true-peak inter-sample detection, and clip-hold duration all checked against known test signals
  • Hosted successfully in REAPER

Known limitations

  • Dynamic EQ: no external sidechain input yet (internal detection only)
  • Not yet tested against real (non-silent) audio hardware in a live signal chain
  • No linear-phase mode — intentionally out of scope (zero latency is the point)

Full Changelog: v0.4.0...v0.5.0

v0.4.0 — Presets

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 17 Jul 09:12

8-band zero-latency parametric EQ + dynamic EQ + harmonic saturation + feed-forward
compressor + I/O trim, now with a full preset system.

New in this release

  • 7 factory presets covering every feature area: Init (flat baseline), Vocal Presence
    (static EQ shaping), De-Esser (Dynamic) (dynamic EQ), Warm Bus (Harmonic) (harmonic
    saturation), Podcast Voice (combined real-world use), Broadcast Loudness
    (compressor-forward), and Telephone/Lo-Fi (creative extreme).
  • User preset save/load to the standard per-user preset directory
    (~/Library/Audio/Presets/Allan Sargeant/Zero EQ/ on macOS), as plain APVTS-state
    XML — the same mechanism a host already uses for session recall.
  • A preset bar in the GUI: combo box (Factory/User sections), prev/next, and Save with
    a name-entry dialog.
  • Factory presets now back the plugin's real VST3/AU "program" interface, so a host's
    own program browser works too. This also resolves a previously-noted benign
    pluginval warning on the AU build ("Current program is -1").

Verified

  • pluginval (VST3 + AU, strictness 5): clean pass, zero warnings on both formats.
  • Real audio through the actual shipped processor class: every factory preset applies
    correctly (every override lands exactly as specified), produces finite audio, and
    reports zero added latency. A save/load round trip to disk restores exact values.

Known limitations

  • Meters are simple peak reads, not ballistics-accurate.
  • Dynamic EQ detection is internal only (no external sidechain input yet).
  • Not yet tested against real audio hardware in a live signal chain.

All three original roadmap items (dynamic EQ, harmonic EQ, presets) are now shipped.

Full Changelog: v0.3.0...v0.4.0

v0.3.0 — Harmonic EQ

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 15 Jul 21:26

8-band zero-latency parametric EQ + dynamic EQ + feed-forward compressor + I/O trim,
now with a Harmonic band character.

New in this release

  • Harmonic character: a third per-band option alongside Modern/Vintage. Layers
    gain-driven even/odd harmonic saturation on top of the standard linear response.
    Even harmonics (warm, tube-like) come from an asymmetric quadratic waveshaper; odd
    harmonics (grittier, transistor-like) come from a tanh waveshaper; a per-band blend
    slider crossfades between them. Drive scales with the band's own applied gain, so a
    band left at 0dB stays transparent.
  • Uses first-order antiderivative anti-aliasing (ADAA) instead of oversampling, so
    the added harmonic content stays clean without adding latency.

Verified

  • pluginval (VST3 + AU, strictness 5): clean pass.
  • Real audio through the actual shipped processor class, FFT-checked: the even
    generator produces exactly the fundamental + 2nd harmonic and nothing else; the odd
    generator produces exactly odd harmonics and nothing else. Zero added latency
    confirmed throughout.

Known limitations

  • No preset system yet.
  • Meters are simple peak reads, not ballistics-accurate.
  • Dynamic EQ detection is internal only (no external sidechain input yet).
  • Not yet tested against real audio hardware in a live signal chain.

Both roadmap items from v0.2.0 (dynamic EQ, harmonic EQ) are now shipped.

v0.2.0 — Dynamic EQ bands

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 15 Jul 21:10

8-band zero-latency parametric EQ + feed-forward compressor + I/O trim, now with
per-band dynamic EQ.

New in this release

  • Dynamic EQ: any gain-having band (Bell/Shelf/Tilt) can run as a frequency-selective
    compressor/expander — threshold, ratio, attack, release, range, and a Downward (duck)
    or Upward (boost) direction. No lookahead, zero added latency preserved.
  • Live curve indicators showing which bands are dynamically engaged and by how much.

Verified

  • pluginval (VST3 + AU, strictness 5): clean pass.
  • Hosted successfully in REAPER (real DAW).
  • Real audio processed through the actual shipped processor class confirms both EQ
    shaping and dynamic ducking/boosting match analytically-predicted values closely.

Known limitations

  • No preset system yet.
  • Meters are simple peak reads, not ballistics-accurate.
  • Dynamic EQ detection is internal only (no external sidechain input yet).
  • Not yet tested against real audio hardware in a live signal chain.

Next up: harmonic-based EQ.