docs: close out Θ-W3-T2 into COMPLETED, collapse the W3 wave, and record the overlay contrast wart as open
This commit is contained in:
@@ -261,6 +261,74 @@ single derivation from the value type; `PlayParams` stays a plain copyable value
|
|||||||
- A filter envelope only advances while its depth is non-zero (the exact-skip at
|
- A filter envelope only advances while its depth is non-zero (the exact-skip at
|
||||||
`modAmount == 0`), which is what keeps the at-rest path byte-identical.
|
`modAmount == 0`), which is what keeps the at-rest path byte-identical.
|
||||||
|
|
||||||
|
### Θ-W3-T2 — staged-envelope-curves
|
||||||
|
|
||||||
|
Grows the envelope-overlay editor from an amp-only fixture into the shared graphical
|
||||||
|
surface for all three envelopes (amp, pitch, filter): a corner radio switch per deck
|
||||||
|
selects which envelope is overlay-active (none by default, exclusive); every sloped
|
||||||
|
stage on every envelope (Attack/Decay/Release — Hold and Sustain stay flat) gets an
|
||||||
|
editable curve exponent (0.1–10, 1.0 the linear neutral) via a paired inner knob dial
|
||||||
|
and a round mid-segment overlay knot, both resolving through the one curve law in
|
||||||
|
`src/core/util/curve_law.h`; the pitch envelope becomes AHD (Attack → Hold → Decay,
|
||||||
|
Hold a fraction of the time remaining after Attack and Decay, so A+H+D ≤ span holds by
|
||||||
|
construction, no clamp); AHDSR envelopes get a right-anchored release, dragged from
|
||||||
|
its top node with the bottom-right corner fixed; and the Trigger amp/filter
|
||||||
|
fade-in/fade-out pair is retired in favor of a Trigger AHD, consolidating what were
|
||||||
|
two staged-shape mechanisms into one — item 8's rule (pitch always AHD; amp and filter
|
||||||
|
AHDSR in Gate, AHD in Trigger) governs all three. The Trigger × Preserve end-of-sample
|
||||||
|
click is fixed at its root cause: `freezeTail()` stopping the pitch shifter's writer a
|
||||||
|
full window before the read head arrives.
|
||||||
|
|
||||||
|
**Open question resolved — per-mode stage-value state.** Gate and Trigger keep
|
||||||
|
SEPARATE stored stage values, on both the amp (`PlaySeconds::adsr` +
|
||||||
|
`PlaySeconds::trigAhd`) and the filter (`FilterSeconds::env` + `FilterSeconds::trigEnv`).
|
||||||
|
Migration forces it: an old instance carries both an AHDSR and a fade pair, and one
|
||||||
|
shared set cannot preserve both modes' prior sound. Cost: ~160 bytes of persisted
|
||||||
|
state per instance, 6 additional `DeckParam` ids.
|
||||||
|
|
||||||
|
**Deviations from spec:**
|
||||||
|
- **The migration exponent is FITTED, not neutral — Daniel's explicit ruling,
|
||||||
|
resolving a spec contradiction.** PLAN.md stated both "pre-existing instances load
|
||||||
|
at exponent 1.0" and "exponents at whatever reproduces the prior fade shape";
|
||||||
|
those conflict, and the fix resolves toward the second, since it carries the
|
||||||
|
migration guarantee. Attack lifts at **p = 0.6133**, decay at **q = 1.7437**; max
|
||||||
|
deviation from the retired equal-power (sin/cos) fade shape drops from 0.2105 to
|
||||||
|
0.0875. Every non-migrated curve still lifts to the 1.0 neutral.
|
||||||
|
- **Item 4's fix is deliberately WIDER than spec.** The spec scoped the end-of-sample
|
||||||
|
click fix to Trigger × Preserve; the landed fix is not mode-scoped, so Gate ×
|
||||||
|
Preserve × source-exhaustion also now rings out (~4 ms) where it previously
|
||||||
|
hard-cut. A held Gate note whose source runs out with no loop is cut at sustain
|
||||||
|
level, landing on the same recycled synthetic tail — scoping the fix to Trigger
|
||||||
|
alone would have knowingly left that click.
|
||||||
|
- **Migration is lossy under a sample-rate mismatch** — a documented bound, not a
|
||||||
|
bug. The retired fades were source frames; the lift divides by the project rate
|
||||||
|
while the AHD rebuilds at decode rate, so a rate mismatch shifts migrated stage
|
||||||
|
lengths by that ratio. Documented in the v10 version ladder
|
||||||
|
(`component_state_io.h`) with a test.
|
||||||
|
- **Payload version is v10.** `component_state_io.cpp` was split on the format seam
|
||||||
|
into `component_state_io.cpp` + a new `params_payload.{h,cpp}`.
|
||||||
|
- **New pure module:** `src/core/util/curve_law.h` — the one per-segment curve law
|
||||||
|
(exponent domain, normalized-position→level map, the mid-segment inverse an overlay
|
||||||
|
knot drags through, and the knob's norm↔exponent travel with an exact centre
|
||||||
|
detent). The neutral exponent is a bit-identity. Measured cost of a non-neutral
|
||||||
|
exponent: ~4.7 ns per evaluation, +224 ns/output frame worst case at 16 voices —
|
||||||
|
3.1% → 4.1% of one core at 44.1 kHz.
|
||||||
|
- **`OverlayEnv` and the overlay-selection state machine live in
|
||||||
|
`core/instrument/ui/deck_groups`**, not the shell.
|
||||||
|
- **The knot-creation gesture differs from spec.** Spec said dragging a segment
|
||||||
|
*adds* a knot; the landed behavior draws the knot unconditionally on every sloped
|
||||||
|
non-zero segment and responds to a drag within the grab radius. Daniel confirmed
|
||||||
|
this reading stands.
|
||||||
|
- **Loop markers moved from `AccentTertiary` to `AccentSecondary`** — they collided
|
||||||
|
exactly with the envelope trace (RGB delta 0) in the same overlay rect. Daniel
|
||||||
|
ruled. The palette is under active change on a separate branch, so the specific hex
|
||||||
|
values are in flux (see `docs/TODO.md`).
|
||||||
|
|
||||||
|
**Still open — not closed by this track.** The envelope overlay's contrast against
|
||||||
|
the waveform (tertiary purple, measured 1.37:1, below the 3:1 indicator floor) awaits
|
||||||
|
Daniel's eye on a build; pinned as a flagged deviation in `tests/test_theme.cpp`. See
|
||||||
|
`docs/TODO.md`.
|
||||||
|
|
||||||
### Ξ-W1-T1 — tracking-consolidation
|
### Ξ-W1-T1 — tracking-consolidation
|
||||||
|
|
||||||
Consolidates the provenance/usage territory into one system: the retired
|
Consolidates the provenance/usage territory into one system: the retired
|
||||||
|
|||||||
+15
-213
@@ -172,219 +172,21 @@ exist before it can receive a corner radio switch and inner curve dials. T1 addi
|
|||||||
depends on W2-T1 for the filter itself: the filter is the first control set where the
|
depends on W2-T1 for the filter itself: the filter is the first control set where the
|
||||||
latched-at-note-on delivery model fails audibly, and it is what made the defect visible.
|
latched-at-note-on delivery model fails audibly, and it is what made the defect visible.
|
||||||
|
|
||||||
**Two tracks, and they are SERIAL — T1 before T2. This is not a priority hint.** Θ-W4's
|
**Both tracks have landed** — Θ-W3-T1 (`live-parameter-delivery`) and Θ-W3-T2
|
||||||
two tracks are disjoint and merely ordered; these two are not. T1
|
(`staged-envelope-curves`) — see `docs/COMPLETED.md` for the full narrative of each.
|
||||||
(`live-parameter-delivery`) changes how every playback parameter *reaches* a sounding
|
Between them: every continuous playback control (filter cutoff/Q/morph/drive/mod
|
||||||
voice; T2 (`staged-envelope-curves`) changes what *shape* the three envelopes have. Both
|
amount/key-track, every stage time and level on all three envelopes) now reaches a
|
||||||
land inside `envelopes.h`'s stage math: T1 has to define what happens to a stage's
|
sounding voice live instead of latching at note-on, and the envelope-overlay editor
|
||||||
counters when its duration changes mid-flight, and T2 rewrites the same level
|
grew from an amp-only fixture into the shared graphical surface for all three
|
||||||
computation to apply a per-segment curve exponent and to grow the Trigger AHD. Two
|
envelopes — a corner radio switch per deck (none active by default), curve-shapeable
|
||||||
writers on those forty lines is a merge fight over the phase's hottest code.
|
segments on every sloped stage (0.1–10 exponent, an inner dial paired with an overlay
|
||||||
|
knot), the release-right-anchored AHDSR layout against the pitch envelope's 1:1 AHD,
|
||||||
The ordering is T1-first for one structural reason: **T2 adds parameters, T1 defines how
|
and the Trigger amp/filter fade pair folded into a Trigger AHD, consolidating what
|
||||||
parameters are delivered.** Authoring T2's curve exponents, Hold fraction, and Trigger
|
were two staged-shape mechanisms into one. The Trigger × Preserve end-of-sample click
|
||||||
AHD fields under the latching model means adding them, then re-plumbing every one of them
|
is fixed; the landed fix is wider than scoped, also ringing out Gate × Preserve ×
|
||||||
through the live path afterwards — a backfill cliff, and the exact shape of avoidable
|
source-exhaustion, previously a hard cut. The ordering was deliberately serial — T1's
|
||||||
rework. T1 first means every parameter T2 introduces is authored into the delivery
|
live-delivery mechanism landed first so T2's new curve exponents and Trigger AHD
|
||||||
mechanism from the start. The cost of the ordering is honest and worth stating: T2 is the
|
fields were authored directly into it rather than backfilled afterward.
|
||||||
wave's user-visible payload and T1 delays it by one track.
|
|
||||||
|
|
||||||
T1 has landed and specified its mid-stage rule in terms that survive T2: **hold
|
|
||||||
normalized stage position** — φ = elapsed/duration held fixed across a duration change,
|
|
||||||
then advancing at 1/newDuration (Daniel's pick among the candidates T1 laid out; see
|
|
||||||
`docs/COMPLETED.md`). It is expressed over *normalized stage position* rather than over
|
|
||||||
output level specifically so T2's exponent is a pure map of that position and composes
|
|
||||||
with the rule rather than invalidating it.
|
|
||||||
|
|
||||||
Beyond the ordering, the W3 collision is the phase's densest, which is why neither track
|
|
||||||
splits further: the envelope parameter model, the per-sample envelope evaluation, the
|
|
||||||
deck-band inner dials, and the waveform-band overlay are one system with one source of
|
|
||||||
truth (the settled S-VIEW rule: node-drag and slider-edit read/write the same model,
|
|
||||||
structurally, not via a listener chain). Splitting T2 into model and UI tracks would put
|
|
||||||
two writers on `play_params.h` + `component_state_io` and two writers on the overlay's
|
|
||||||
forward/inverse map pair. The serialization is the correct answer.
|
|
||||||
|
|
||||||
#### Θ-W3-T1 — `live-parameter-delivery`
|
|
||||||
|
|
||||||
**Θ-W3-T1 has landed** — see `docs/COMPLETED.md` for the full narrative. Continuous
|
|
||||||
playback controls (filter cutoff/Q/morph/drive/mod amount/key-track; every stage time
|
|
||||||
and level on all three envelopes) are now delivered live to sounding voices via a
|
|
||||||
seqlock-published `LiveValues` block owned at processor-instance scope, above
|
|
||||||
`LoadedInstrument`, so a drain-slot voice keeps tracking the knob (the desired
|
|
||||||
behavior). `foldLive(const PlayParams&)` is the single derivation from the value type.
|
|
||||||
Both of Daniel's ruling questions are answered and implemented: **reload tier =
|
|
||||||
Grouping B** (root note, loop span, and start frame still reload; everything else
|
|
||||||
continuous goes live), and **mid-stage rule = candidate (iv), hold normalized stage
|
|
||||||
position** (φ = elapsed/duration held fixed across a duration change, then advancing
|
|
||||||
at 1/newDuration) — expressed over normalized position specifically so T2's
|
|
||||||
per-segment curve exponent, below, composes with it. **Trigger's %-length and fades
|
|
||||||
are NOT live** — they are baked into `SampleData` at build, so a Trigger-mode instance
|
|
||||||
gets zero live amp delivery until T2 folds the fade pair into the AHD. The five
|
|
||||||
non-live exclusions (`kKeyTrack`, `kFilterVel`, `kTrigLength`, `kTrigFadeIn`,
|
|
||||||
`kTrigFadeOut`) are documented in `src/core/instrument/ui/deck_groups.h`, now their
|
|
||||||
single home.
|
|
||||||
|
|
||||||
#### Θ-W3-T2 — `staged-envelope-curves`
|
|
||||||
|
|
||||||
**Prerequisite: Θ-W3-T1 has landed** — see `docs/COMPLETED.md`. Both tracks write
|
|
||||||
`envelopes.h`'s stage math, and this track's curve exponent must be authored as a map of
|
|
||||||
the normalized stage position T1's mid-stage rule holds fixed (φ = elapsed/duration,
|
|
||||||
candidate (iv)).
|
|
||||||
|
|
||||||
**Goal.** Grow the envelope-overlay editor from an amp-only fixture into the shared
|
|
||||||
graphical surface for every envelope, give every envelope shapeable segments, fix the
|
|
||||||
overlay's layout and contrast failures, consolidate the Trigger amp fade pair into the
|
|
||||||
AHD — and, in the same region, kill the Trigger×Preserve end-of-sample click.
|
|
||||||
|
|
||||||
**Consolidates items 1, 8, 14, 4.**
|
|
||||||
|
|
||||||
**Surface boundary — owns:**
|
|
||||||
- `core/instrument/engine/` — the envelope evaluator (from W1's seam), `zone_params.h`,
|
|
||||||
`pitch_shift.{h,cpp}` (item 4's tail), the voice-render envelope and tail path.
|
|
||||||
- `core/instrument/map/` — `trigger_seam`, `component_state_io` (curve exponents, the
|
|
||||||
pitch Hold stage, the Trigger AHD, migration).
|
|
||||||
- `core/instrument/ui/` — `envelope_overlay`, `envelope_edit`, `knob_deck` (the inner
|
|
||||||
dial).
|
|
||||||
- `src/shell/instrument/` — the deck band (inner dials, radio switches) and the
|
|
||||||
waveform band's **overlay layer only** (T2's lane geometry from W2 is consumed, not
|
|
||||||
re-owned).
|
|
||||||
|
|
||||||
**Behavior — the overlay surface (item 1).**
|
|
||||||
- **Radio switch per envelope deck.** Each envelope knob-deck group (AMP ENVELOPE,
|
|
||||||
PITCH ENV, and now Filter) gains a radio switch in the corner of its deck. Selecting a
|
|
||||||
deck's radio makes *that* envelope the one displayed and editable in the graphic
|
|
||||||
waveform overlay — replacing today's always-on Amp AHDSR. The switch is exclusive:
|
|
||||||
**one overlay-active envelope at a time, or none** — no-envelope-shown is a valid
|
|
||||||
state, not an error.
|
|
||||||
- **Default overlay: none.** The editor opens with **no envelope selected**.
|
|
||||||
- **Segment curve values on all envelopes.** Amp AHDSR, Pitch AHD, and Filter AHDSR all
|
|
||||||
gain an editable curve value per *sloped* segment. The curve is exponential; the
|
|
||||||
per-segment parameter is the exponent scalar, range **0.1 to 10**. **1.0 is the linear
|
|
||||||
neutral** (y = x^1.0 is linear).
|
|
||||||
- **Which segments are sloped: every stage except Hold and Sustain.** For an AHDSR that
|
|
||||||
is Attack, Decay, Release; for the Pitch AHD, Attack and Decay. Hold is flat
|
|
||||||
everywhere and carries no curve dial.
|
|
||||||
- **Inner dial on curvable-segment knobs.** Every knob controlling a sloped, curvable
|
|
||||||
segment gains an **inner dial**: its own inner arc, its own hover accent (tertiary
|
|
||||||
purple), its own needle, its own numerical label. The inner dial controls that
|
|
||||||
segment's curve exponent; the outer knob keeps controlling the segment's time/level
|
|
||||||
value as today.
|
|
||||||
- **Curve editing in the overlay.** Dragging on a segment **adds a round mid-segment
|
|
||||||
knot** whose drag adjusts that segment's curve — the overlay is a curve-edit surface
|
|
||||||
in its own right, alongside (not instead of) the inner dial. Same one-model rule as
|
|
||||||
the existing draggable nodes: knot-drag and inner dial cannot diverge.
|
|
||||||
- **Overlay recolor.** Envelope segments change from secondary blue to **tertiary
|
|
||||||
purple** — secondary blue contrasts poorly against the primary green waveform behind
|
|
||||||
it. Re-verify the tertiary-purple-on-green pair against `theme`'s contrast-floor
|
|
||||||
assertions.
|
|
||||||
- **Pre-existing instances load at exponent 1.0** — the linear neutral — so their
|
|
||||||
audible envelope behavior is unchanged.
|
|
||||||
|
|
||||||
**Behavior — layout policy and the pitch AHD (item 8).**
|
|
||||||
- **The release segment anchors to the right edge** of the overlay. Release is dragged
|
|
||||||
from its **top node** (the node joining sustain to release) instead of the bottom
|
|
||||||
corner; the bottom corner — the envelope's end point — becomes **fixed/anchored**, not
|
|
||||||
draggable. Today's failure is that with little or no release the sustain portion
|
|
||||||
occupies only a small stretch and the whole figure reads off-center.
|
|
||||||
- **Spec change: the pitch envelope becomes AHD** — Attack → Hold → Decay. **Hold ranges
|
|
||||||
0 to 100% of the time remaining after Attack and Decay** (100% of
|
|
||||||
`sample length − (attack + decay)`). At 100% Hold fills all remaining time; at 0% it
|
|
||||||
takes none.
|
|
||||||
- **The A + H + D ≤ sample-length bound holds by construction, not by a clamp.** Hold is
|
|
||||||
expressed as a fraction of what is left, so the sum cannot overflow. Do not add a
|
|
||||||
separate clamp; none is needed.
|
|
||||||
- **Scope rule — split on the sustain stage.** The 1:1-overlay property and the
|
|
||||||
combined-time bound apply to **all envelopes that do NOT have a sustain stage**;
|
|
||||||
envelopes **with** a sustain stage get the right-anchored-release policy instead. The
|
|
||||||
two policies **coexist rather than merge**. Daniel's rationale: the 1:1 mapping only
|
|
||||||
makes sense for trigger, not gated envelopes. **The pitch envelope's D is not a
|
|
||||||
release, so it is not right-anchored.**
|
|
||||||
|
|
||||||
**Behavior — mode-driven envelope shape (item 14).**
|
|
||||||
- **Trigger mode: fades out, AHD in.** In Trigger mode the amp envelope becomes an AHD
|
|
||||||
per item 8's definition. The fade-in-length and fade-out-length controls **go away in
|
|
||||||
Trigger mode**; Attack and Decay carry those roles. Daniel's motive is stated:
|
|
||||||
reducing code for the same job — one staged-envelope design covering what is currently
|
|
||||||
two mechanisms.
|
|
||||||
- **The Trigger AHD's sloped segments get the full curve treatment** — exponent 0.1–10,
|
|
||||||
inner dials, mid-segment overlay knots, tertiary-purple rendering.
|
|
||||||
- **Gate mode unchanged** — the amp envelope stays the AHDSR with the right-anchored
|
|
||||||
release.
|
|
||||||
- **The filter envelope follows the same rule.** Stated once, governing all three:
|
|
||||||
**pitch is always AHD (1:1 overlay); amp and filter are AHDSR in Gate
|
|
||||||
(right-anchored release) and AHD in Trigger (1:1 overlay).** The rule selects by the
|
|
||||||
envelope's **current shape under the active playback mode**, not by which processor it
|
|
||||||
modulates. No new rule is needed — item 8's rule already decides every case.
|
|
||||||
- **Pre-existing instances reopen sounding identical.** A Trigger instance's prior
|
|
||||||
fade-in/fade-out contour must be reproduced by the loaded AHD. The evident mapping
|
|
||||||
(Attack ← fade-in, Decay ← fade-out, Hold ← the full remainder, exponents at whatever
|
|
||||||
reproduces the prior fade shape) is a verify-at-implementation detail. **A prior zero
|
|
||||||
fade-out is Decay = 0** — the abrupt end stays representable, so nothing the old
|
|
||||||
controls could express is lost.
|
|
||||||
|
|
||||||
**Behavior — the Trigger×Preserve click (item 4).**
|
|
||||||
- In Trigger mode — polyphonic or monophonic alike — with the pitch engine in
|
|
||||||
**Preserve mode only**, the end of the sample produces an audible click. The scoping is
|
|
||||||
the useful part of the report and is recorded as given: Trigger × Preserve ×
|
|
||||||
end-of-sample; Varispeed is not implicated.
|
|
||||||
- Expected: a Trigger one-shot in Preserve ends silently — no click or discontinuity at
|
|
||||||
the sample end, in either voice mode.
|
|
||||||
- **Why it is in this track and not earlier:** the defect sits in the region the
|
|
||||||
fade-out currently governs, and this track retires that mechanism. Fixing it in a
|
|
||||||
prior wave means fixing it, then re-verifying it under the surviving mechanism
|
|
||||||
anyway — and the only other instrument-side track before this one owns the whole
|
|
||||||
engine. The gate below is the *post-consolidation* gate.
|
|
||||||
|
|
||||||
**Acceptance criteria.**
|
|
||||||
- Each envelope deck shows a corner radio switch; activating one puts that envelope in
|
|
||||||
the overlay, editable there, and the overlay tracks the switch immediately. At most one
|
|
||||||
envelope is overlay-active; with none active the overlay draws no envelope.
|
|
||||||
- The editor opens with no envelope overlay-active.
|
|
||||||
- Every curvable-segment knob shows the inner dial (inner arc, tertiary-purple hover
|
|
||||||
accent, needle, numeric label); sweeping it 0.1 → 10 visibly reshapes the overlay
|
|
||||||
segment and **audibly** reshapes the envelope on played notes. Hold and Sustain knobs
|
|
||||||
carry no inner dial.
|
|
||||||
- Dragging on an overlay segment adds a round mid-segment knot; dragging the knot adjusts
|
|
||||||
that segment's curve, and the segment's inner dial reflects the same value.
|
|
||||||
- Overlay envelope segments render in tertiary purple and are clearly legible against the
|
|
||||||
primary green waveform.
|
|
||||||
- With release at zero or minimum, the sustain segment extends to (near) the right edge —
|
|
||||||
the overlay reads full-width, not bunched left. Dragging the sustain→release top node
|
|
||||||
adjusts release; the bottom-right corner is fixed and not draggable.
|
|
||||||
- Envelopes with a sustain stage follow that anchoring policy; the pitch envelope's Decay
|
|
||||||
is **not** right-anchored.
|
|
||||||
- The pitch envelope plays and displays three stages — Attack, Hold, Decay — with Hold
|
|
||||||
spanning 0–100% of the time remaining after Attack and Decay. **No combination of A, H,
|
|
||||||
D exceeds the sample length, and no clamp fires, because none exists.**
|
|
||||||
- The overlay of any sustain-less envelope is 1:1 with the waveform's time axis: a stage
|
|
||||||
boundary at N seconds sits over the waveform at N seconds.
|
|
||||||
- In Trigger mode the amp deck shows Attack / Hold / Decay — **no fade-in or fade-out
|
|
||||||
control anywhere in Trigger mode** — with curve inner dials on Attack and Decay and
|
|
||||||
none on Hold.
|
|
||||||
- Switching between Gate and Trigger switches the amp *and* filter envelope surfaces
|
|
||||||
(deck and overlay) between AHDSR and AHD accordingly.
|
|
||||||
- The Trigger amp AHD behaves stage-for-stage like the pitch AHD — Hold semantics, curve
|
|
||||||
treatment, and overlay mapping match. (The observable proxy for the consolidation
|
|
||||||
motive: one staged-envelope design, several consumers.)
|
|
||||||
- **Migration:** a project saved before this change reopens sounding identical — every
|
|
||||||
curve at exponent 1.0, and a Trigger instance's prior fade contour (including a zero
|
|
||||||
fade-out's abrupt end) audibly reproduced by the loaded AHD.
|
|
||||||
- **Item 4's gate, under the surviving mechanism:** a Trigger-mode note played in
|
|
||||||
Preserve, in both Poly and Mono, ends with no audible click at the sample end —
|
|
||||||
verified by ear *and* by inspecting the rendered output for a terminal discontinuity.
|
|
||||||
Varispeed playback is unchanged.
|
|
||||||
|
|
||||||
**Open questions.**
|
|
||||||
- **Stage-value state across the Gate/Trigger switch — shared or per-mode? [propose]**
|
|
||||||
The Gate AHDSR and Trigger AHD share stage names (A, H, D); whether they share *values*
|
|
||||||
(one envelope whose S and R fall away in Trigger) or keep per-mode state (item 3's
|
|
||||||
"save but inactive" dual-state precedent) is unspecified — and the question covers the
|
|
||||||
**filter envelope too**. Migration leans per-mode for the amp: an old instance carries
|
|
||||||
both its AHDSR values and its Trigger fade values, and a shared-value model cannot
|
|
||||||
preserve both modes' prior sound at once. The filter is new and carries no migration
|
|
||||||
weight either way; matching the amp's answer is the natural default. Resolve with a
|
|
||||||
proposal at review — the sound-identical gate must hold for whichever mode a saved
|
|
||||||
instance plays in.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -179,3 +179,17 @@ Forward-looking follow-ups. Deferred by decision, not oversight — each entry r
|
|||||||
**Priority / risk.** Low / deferred. Mitigated in the meantime: the console message names the stranded file's project-relative path, so the operator can recover or remove it rather than discovering it later as an unexplained orphan.
|
**Priority / risk.** Low / deferred. Mitigated in the meantime: the console message names the stranded file's project-relative path, so the operator can recover or remove it rather than discovering it later as an unexplained orphan.
|
||||||
|
|
||||||
**Done looks like.** Switching projects mid-record leaves the recorded file with a ledger record in the project it belongs to, so a later prune of that project can reclaim it normally.
|
**Done looks like.** Switching projects mid-record leaves the recorded file with a ledger record in the project it belongs to, so a later prune of that project can reclaim it normally.
|
||||||
|
|
||||||
|
## Envelope overlay's tertiary-purple trace fails the 3:1 indicator contrast floor against the waveform
|
||||||
|
|
||||||
|
**Context (what shipped — Θ-W3-T2, staged-envelope-curves).** The overlay recolor moved every envelope's segment trace to `Role::AccentTertiary` (purple), specifically because it separates from the primary-green waveform underneath it more than the previously-used secondary blue did — `tests/test_theme.cpp`'s `testOverlayAccentSeparatesFromTheWaveformAccent` pins that the tertiary pairing measures a HIGHER contrast than the retired secondary one against the same waveform color.
|
||||||
|
|
||||||
|
**The wart.** Even the improved pairing measures **~1.37:1**, well below the **3:1** floor `theme.h` states as the indicator-class contrast floor everywhere else in the kit. The test pins the measured value as a KNOWN, HELD deviation rather than asserting the floor.
|
||||||
|
|
||||||
|
**Intended fix.** Not yet specified — awaiting Daniel's eye on a running build before choosing a new tertiary hue or a different palette approach.
|
||||||
|
|
||||||
|
**The constraint the fix MUST handle.** A separate palette branch is in flight (worktree `palette-darker-teal`) and may change the tertiary/secondary hues involved; a fix landed here could collide with or be superseded by that branch. Also: whatever fix lands must not reopen the loop-marker/envelope-overlay role collision this same track resolved by moving loop markers to `AccentSecondary` — a new tertiary hue must keep that separation too.
|
||||||
|
|
||||||
|
**Priority / risk.** Not yet triaged — pinned as a flagged deviation in `tests/test_theme.cpp`, open pending a visual pass.
|
||||||
|
|
||||||
|
**Done looks like.** The envelope overlay trace clears the 3:1 indicator floor against the primary-green waveform, `tests/test_theme.cpp`'s pinned deviation is replaced with a floor assertion matching every other indicator pairing, and the fix is reconciled with whatever the in-flight palette branch lands.
|
||||||
|
|||||||
Reference in New Issue
Block a user