Skip to content

feat: v0.3.0 hybrid dynamic multiband (upward compression + Range)#26

Merged
yves-vogl merged 1 commit into
mainfrom
feat/v0.3.0-dynamic-multiband
Jul 17, 2026
Merged

feat: v0.3.0 hybrid dynamic multiband (upward compression + Range)#26
yves-vogl merged 1 commit into
mainfrom
feat/v0.3.0-dynamic-multiband

Conversation

@yves-vogl

Copy link
Copy Markdown
Contributor

Summary

Upgrades Triptych from a downward-only multiband compressor to a true dynamic multiband processor, closing a real reference-class gap: v0.2.0's KneeGainComputer hard-floored Ratio at 1:1 and forced an unconditional bypass for any ratio <= 1, so a band could only ever get quieter above threshold, never lifted. Every reference-class unit researched for v0.2.0 (Weiss DS1-MK3, FabFilter Pro-MB) documents ratio ranges that extend below 1:1 specifically to enable this.

Paradigm: Hybrid à la Pro-MB (per the binding brief) - keep the existing single Ratio parameter, extend it below 1:1 for upward compression/expansion, add a per-band Range (maximum gain-change clamp), and defer downward expansion/gating rather than bolt it onto a model that doesn't cleanly fit it. Full spec, sourced defaults, and honesty section in docs/design-brief-v3-dynamics.md; sourcing addendum in docs/research-notes.md.

New parameters (per band: Low/Mid/High)

ID Range Default Change
lowRatio/midRatio/highRatio 0.2 - 20.0 (was 1.0 - 20.0) 2.5 / 1.8 / 2.0 (unchanged) Lower bound widened for upward compression/expansion (sourced: Weiss DS1-MK3's documented "1:5" endpoint). Skewed so 1:1 sits at knob centre.
lowRangeEnabled/midRangeEnabled/highRangeEnabled (new) Off / On Off New.
lowRange/midRange/highRange (new) 0 - 30 dB 12 dB New; only active while the corresponding RangeEnabled is on.

Key design points

  • ratio == 1.0 is a bit-exact null, special-cased independently of Knee/Range (not just floating-point-close) - the taper the brief calls for.
  • Same formula, both directions. The existing closed-form curve is evaluated unconditionally for any ratio > 0 now; the (ratioInverse - 1) term flips sign, cutting for ratio > 1 and boosting for ratio < 1. No naive inversion, no second formula.
  • Range is bidirectional and unsigned - bounds cut or boost to +-Range dB, applied via a defensive unlimitedRangeDb (500 dB) sentinel when disabled that's verified to be a true no-op for this engine's own realistic parameter ranges (see KneeGainComputer.h's doc comment for the derivation and float-overflow-safety margin).
  • Fresh v0.3.0 defaults are bit-identical to v0.2.0: every Ratio default sits unchanged inside the widened range, RangeEnabled defaults off everywhere, and a dedicated test proves a BandCompressor driven through the v0.3.0 API at shipped defaults produces the same measured gain reduction, on real audio, as one that never touches the Range API at all.
  • v0.2.0-shaped state/presets import tolerantly - the six new parameter IDs resolve to off/12 dB via APVTS's existing "missing ID keeps current value" mechanism, no special-case code, verified explicitly.
  • Two factory presets extended to showcase the capability (Density Glue's Mid band now genuinely upward at 0.7:1; Parallel-Style Density pushed to 0.6:1 across all bands with Range engaged as the safety net that makes the extremity usable); the other six presets unchanged in sound, gaining the new keys at neutral defaults.

Deferred (tracked, not implied as parity)

Test plan

  • ctest --test-dir build - 84/84 green locally (up from 73), including:
    • Bit-exact ratio == 1.0 null (Knee/Range-independent)
    • Upward-compression transfer curve matches its closed-form expected dB, quantitatively
    • Range clamp bounds gain change at both the pure-math and real-audio levels
    • Range-disabled default reproduces v0.2.0 bit-for-bit (4-arg vs. 6-arg overload equivalence, and untouched-vs-explicitly-disabled API parity)
    • v0.2.0-to-v0.3.0 state migration tolerance
    • "Fresh v0.3.0 instance is bit-identical to v0.2.0 defaults" (parameter values + Range neutrality + processed audio)
    • NaN/Inf sweeps extended to the new Ratio floor and Range extremes, plus a dedicated extreme-upward-ratio robustness test (with an explicit note on why literal audio-feedback self-divergence is out of scope, matching a delay-with-feedback's own accepted risk profile)
    • Preset round-trip for all 8 factory presets, including the two extended ones
  • Triptych_Standalone builds cleanly (Debug, arm64)
  • Manual review: no allocation/lock/logging introduced in BandCompressor::process()/KneeGainComputer/TriptychEngine (grepped the diff)
  • CI: macOS + Windows matrix build, pluginval (not run locally per repo convention)

Docs

docs/design-brief-v3-dynamics.md (new), docs/research-notes.md (v0.3.0 addendum), docs/manual.md, docs/presets.md, docs/architecture.md, CHANGELOG.md all updated. CMakeLists.txt version bumped to 0.3.0.

Note for the next GUI batch

The M3 faceplate layout-manifest for Triptych (.scaffold/gui-assets/faceplate-triptych-v1, outside this repo) needs to be regenerated to include the six new v0.3.0 parameter IDs before that GUI work runs.

Extends every band's Ratio parameter below 1:1 (0.2:1-20:1, widened from
1:1-20:1) for genuine upward compression/expansion: the same closed-form
transfer curve v0.2.0 already used for downward compression now boosts
signal above threshold for ratio < 1 instead of cutting it, tapering
through a bit-exact ratio == 1.0 null point. Adds a new per-band Range
clamp (lowRange/midRange/highRange, 0-30 dB, default 12 dB, off by
default) bounding gain change - cut or boost - to a maximum dB, the
reference-class safety valve that keeps an aggressive upward setting
musically usable. Two factory presets (Density Glue, Parallel-Style
Density) extended to showcase the new capability.

Fresh v0.3.0 defaults are bit-identical to v0.2.0 (Range off everywhere,
Ratio defaults unchanged); v0.2.0-shaped state/presets missing the six
new parameter IDs load tolerantly. Test suite broadened from 73 to 84
cases. See docs/design-brief-v3-dynamics.md and docs/research-notes.md's
v0.3.0 addendum for the sourced rationale.
@yves-vogl
yves-vogl merged commit ddc762a into main Jul 17, 2026
2 checks passed
@yves-vogl
yves-vogl deleted the feat/v0.3.0-dynamic-multiband branch July 17, 2026 23:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant