Skip to content

M1: multiband width, Auto Mono Safety, Haas Mode, correlation meter, broadened tests, manual#8

Merged
yves-vogl merged 4 commits into
mainfrom
feat/m1-dsp-docs
Jul 14, 2026
Merged

M1: multiband width, Auto Mono Safety, Haas Mode, correlation meter, broadened tests, manual#8
yves-vogl merged 4 commits into
mainfrom
feat/m1-dsp-docs

Conversation

@yves-vogl

Copy link
Copy Markdown
Contributor

Summary

Completes the M1 — DSP completion & test coverage milestone and prepares Firmament for a v0.1.0 tag.

DSP (issue #1, "Complete and refine the DSP")

  • Multiband width: independent Low Width (0-200%, default 0%) scales the Side signal's low band (below Bass Mono Freq), while Width now governs the high band whenever the crossover is engaged. Low Width's default reproduces the v0.1 "bass mono forces the low band to silence" behaviour exactly (verified in tests/MultibandWidthTests.cpp).
  • Auto Mono Safety: an optional, correlation-driven Side attenuation that reins in the stereo image when the input goes strongly out-of-phase. Only ever scales Side, so it can never break the L + R == 2 * Mid mono-sum invariant.
  • Correlation/phase meter (DSP): a running, leaky-integrated (200 ms) input-correlation estimate, exposed via FirmamentEngine::getCorrelationValue() / FirmamentAudioProcessor::getCorrelationMeterValue(). Drives Auto Mono Safety internally.
  • Haas Mode: an optional post-M/S-decode Right-channel delay (0-40 ms, juce::dsp::DelayLine), off by default - a deliberate, clearly-documented exception to the mono-sum invariant, since delaying one channel relative to the other is fundamentally incompatible with an exact sum guarantee.
  • PluginEditor extended with knobs/toggles for every new parameter (still the plain v0.1-style editor - custom LookAndFeel is M3).

Notable finding, corrected in docs: juce::dsp::LinkwitzRileyFilter's dual-output low+high bands sum to a flat-magnitude allpass, not an exact reconstruction of the input - this is JUCE 8.0.14's own documented behaviour, confirmed empirically during development. The original v0.1 "Bass Mono" feature was never affected (it only ever discards the low band rather than re-summing), but a pre-existing doc comment overclaimed exact reconstruction; corrected in docs/architecture.md and FirmamentEngine.h/.cpp before it could mislead future multiband work.

Deferred / explicitly out of scope

  • A visible correlation/phase meter widget in the GUI - the DSP value is fully computed, tested, and exposed (getCorrelationMeterValue()), but the visual meter component itself belongs to the M3 "Custom GUI / LookAndFeel" issue ("Add metering where relevant to the plugin"), not M1. Not closing that issue.
  • 3+ band multiband width - the M1 issue text didn't specify band count; a 2-band (low/high) split reusing the existing single crossover was chosen as the safe, minimal-risk interpretation. A finer multiband split (e.g. low/mid/high) is a reasonable follow-up but not required here.
  • No bus-layout changes were made (no sidechain input added) - out of scope for this issue and correctly not attempted.

Tests (issue #2, "Broaden test coverage")

Grew the suite from 24 to 49 Catch2 test cases:

  • SampleRateSweepTests.cpp - unity round-trip, latency, and finiteness across the full 44.1-192 kHz range, plus a mid-session sample-rate-change test.
  • BusConfigTests.cpp - direct isBusesLayoutSupported() accepted/rejected coverage (mono/stereo in, stereo out, surround/mono-out/disabled rejections) plus full-signal-path smoke tests with every M1 stage engaged.
  • LongRunStabilityTests.cpp - ~1000-block randomised-automation and ~500-block sustained-worst-case NaN/Inf stability sweeps.
  • Extended RobustnessTests.cpp's extreme-value and rapid-automation tests to cover every new parameter.
  • Dedicated coverage per M1 feature: MultibandWidthTests.cpp, AutoMonoSafetyTests.cpp, CorrelationMeterTests.cpp, HaasModeTests.cpp.
  • ParameterTests.cpp/StateTests.cpp updated for the 4 new parameters (count, defaults, ranges, state round-trip).

All existing v0.1 null/reference/mono-collapse/latency tests remain green and unmodified in intent (only extended with new parameters at their v0.1-equivalent defaults).

Documentation

  • New docs/manual.md - full user manual (what Firmament is, where it sits in a symphonic-metal chain, signal flow, complete 7-parameter reference, mixing tips).
  • docs/architecture.md - updated diagram/sections for the new signal path, plus the LinkwitzRileyFilter documentation correction above.
  • README.md - expanded feature list, added a parameter table, corrected the roadmap table to match the actual GitHub milestones.
  • CLAUDE.md - updated Status/DSP sections.
  • CHANGELOG.md - moved Unreleased content into a new [0.1.0] - 2026-07-14 section.

Test evidence (local, Debug, macOS arm64)

export CPM_SOURCE_CACHE="$HOME/.cache/CPM"
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build --target Tests Firmament_Standalone --parallel 3
ctest --test-dir build --output-on-failure
  • Tests and Firmament_Standalone build cleanly (no warnings from new code).
  • ctest: 100% tests passed, 49/49, ~8s total.

CMake project VERSION is already 0.1.0 (project(Firmament VERSION 0.1.0 ...), JUCE 8.0.14's juce_add_plugin inherits PROJECT_VERSION when no explicit VERSION is passed - confirmed via JUCEUtils.cmake), so no version bump was needed.

Closes #1
Closes #2

…and Haas Mode

Completes the M1 "Complete and refine the DSP" milestone:

- Multiband width: an independent Low Width (0-200%, default 0%) control
  scales the Side signal's low band (below Bass Mono Freq) separately from
  Width, which now governs the high band whenever the crossover is engaged.
  Low Width's default reproduces the v0.1 "bass mono" behaviour exactly.
- Auto Mono Safety: an optional, correlation-driven Side attenuation that
  reins in the stereo image whenever the input goes strongly out-of-phase.
  Only ever scales Side, so the mono-sum invariant holds regardless.
- Correlation/phase meter (DSP): a running, leaky-integrated input-
  correlation estimate, exposed via FirmamentEngine::getCorrelationValue()
  and FirmamentAudioProcessor::getCorrelationMeterValue(). Drives Auto Mono
  Safety internally; not yet wired to a GUI meter widget (M3 scope).
- Haas Mode: an optional post-decode Right-channel delay (0-40 ms) via
  juce::dsp::DelayLine, trading the exact mono-sum guarantee for a
  precedence-effect widening technique. Off by default.
- PluginEditor: extended with knobs/toggles for every new parameter,
  matching the existing plain v0.1-style editor.

All four features are gated off/at-default by default, so the existing v0.1
signal path and its tests are unchanged. Verified during implementation that
juce::dsp::LinkwitzRileyFilter's dual-output low+high sum is a flat-
magnitude allpass, not an exact reconstruction of the input (per JUCE 8.0.14's
own class documentation) - this only affects multiband width's design/docs,
not the original bass-mono behaviour, which never relied on summing bands.
Grows the suite from 24 to 49 test cases (issue "Broaden test coverage"):

- Dedicated coverage for every M1 addition: MultibandWidthTests.cpp,
  AutoMonoSafetyTests.cpp, CorrelationMeterTests.cpp, HaasModeTests.cpp.
- SampleRateSweepTests.cpp: unity round-trip, latency, and finiteness
  checks across the full 44.1-192 kHz range, plus a mid-session sample-
  rate-change (re-prepare) test.
- BusConfigTests.cpp: direct coverage of isBusesLayoutSupported()'s
  accepted (mono/stereo in, stereo out) and rejected (mono out, surround,
  disabled bus) configurations, plus full-signal-path smoke tests for both
  accepted layouts with every M1 stage engaged.
- LongRunStabilityTests.cpp: ~1000-block randomised-automation and
  ~500-block sustained-worst-case NaN/Inf stability sweeps.
- Extended the existing extreme-parameter-value and rapid-automation tests
  in RobustnessTests.cpp to exercise every new parameter.
- ParameterTests.cpp/StateTests.cpp: updated parameter count/defaults and
  added state round-trip coverage for the four new parameters.

All test cases run in well under a second each locally (Debug, macOS); the
full suite completes in ~8s.
- docs/manual.md: full user manual - what Firmament is, where it sits in a
  symphonic-metal chain, signal-flow description, complete parameter
  reference (range/default/unit/musical description) for all 7 parameters,
  and mixing tips.
- docs/architecture.md: updated Mermaid diagram and per-stage sections for
  multiband width, Auto Mono Safety, the correlation/phase meter, and Haas
  Mode; corrected the pre-existing "sum reconstructs the input exactly"
  claim about LinkwitzRileyFilter's dual-output bands to match JUCE 8.0.14's
  own documented behaviour (flat-magnitude allpass, not identity).
- README.md: expanded feature list, added a parameter table, corrected the
  roadmap table to match the actual GitHub milestones (M1 DSP completion,
  M2 presets/state, M3 GUI & accessibility, M4 release).
- CLAUDE.md: updated Status/DSP sections to reflect the M1 signal path and
  test count.
Documents everything added in this M1 DSP-completion pass under a new
[0.1.0] - 2026-07-14 section, Keep-a-Changelog style.
@yves-vogl
yves-vogl merged commit c094a51 into main Jul 14, 2026
2 checks passed
@yves-vogl
yves-vogl deleted the feat/m1-dsp-docs branch July 14, 2026 16:34
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.

Broaden test coverage Complete and refine the DSP

1 participant