docs: close out Θ-W1-T3 and Θ-W2-T2/T3 into COMPLETED; add the filter directory to the architecture map

This commit is contained in:
2026-07-30 11:16:21 -04:00
parent b9d85161e5
commit 25c63fb807
3 changed files with 98 additions and 150 deletions
+80
View File
@@ -84,3 +84,83 @@ FX-button drop path.
explicitly a soak ("a single pass is not a gate") and item 6 needs a live container
drop; both require REAPER and are outstanding. The code is merged; the acceptance
gates are not closed.
### Θ-W1-T3 — filter-dsp-port
Lands the per-voice resonant filter as a standalone pure module
(`core/instrument/engine/filter/`, five files: `filter_params`, `filter_coeffs`,
`filter_morph`, `filter_saturate`, `voice_filter`) — concrete `VoiceFilter` type, no
vtable, no allocation in `process()`. **No call site**; Θ-W2-T1 wires it into the voice
path.
**Deviations from spec — the spec itself changed mid-flight, headline first:**
- **The Cortex-M4 biquad port was superseded entirely by a TPT/SVF topology, Daniel's
call.** Measurement found the firmware's high-pass resonance feedback tap vestigial:
its stated rationale was inverted (the HP numerator approaches 1 as cutoff falls, not
zero — it's the LP numerator that collapses), it *reduced* HP resonance everywhere it
ran, and it carried unwanted sample-rate and input-level dependence. It was a Q15
fixed-point workaround for a ~17-bit cancellation float32 doesn't suffer. Daniel's
ruling on the resulting level-dependent resonance bloom: *"was a feature on the
hardware (one knob colorful HP for master FX), wrong choice for this approach."*
- **Two discrete modes (HP/LP) became a continuous morph, with two selectable morph
laws** — HP→BP→LP (default) and HP→notch→LP (Oberheim SEM) — chosen at `prepare()`
via a `MorphLaw` enum on `FilterSettings`. Zero per-sample cost, verified by diffing
emitted assembly (byte-identical between laws).
- **A configurable drive stage was added:** an in-loop soft limiter on the band-pass
integrator state, normalized 0..1, with a radial dial planned for Θ-W2. Drive at 0 is
bit-exact linear.
- **`Biquad1PoleLP` was struck (Daniel's call) and never ported.**
- **Q spans the full 0.110 with √2 at the control centre**, replacing the firmware's
0.707-floored mapping — as originally specified.
- **No reference sample rate exists anywhere in the module** — rate reaches the DSP
only via `g = tan(π·fc/sr)`. An interim fix that anchored a feedback tap to a
`1/48000` constant was superseded by the rewrite.
- **The rewrite fixed a float32 conditioning defect the biquad carried:** Direct Form I
measured 27% peak error at 20 Hz / 192 kHz; TPT measures +0.034%.
- **Still open, deliberately:** drive's maximum depth (4.0, set by measurement — at 64
the resonant peak inverted below passband) and the absence of makeup gain both await
an ear pass against the real dial.
- **The module has no call site** — integration is Θ-W2-T1, which also carries three
recorded decisions of its own: the envelope-order choice (drive is level-dependent, so
pre- vs post-envelope placement is sound-defining), the drive dial's calibration, and
the fact that drive authority varies ~11 dB across the morph sweep.
### Θ-W2-T2 — stereo-waveform-lanes
Delivered as specified: two stacked lanes, L above R, in stereo mode; one lane in mono;
overlays draw once at full stacked height.
**Deviations from spec:**
- **A mono source under stereo mode draws one lane**, not two — lane count keys off
`stereoMode && sourceChannels >= 2`, not channel mode alone, since a mono capture in
stereo mode is dual-mono and a second lane would be the redundant duplicate the spec
forbids. Review confirmed this is the only reading consistent with the decode path.
- The full-height overlay contract Θ-W3 and Θ-W4 consume is **type-enforced, not
merely documented**: an `OverlayArea` wrapper type that lane rects cannot satisfy.
- A single-slot per-channel PCM cache was added to the editor session, invalidating
alongside the existing PCM cache.
### Θ-W2-T3 — toolbar-and-piano-strip
Delivered as specified: one toolbar font, no zone-count label, full-width piano strip,
uniform key widths, note-name tooltips, root displayed and settable.
**Deviations from spec:**
- **The non-uniform key widths were integer quantisation, not aliasing** — the old
`keyEdgeToX` truncated an exact rational, alternating 6px/7px. Θ-W6's general
antialiasing audit inherits nothing on key *widths* as a result, though key *edges*
are still drawn unantialiased.
- **Uniform integer key widths and gap-free edge-to-edge tiling are mutually
exclusive** — 75 white keys do not divide an arbitrary width. The residue now lands
in symmetric end gutters, 37px each side at the shipped 840px default — the maximum
of a sawtooth with period 75px of window width. Daniel accepted this provisionally,
pending how it looks in REAPER.
- The whole control run moved into the toolbar row, not just preview and mono/stereo —
the full-width strip left the velocity knob and curve button nowhere else to go.
- Root drag became absolute-tracking rather than pixel-delta, since black keys
overlaying white give no single pixels-per-semitone rate.
- **Width uniformity is guaranteed in client pixels only.** Nothing in the instrument
consumes a DPI scale factor, so host-side scaling is unverified — Θ-W6 already
carries a "confirm the fix survives DPI scaling" item and now genuinely inherits it.
- A stale-hover latch was fixed across **all** drag kinds and both drag-termination
paths, wider than the strip work that surfaced it.
+15 -148
View File
@@ -140,65 +140,13 @@ collision is real and the serialization is the correct answer.
### Θ-W1 — Collapse and re-seam
**Θ-W1-T1 (`zone-retirement`) and Θ-W1-T2 (`capture-handoff-bugs`) have landed** — see
`docs/COMPLETED.md`. Between them: the zone subsystem is retired and the wave's two
responsibility seams (the `sampler_core` split, the Sample-face band split) are in place;
the two extension-side capture-handoff defects are fixed. **One track remains:**
Θ-W1-T3 (`filter-dsp-port`), which lands the filter DSP as a standalone pure module —
so that from W2 on, tracks fill bands and call modules rather than fighting over
god-TUs.
---
#### Θ-W1-T3 — `filter-dsp-port`
**Goal.** Land Daniel's Cortex-M4 resonant high/low-pass filter as a pure, tested
module with the settled parameter ranges — **no call site**, so it is disjoint from
every other track by construction.
**Consolidates item 2 (DSP half).** The integration half is Θ-W2-T1.
**Surface boundary — owns:** a new `src/core/instrument/engine/filter.{h,cpp}` (name
[propose]) plus its `filter_tests` target and the CMake row. No existing file is edited
except `CMakeLists.txt`.
**EXTERNAL PREREQUISITE — this track cannot start without it.** The filter processor is
based on Daniel's own Cortex-M4 resonant high/lowpass filter code. **That code is an
input Daniel supplies at implementation time** — it is not in this repo, and neither
this plan nor `TODO-1.0.md` characterizes it beyond "resonant high and lowpass."
Dispatching this track before the source is in hand is a stall, not a start.
**Behavior.**
- **Modes:** high-pass and low-pass now. **Modes beyond the pass filters are explicitly
deferred to later** — do not build a mode-extension framework for them.
- **Cutoff:** the full audio spectrum, **log scaled**, from fully open to fully closed.
- **Q:** **0.1 to 10** (the same range as the curve exponent), scaled so **√2 sits at
the center** of the control.
- **State is per-voice.** The module carries no shared/instance-wide state; each
sounding voice runs its own instance with its own coefficients and history.
- **Concrete type, no interface.** Two modes select by branch or compile-time-known
dispatch. No `IFilter`, no virtual `tick()` — this lands directly on the per-voice
per-sample path.
- Sample-rate-aware by parameter, not by hardcoded constant (the standing
no-hardcoded-sample-rates ruling).
**Acceptance criteria.**
- The module builds and tests without REAPER, VST3, or a DAW.
- Cutoff sweeps the full audio spectrum on a log scale, from fully open to fully closed,
with the mapping asserted in tests at the endpoints and the decade midpoints.
- Q spans 0.1 → 10 with √2 at the control's center — asserted, not eyeballed.
- High Q measurably emphasizes the cutoff region (resonance) in **both** HP and LP
modes; the test asserts the resonant peak, not just monotone rolloff.
- Coefficient updates are stable across a full-range cutoff sweep at audio rate — no
blow-up, no NaN, no denormal stall.
- No virtual dispatch and no allocation in the per-sample entry point.
**Open questions.**
- **Module name and file placement [propose]** — `core/instrument/engine/filter` is the
obvious home; confirm at review.
- **Whether mode is a runtime branch or a compile-time-known dispatch [propose]** —
decided by what the ported DSP actually costs per sample; the constraint is
"no vtable," not "no branch."
**All three tracks have landed**Θ-W1-T1 (`zone-retirement`), Θ-W1-T2
(`capture-handoff-bugs`), and Θ-W1-T3 (`filter-dsp-port`) — see `docs/COMPLETED.md` for
the full narrative of each. Between them: the zone subsystem is retired, the wave's two
responsibility seams (the `sampler_core` split, the Sample-face band split) are in
place, the two extension-side capture-handoff defects are fixed, and the filter DSP has
landed as a standalone pure module with no call site — Θ-W2-T1 integrates it into the
voice path.
---
@@ -210,9 +158,14 @@ band-stack allocator that T2 and T3 fill; and W1-T1's key-range answer, which de
what T3's piano strip displays. Authoring any of this against the per-zone model means
writing storage plumbing W1 deletes.
**Three tracks, in priority order.** They are disjoint by band: T1 owns the parameter
model and the deck band, T2 the waveform band, T3 the chrome band. None re-allocates the
band stack.
**Θ-W2-T2 (`stereo-waveform-lanes`) and Θ-W2-T3 (`toolbar-and-piano-strip`) have
landed** — see `docs/COMPLETED.md`. Between them: the waveform band now shows both
channels in stereo mode behind a type-enforced full-height overlay contract, and the
chrome band's toolbar and piano strip are cleaned up per spec. **One track remains:**
Θ-W2-T1 (`filter-voice-path`), which puts the filter into the per-voice signal path and
gives it its deck. The three tracks were disjoint by band — T1 owns the parameter model
and the deck band, T2 owned the waveform band, T3 the chrome band — and none
re-allocates the band stack.
#### Θ-W2-T1 — `filter-voice-path`
@@ -279,92 +232,6 @@ mod sources, parameter storage side, neutral default) closed in the source doc.
---
#### Θ-W2-T2 — `stereo-waveform-lanes`
**Goal.** Show both channels in the waveform visual when the instrument is in stereo
mode, without duplicating any overlay.
**Consolidates item 7.**
**Surface boundary — owns:** `core/instrument/ui/waveform_view` and the **waveform
band** of the Sample face (geometry, paint, input). Reads channel mode; does not own
the mono/stereo control (that widget relocates in T3 — coordinate on state, not on
layout).
**Behavior.**
- In **stereo mode**, the waveform visual shows **both L and R channels, left on top**
— two stacked lanes.
- In **mono mode**, a single channel shows — no redundant duplicate lane.
- The display keys off the active channel mode.
- **Overlays draw once, at full height.** Overlays that ride the waveform — the envelope
overlay, markers, and item 9's loop region when it lands — draw **once at full height
across both stacked lanes**, not per lane. Land the full-height overlay contract now
even though the envelope overlay is reworked in W3 and the loop region arrives in W4;
both consume this contract.
- **Stereo processing is linked.** One editor, one set of controls governing both
channels — no per-channel parameter divergence, no per-channel editing surface.
**Acceptance criteria.**
- A stereo capture in stereo mode shows two stacked lanes, L above R, each a true view
of its channel's content — an asymmetric-channel capture visibly differs between lanes.
- Mono mode shows exactly one lane. Switching modes updates the display accordingly.
- In stereo mode, waveform-riding overlays render **once** at full stacked height — no
duplicated per-lane copies — and stay legible across both lanes.
- No per-channel controls appear; every edit applies identically to both channels.
**Open questions.** None — item 7's overlay-layout question closed in the source doc
("one full height; stereo linked processing, one editor").
---
#### Θ-W2-T3 — `toolbar-and-piano-strip`
**Goal.** Clean up the top toolbar, free the note-range piano strip to full width, and
make the key pattern uniform and self-describing.
**Consolidates item 12.**
**Surface boundary — owns:** `core/instrument/ui/keyboard_strip`, `core/ui/tooltip` (as
consumer), and the **chrome band** of the Sample face (toolbar layout, title, piano
strip — geometry, paint, input). Reads the band allocator; does not change it.
**Behavior.**
- **Font cleanup.** One consistent font treatment across the top toolbar text.
- **Zone count label removed** from the title. (Doubly settled — item 16 removed its
referent in W1.)
- **Preview and mono/stereo controls relocate** next to the browse and zoom buttons,
freeing the **note-range piano strip to take the full width**.
- **Uniform piano keys.** Some keys currently render skinnier than others — Daniel
suspects pixel aliasing, but the requirement stands regardless of cause: keys of the
same class render at uniform width. (If aliasing turns out to be the cause, the
*general* audit is item 13 — Θ-W6; the uniform-width requirement is this track's
regardless.)
- **Note-name tooltips.** Hovering a piano key shows its note value (C4 etc., DAW
convention), following the instrument's existing tooltip conventions.
- **What the strip means with no zones.** With zone bars gone, the strip's surviving
jobs are the **root display/affordance** (item 15's answer round makes root explicitly
resample-stable, so it must remain visible and settable here) and the note-name
tooltips — plus a range display only if W1-T1's key-range answer kept one.
**Acceptance criteria.**
- Top toolbar text renders in one consistent font treatment; the title carries no zone
count.
- Preview and mono/stereo sit adjacent to browse/zoom; the piano strip spans the full
editor width.
- **Same-class keys are equal pixel width at any window width and at any DPI scale.**
- Hovering any piano key shows its note name in a tooltip.
- The root note is displayed on and settable from the strip.
**Open questions.**
- **The strip's exact contents [propose]** — jointly owned with item 16's key-range
answer, which W1-T1 delivers. Propose the final inventory (root affordance +
tooltips, ± range display) at this track's review.
- **The preview button's glyph is not this track's** — item 11 (Θ-W4-T2) replaces its
inner text with a glyph in the position this track gives it. Ship the button with its
current text; do not pre-empt the glyph.
---
### Θ-W3 — The staged envelope system
**Depends on W2 for:** the filter envelope's existence — items 1, 8, and 14 govern