docs: record Phase Gamma Wave 1's seven landed tracks in COMPLETED, including T6 which never had a plan entry

This commit is contained in:
2026-08-02 05:44:04 -04:00
parent fefd169d5b
commit 35ab59d7f9
+162
View File
@@ -887,3 +887,165 @@ wrong that refusal costs a working capture. Each track's DAW-verification obliga
is recorded in `docs/PLAN.md`'s Phase Ψ section; `docs/verify-track-scope-multitrack.md` is recorded in `docs/PLAN.md`'s Phase Ψ section; `docs/verify-track-scope-multitrack.md`
is a new standalone verification script on this branch, for Ψ-W3-T1's multi-track is a new standalone verification script on this branch, for Ψ-W3-T1's multi-track
refusal specifically. No human has observed any of these seven behaviors in a DAW. refusal specifically. No human has observed any of these seven behaviors in a DAW.
### Γ-W1-T1 — knob-interaction-law
One consistent interaction and taper law across every variable control in the instrument,
landed before any new control (Rate, Pitch) or VST3 parameter existed so both are authored
into it rather than retrofitted. The taper is extracted into its own pure module,
`core/instrument/ui/param_taper` — the norm↔value maps (ms knobs log-scaled, semitone knobs
log2/centre-expanded) and the modifier vocabulary (`DragModifiers`, `kFineDragScale`, the
four whole-unit Shift snaps), now the single home three consumers read: the knob's needle
(`deck_values`), the AHDSR overlay's schematic axis and its drag inverse
(`envelope_overlay`/`envelope_edit`), and — from a later wave — the VST3 host's
`toPlain`/`toNormalized`. Shift snaps to whole units in the control's displayed category
(ms, semitones, percent, curve-exponent, dB); Ctrl scales the drag by 0.05; Shift+Ctrl
resolves to Shift; a mid-drag modifier press or release re-anchors value and cursor
position so the rate changes without a value jump. `resetDeckParam`'s taper bypass —
writing the default directly rather than round-tripping through `norm → value` — is now
mandatory rather than merely convenient, since the 10 s ceiling is not a power of two the
way the retired 2 s one was.
**The stage-time ceiling moves 2.0 s → 10.0 s** (`kGateStageMaxSeconds` /
`kEnvTimeMaxSeconds`, moved together so they cannot drift), reversing Γ-F3 on Daniel's
later ruling. The AHDSR overlay's schematic axis was re-derived against it: a stage's slot
width is now `slotPx × taperNorm(seconds)` rather than a linear fraction of the ceiling, so
a node's position within its slot is its knob's needle position and a short attack stays
legible at the raised ceiling instead of collapsing under a pixel. Every default gains an
exact normalized preimage under its own taper — the requirement Γ-W4-T1's
`defaultNormalizedValue` depends on, since a host's reset-to-default has no
`resetDeckParam` bypass to fall back on. The filter's four `*Norm` controls (cutoff, Q,
morph, drive) are untouched — their laws are wire-frozen in payload v9 — and the change is
persistence-neutral throughout: the payload stores raw engine doubles, so a project saved
at the old ceiling reloads with identical stored seconds and identical audio.
### Γ-W1-T2 — master-bus-audio
The master bus: a bypassable true-peak limiter, the meter's audio and publication half, and
the plugin's first latency report. New pure modules `core/instrument/engine/limiter` and
`core/instrument/engine/meter_ballistics`. Chain: voice mixer → master gain → limiter →
output bus, with the meter tapped post-limiter. The limiter is a single toggle with no
configurable controls — a baked 0.3 dBTP ceiling, default off, no makeup gain of any kind,
stereo-linked detection so the image never moves. True-peak detection is a 4x-oversampled
sidechain-only detector; the signal path itself is never oversampled. Its gain law is a
sliding minimum of the per-sample target over the lookahead window followed by a moving
average of the same width — every term of that average is a minimum whose own window
contains the sample being gained, so the ceiling holds structurally rather than by a tuned
attack, and the release only ever slows the rise. Switching is a mute, never a blend: the
unlimited signal is emitted at weight 1 or weight 0 and never in between, so the fade
always rides the limited path and the hard edge always lands on the bypassed side.
**Dynamic reported latency**`getLatencySamples()` returns 0 with the limiter off and the
lookahead in samples with it on, driving `restartComponent(kLatencyChanged)` on toggle — is
the plugin's first latency reporting of any kind; nothing in `src/` called
`restartComponent` before this track. Per block the processor publishes relaxed-atomic
peak, clip flag, and max gain reduction with no dB conversion or ballistics on the audio
thread; `meter_ballistics` (pure, unit-tested) does the conversion — instantaneous rise,
20 dB/s fall, a 1.5 s peak hold releasing at the same rate, linear-in-dB scale over
60…+6 dBFS, and a clip latch cleared on request. **Spent the phase's first payload rung**:
`kParamsPayloadVersion` reaches **15**, appending the limiter enable flag as a strict
suffix; a pre-v15 blob lifts to bypassed.
### Γ-W1-T3 — contour-trace-curves
Staged envelope segments now draw as the curve their exponent defines, closing the defect
where the mid-segment knot floated off its own trace — the paint path dropped knots and
joined the remaining vertices with straight strokes even though the exponent was already
in scope, while knot *positioning* had honoured it since Θ-W3-T2. A new pure module,
`curve_tessellate`, joins the non-knot vertices along the same curve `envelopes.h`'s
evaluators use — one point per pixel column at `start + (end start) × curveMap(phi)`
so the drawn stage and the sound it makes cannot diverge; a neutral exponent or a
zero-level span still emits just the two endpoints, matching the straight stroke drawn
before curves existed. All three envelopes (amp, pitch, filter), both play modes, every
sloped stage, share the one fix.
### Γ-W1-T4 — editor-floor-and-row-law
Commits the editor's canvas — the window floor, the width budget it derives from, and
which row each deck group belongs to — so every later UI track in the phase is drawn and
judged at the final window size rather than a size a subsequent wave changes under it.
`kEditorMinWidth` moves 980 → 1190, staying in `sample_bands.h`; `kEditorMinHeight` stays
680 (Γ-F1). `kEditorCeilingWidth` (1280, the hard cap the floor may not exceed) relocates
from `knob_deck.h` into `sample_bands.h` alongside the min-width/min-height pair, since it
is a window fact rather than a deck one; the deck's own width-budget constants — the row
block (1020) and MASTER's reserved width (142) — stay in `knob_deck.h`. The floor is
derived rather than asserted as a literal: `1020 + 12 (gap) + 142 + 2×8 (pad) = 1190`,
leaving 90 px of headroom against the 1280 px ceiling. Row membership becomes a property of
the group id — `DeckRow { Sound, Contour, Spanning }` plus `deckRowFor(DeckGroupId)`, an
exhaustive switch (Sound = PITCH/RATE, FILTER, VELOCITY, VOICE; Contour = PITCH ENV, FILTER
ENV, AMP ENVELOPE; Spanning = MASTER) so a future group left unclassified is a compile
error. Nothing consumes the predicate yet — the two-row arrangement inside this canvas is
Γ-W3-T1's — so at the new floor the deck still packs by the unchanged greedy whole-group
wrap, landing on two rows rather than three; the composition is knowingly interim (PITCH
ENV sits with the sound decks, both rows left-packed with dead space) until Γ-W3-T1 lands
the reflow. No drawing code, descriptor, parameter, or audio changed.
### Γ-W1-T5 — preserve-time-stretch
A real pitch-preserving time-stretcher for Preserve mode, landed a wave ahead of the Rate
control that will drive it so Rate ships onto a finished engine instead of a disposable
stand-in — moved up from a later wave on Daniel's ruling that it was the phase's longest
pole and had no UI dependency. The write rate (duration) and the tap rate (pitch) are
independent, which is the whole mechanism: a new header-only pure module, `time_stretch`,
holds `StretchCursor` — the per-output-frame source-feed schedule, a fractional cursor
carrying its rate debt, loop-wrapped — plus the measured rate bounds and their clamp,
alongside `pitch_shift`'s existing shift-ratio control. Rate 1.0 is exactly one source
frame per output frame with no residue, which is what makes the unity-ratio Preserve read
bit-identical to the pre-stretch engine — the regression floor the track is gated on, since
nothing publishes a non-unity ratio until Γ-W2-T1's Rate knob exists. No new third-party
dependency, no allocation or lock in `process()`, no dispatch on the per-sample path,
buffers sized at voice allocation or reload on `pitch_shift`'s existing pre-warm precedent.
### Γ-W1-T6 — exhaustive-switch gate on pure libraries
Merged as `ee839cf`. **This track has no entry in `docs/PLAN.md`** — the plan's Γ-W1 wave
header states so directly ("the phase's track numbering runs to T7; T6 landed within this
wave but has no entry in this document") — so this record is reconstructed from
`cmake/reasampler_targets.cmake` and the enforcement comment at its confirmed call site,
`src/core/instrument/ui/deck_groups.cpp`, rather than from a spec section.
`reasampler_pure_library()` (`cmake/reasampler_targets.cmake`) now promotes a default-less
`switch` missing an enumerator to a compile error on every pure library: `/we4062` on MSVC,
`-Werror=switch` on GCC/Clang. Neither fired before this track — MSVC's C4062 is off at the
repo's `/W1` default, and GCC/Clang's `-Wswitch` warns without `-Werror`, which this repo
sets nowhere else. The gate is deliberately **not** C4061, which fires even on a switch
that already has a `default:` clause — that would light up every defensive switch in the
tree instead of catching only the deliberately default-less ones, such as
`isLiveDeckParam` and `deck_groups.cpp`'s live-param routing, where a newly added
enumerator must be a compile error rather than a silent fall-through. Later Γ-W1 work
(T4's `deckRowFor`) relies on this gate being in place.
### Γ-W1-T7 — psola-preserve
Preserve's splices become pitch-synchronous. A new pure module,
`core/instrument/engine/period_detect` (two-pass YIN — a decimated cumulative-mean-
normalized difference picks the period, then the full-rate difference function refines it
to a fraction of a frame), estimates the source's fundamental period once at load;
`pitch_shift`'s splice jump becomes the multiple of that period nearest the fixed window
that still fits the ring's jump bound, so an aligned landing point sits at the centre of
the existing correlation search instead of possibly not existing inside it at all.
Detection runs off the audio thread by link graph — `sampler_core` does not link
`period_detect`, so no translation unit on the render path can name `detectPeriod` — and an
unknown period (noise, polyphony, percussion, a drifting source) restores the fixed-window
geometry byte for byte. A period is derived from the audio at load, so it is cache rather
than state: no `ComponentState` field, no payload rung. Merged as `7a162a5`.
**Status, corrected against what `docs/PLAN.md` currently states — the closure is now
complete, not partial.** The **geometry** failure mode (no phase-aligned landing existing
inside the search window at all, for low material such as a 30 Hz tone) was closed and
asserted at the original merge and stands unchanged. The **cadence** failure mode —
splices recurring faster than the output period at rate/shift combinations where
`window/|rate shift|` is short — was left "not closed, re-characterized rather than
fixed, no-regression asserted rather than improvement claimed" at that point, pending
remediation. Three remediation commits have since landed, including `f66b9bd` (correcting
the agreement denominator to count only probes that carried signal) and `83e7cca` — the
commit that closes the track, current tip of this merge — and a re-review confirmed the
earlier findings closed. The re-review found the original cadence analysis itself stale:
PSOLA made the splice interval follow `spliceJump()` rather than the fixed `window` the
module header still described, and the closeout supplied the missing measurement in the
collapse band. **Measured at P = 1470 frames (30 Hz at 44.1 kHz), rate 2.0 / 24 st,
against a known-answer metric floor of 55.86 % and a period-off control arm: fixed-window
excess 18.52 %, pitch-synchronous excess 0.00 %** — PSOLA eliminates that corner rather
than regressing it, with every splice at n = 1 landing exactly one source period away.
**These figures are a one-machine, Debug-build measurement against the named control arm,
not a general performance claim.**