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:
@@ -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 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.
|
||||
|
||||
### Γ-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.**
|
||||
|
||||
Reference in New Issue
Block a user