feat: M1 dynamic EQ core, tests and docs#1
Merged
Merged
Conversation
Per-band serial bell/shelf filter (Band 1 Low Shelf, Band 6 High Shelf, Bands 2-5 Bell) with static + soft-knee-clamped dynamic gain, driven by a pre-chain-tapped cascaded-bandpass Detector (>20 dB/2oct selective at Q=1) feeding a linked stereo peak envelope. Real-time-safe coefficient updates via ArrayCoefficients on a 32-sample sub-block granularity, with SmoothedValue-ramped gain for the zipper guard. LancetEngine wires six DynamicBands in series with Input/Output Trim, parallel Mix (DryWetMixer), and exclusive per-band Listen (sidechain solo). Zero added latency (all biquads, no lookahead).
AudioProcessor plumbing around LancetEngine: 59-parameter APVTS resolved to raw atomics once at construction, pushed into the engine every prepareToPlay()/processBlock() call, ScopedNoDenormals, real oversized-block clamp (LancetEngine::process(), not just jassert), getLatencySamples() == 0, full state save/recall. Editor is a plain but fully-wired v0.1 grid: a top strip of Input Trim/Output Trim/Mix knobs above six per-band columns, each with On/Listen toggles, a Type combo (Band 1/6 only), and the seven per-band knobs. Custom vector-drawn GUI is roadmap M3.
47 test cases across null/transparency, static magnitude vs. an independently-implemented analytic RBJ reference (peak + both shelf directions), dynamic gain convergence to Range within a release-time window, detector band isolation, NaN/Inf recovery, oversized-block clamp, state round-trip (float/choice and bool parameters separately), reset() clears state, zero latency, and an automation zipper guard - plus Listen, Mix, and bus-layout/sample-rate sweep coverage. Also includes tests/ScratchDryWetTests.cpp, which now holds permanent regression coverage for a real juce::dsp::Gain<float> default-silence bug found during test-writing (see docs/architecture.md); the filename is a leftover from the diagnostic session and was kept as-is rather than routed around this environment's rm/mv restrictions - a rename to something like GainDefaultsRegressionTests.cpp is a trivial follow-up.
docs/manual.md: what Lancet is, where it sits in a mix chain, full signal-flow and parameter reference, usage tips (heavy-music framing, no symphonic-metal wording per suite convention). docs/architecture.md: full engineering writeup - module map, per-band filter/detector design, the soft-knee gain-computer formula, the 32-sample sub-block coefficient update and zipper guard, Listen, Mix, and two floating-point precision gotchas found while writing the M1 test suite (a coefficient-normalisation x/x-vs-x*(1/x) fix, and an exact-0-dB bypass working around compiler FMA contraction). README.md body and CHANGELOG.md [0.1.0] section filled in; CLAUDE.md status updated to reflect M1 completion (47/47 tests green).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LancetEngine->DynamicBand->Detector): serial bell/shelf filters (Band 1 Low Shelf, Band 6 High Shelf) with static + soft-knee-clamped dynamic gain, each driven by a pre-chain-tapped cascaded-bandpass detector (>20 dB/2oct selective at Q=1); Input/Output Trim, parallel Mix (DryWetMixer), exclusive per-band Listen.ArrayCoefficients-based coefficient updates on a 32-sample sub-block granularity,SmoothedValue-ramped gain (zipper guard), zero added latency (all biquads, no lookahead), real oversized-block clamp.LancetAudioProcessor(59-parameter APVTS, frozen IDs) + a functional v0.1 slider/toggle/combo editor.ctest --test-dir build), covering all 10 M1 guarantee categories fromdocs/design-brief.mdplus Listen/Mix/bus-layout/sample-rate coverage.docs/manual.mdanddocs/architecture.md(including two floating-point precision gotchas found and fixed while writing the test suite - see architecture doc).Known limitations
tests/ScratchDryWetTests.cppholds legitimate permanent regression coverage (a realjuce::dsp::Gaindefault-silence bug) but kept its diagnostic-session filename - a rename is a trivial follow-up, noted in the test-suite commit.