Skip to content

feat: v0.2.0 deep-dive voicing pass, M2 presets, DE localisation, app icon#20

Merged
yves-vogl merged 1 commit into
mainfrom
feat/v0.2.0-deep-dive-and-presets
Jul 16, 2026
Merged

feat: v0.2.0 deep-dive voicing pass, M2 presets, DE localisation, app icon#20
yves-vogl merged 1 commit into
mainfrom
feat/v0.2.0-deep-dive-and-presets

Conversation

@yves-vogl

Copy link
Copy Markdown
Collaborator

Summary

Research-driven rework of Overture's Drive -> Clipper -> Tone chain (docs/design-brief.md, docs/research-notes.md), the M2 preset system, a German i18n frame, and housekeeping (app icon wiring, release workflow fix).

DSP rework

  • biteAmount ("Bite", 0-100%, default 65%): frequency-dependent gain inside the drive-to-clipper path via a fixed ~700 Hz low-shelf (anchored to the sourced ~720 Hz reference-circuit feedback corner), applied to the oversampled signal immediately before the voicing dispatch. Bass is clipped less than treble - the reference circuit's actual tightening mechanism, not an approximation via a separate pre-clip filter (Tight keeps doing that unchanged job). Skipped entirely (not unity-gain) at 0% - bit-identical to v0.1's flat-gain clipper.
  • kneeSoften ("Knee Soften", 0-100%, default 40%): drive-dependent knee softening (src/dsp/KneeSoftening.h), blending each voicing's raw output toward tanh(raw), scaled by (kneeSoften/100) * driveIntensity. Applies to all three voicings including Hard Clip (zero knee at 0%, any Drive). Algebraically always 0 at kneeSoften = 0, so Drive-invariant, matching v0.1.
  • asymmetryAmount ("Asymmetry", 0-100%, default 40%): exposes the Asymmetric voicing's previously-fixed bias (a = 0.2) as a 0.0-0.5 mapped control. Default reproduces v0.1's fixed bias exactly.
  • biteTilt ("Bite Tilt", -100%..+100%, default 0%) replaces tone: a single 2nd-order RBJ high-shelf around a fixed ~3 kHz corner, bidirectional (v0.1's Tone could only ever darken). Flat is a true no-op (filter skipped, not unity-gain). A v0.1 session's tone value is lossily migrated to an equivalent biteTilt position on load (OvertureAudioProcessor::setStateInformation()), detected before apvts.replaceState() runs.
  • Defaults: tight 130 -> 100 Hz (midpoint of the sourced 80-120 Hz workflow sweet spot), drive 8 -> 3 dB (best-documented "near-zero drive, Level does the pushing" workflow). Voicing enum indices remain frozen.
  • Every new IIR filter (Bite shelf, Bite Tilt shelf) uses the same allocation-free ArrayCoefficients + ovtr::applyBiquadCoefficients pattern main already established for Tight/Tone (issue Unconditional per-block heap allocation in OvertureEngine::process() via IIR::Coefficients::make{High,Low}Pass #12) - tests/AllocationTests.cpp extended accordingly.

M2 preset system

Copied verbatim from the Nave pilot implementation (src/presets/PresetManager.{h,cpp}, src/presets/PresetBar.{h,cpp}) per .scaffold/specs/preset-system-m2.md. Nine factory presets (docs/presets.md): a certified Default (= Classic Boost's settings, the shipped defaults) plus the brief's eight named presets (Clean Push, Classic Boost, Drop-Tune Tight, Smooth Push, Own Distortion, Fuzz-Adjacent Lead, Parallel Grit, De-Fizz Cleanup).

i18n

German localisation of the preset-bar frame (resources/i18n/de.txt, src/presets/Localisation.{h,cpp}), auto-selected via SystemStats::getUserLanguage(). Core/DSP parameter names are never translated (verified by tests/LocalisationTests.cpp).

Housekeeping

  • ICON_BIG "docs/assets/icon.png" added to juce_add_plugin() - previously missing (no patch release had shipped since the icon was added to docs/README).
  • .github/workflows/release.yml: added an idempotent create-release job (both platform jobs now needs: create-release) - suite-wide fix ported from basilica-audio/crypta main, folded into this PR per the orchestrator's flow addition (nothing else had touched this file).
  • docs/architecture.md, docs/manual.md, README.md, CHANGELOG.md, CLAUDE.md updated for v0.2.0.

Scope note on the "0% = v0.1-identical" guarantee

The brief's guarantee 1 ("bite_amount = 0 and knee_soften = 0 reproduce v0.1's exact transfer function bit-for-bit... for all three voicings, at every Drive level") is tested at the clipper-transfer-function level (tests/ClipperVoicingTests.cpp's new backward-compatibility test, comparing the composed dispatch against the unmodified ClipperVoicings::processSample primitive bit-for-bit) rather than as a whole-engine snapshot diff against v0.1's actual binary output. Tight/Drive/Oversample/Level/Mix are structurally unchanged code (only Tight/Drive's default values moved), so a whole-engine null test on those specifically would be redundant; Bite/Bite Tilt's own "skip when off" behaviour is separately verified by their own dedicated spectral/no-op tests.

Test plan

  • Full local suite green: cmake --build build --target Tests Overture_Standalone && ctest --test-dir build --output-on-failure - 96/96 test cases, 21278/21278 assertions passed (up from 54 on main).
  • No audio-thread allocations while any v0.2.0 control (Bite/Knee Soften/Asymmetry/Bite Tilt) is being automated (tests/AllocationTests.cpp, extended from issue Unconditional per-block heap allocation in OvertureEngine::process() via IIR::Coefficients::make{High,Low}Pass #12's original Tight/Tone guard).
  • Backward-compatible null cases: bite_amount = 0/knee_soften = 0 reproduce v0.1's clipper dispatch bit-for-bit, all three voicings (tests/ClipperVoicingTests.cpp).
  • Frequency-dependent gain proof (Bite): low/high-frequency clip-onset peak gap grows monotonically with biteAmount at a fixed Drive (tests/EngineTests.cpp).
  • Drive-dependent knee-softening proof: waveform-shape change from softening is measurably larger at high Drive than low Drive at a fixed kneeSoften; zero at kneeSoften = 0 regardless of Drive (tests/EngineTests.cpp).
  • Asymmetry sweep: 0-100% maps monotonically to bias 0.0-0.5; default reproduces v0.1's a = 0.2 (tests/ClipperVoicingTests.cpp).
  • Bite Tilt bidirectionality: flat is a true no-op, negative/positive monotonically darken/brighten, -100% is at least as dark as v0.1's fully-closed Tone at a matched test frequency (measured directly against v0.1's own retired formula, not assumed) (tests/EngineTests.cpp).
  • State migration: v0.1-only session loads without crashing, tone maps to the documented biteTilt equivalent, new parameters fall back to their own defaults (tests/StateTests.cpp).
  • Preset system: save/load round-trip, forward/backward-tolerant import, wrong-plugin/format refusal, every factory preset parses/loads/produces no NaN/Inf/silence, default resolution order, dirty-flag lifecycle, prev/next wrap-around, rename/delete guards, single-file and bank export/import (tests/PresetManagerTests.cpp, 16 cases).
  • i18n: de.txt parses, every TRANS() key used by the preset frame is present, parameter names are verifiably absent from the mapping (tests/LocalisationTests.cpp).
  • NaN/Inf robustness: all four new controls swept to extremes combined with extreme Drive/Tight/Level, every voicing (tests/RobustnessTests.cpp).

🤖 Generated with Claude Code

…n, app icon

Research-driven rework of the Drive -> Clipper -> Tone portion of the
signal chain (docs/design-brief.md, docs/research-notes.md), sourced from
published tube-screamer circuit analyses, a purpose-built commercial
pedal's own documentation, and publicly reported artist workflows - not
measured against physical reference hardware.

New controls inside the oversampled block: bite_amount (frequency-
dependent gain INSIDE the drive-to-clipper path via a ~700 Hz low-shelf,
0% bit-identical to v0.1), knee_soften (drive-dependent knee rounding via
src/dsp/KneeSoftening.h, 0% bit-identical to v0.1), and asymmetry_amount
(exposes the previously-fixed Asymmetric voicing bias, default reproduces
v0.1's a=0.2 exactly). Tone (cut-only, 1-8 kHz) is replaced by Bite Tilt,
a bidirectional +/-3 kHz shelf that can darken and brighten; an old v0.1
session's tone value is lossily migrated onto an equivalent Bite Tilt
position on load. Defaults changed: Tight 130 -> 100 Hz (the sourced
80-120 Hz workflow sweet spot's midpoint), Drive 8 -> 3 dB (the
best-documented "near-zero drive, Level does the pushing" workflow). The
Voicing enum's persisted indices remain frozen.

Implements the suite-wide M2 preset system (.scaffold/specs/preset-system-m2.md)
for the first time in this repo: src/presets/PresetManager (factory presets
embedded via BinaryData, user presets on disk, dirty tracking, prev/next,
single-file and zip-bank import/export, user-wins-over-factory default
resolution) and src/presets/PresetBar, copied verbatim from the Nave pilot
implementation per docs/preset-system-notes.md's replication recipe. Ships
with nine factory presets (docs/presets.md: a certified Default plus eight
use-case presets from the brief) and a German frame-string localisation
(resources/i18n/de.txt, selected via SystemStats::getUserLanguage()). The
app icon is now wired into the built AU/VST3/Standalone via ICON_BIG
(previously missing - no patch release had shipped since it was added to
docs/README).

Also folds in the suite-wide release.yml fix: an idempotent create-release
job so gh release upload has an object to attach assets to on a fresh tag
push (both platform jobs now needs: create-release).

96 Catch2 tests green (up from the 54 on main): new DSP-primitive tests
(KneeSofteningTests.cpp), asymmetry-sweep and v0.1 backward-compatibility
regression tests, Bite frequency-dependent-gain and Knee Soften
drive-dependence spectral proofs, Bite Tilt bidirectionality/no-op/
subsumes-v0.1-Tone tests, state-migration tests, the M2 PresetManager
suite, and i18n frame tests - full breakdown in the PR description.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yves-vogl
yves-vogl merged commit 56df634 into main Jul 16, 2026
2 checks passed
@yves-vogl
yves-vogl deleted the feat/v0.2.0-deep-dive-and-presets branch July 16, 2026 21:41
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