docs: close out Θ-W5, and record two spline-overlay UX deferrals
Collapse Θ-W5 to wave-level in PLAN.md, append its narrative to COMPLETED.md, fix a stale resolvePlay reference in core/instrument/CLAUDE.md, and file the drag-off-margin and AttackEnd/Origin shadow warts in TODO.md.
This commit is contained in:
@@ -504,3 +504,51 @@ over the amp gain curve and persist it. Fixed on both routes (the popup close no
|
||||
cancels the drag; the mutable accessor refuses `kNone`). It has **no automated
|
||||
regression pin** — `src/shell/instrument/` has no test target, and the bug is shell
|
||||
state-machine coupling with no pure-layer equivalent.
|
||||
|
||||
### Θ-W5-T1 — spline-egs
|
||||
|
||||
Ships a free-drawn alternative to every staged envelope: the pitch, filter, and amp EGs
|
||||
can each switch Staged → Spline and have their contour drawn directly on the waveform
|
||||
overlay. The one shared monotone-spline implementation
|
||||
(`core/instrument/engine/velocity_curve`) gained **hard points** as a per-segment rule —
|
||||
a hard point does no curve smoothing on either adjacent segment, so the natural sharp
|
||||
angle stands instead of a continuous derivative — and the enhancement flows to every
|
||||
consumer, including the existing velocity→amp transfer curve, with no fork.
|
||||
|
||||
- **Dual state, save-but-inactive.** Both the Staged and Spline state persist
|
||||
simultaneously; switching modes never converts or discards the inactive one, so
|
||||
Staged↔Spline round-trips losslessly. Params payload reached **v13**; v12 projects
|
||||
still load.
|
||||
- **Gate unavailable in Spline mode.** A Spline EG's contour always covers the full
|
||||
sample length as a pure time function (the Trigger/one-shot playback model), so Gate
|
||||
is not selectable while it's active.
|
||||
- **Point-editing grammar converged**: left-click adds a point, right-click deletes it,
|
||||
control-click toggles hard/smooth — one grammar shared by both spline consumers (the
|
||||
EG overlay and the velocity-curve popup), matching the popup's already-shipped
|
||||
right-click delete.
|
||||
- **Point-count ceiling: 128 — a musical bound, not a performance one.** Segment lookup
|
||||
is an indexed binary search (≤7 steps at 128 points); the cap exists so long rhythmic
|
||||
phrases (roughly two points per articulation event) aren't limited, not because the
|
||||
evaluator is expensive.
|
||||
- **Staged controls disabled while Spline is active** — that envelope's segment knobs
|
||||
and their inner curve dials render disabled and reject edits; the dormant staged state
|
||||
is edited only by switching back to Staged.
|
||||
- The overlay's contour is normalized to the full sample length and drawn 1:1 with the
|
||||
sample's time axis; a different-length capture rescales the stored contour
|
||||
proportionally.
|
||||
|
||||
A follow-on change in the same track reworked deck cell width: `-1` in `cellIds` changed
|
||||
meaning from "a blank cell holding geometry" to **one cell's width, reserved and
|
||||
redistributed** — a Trigger face that drops Sustain and Release now gets wider cells
|
||||
instead of 144 px of dead slots. Group widths, row packing, deck height, and Gate-mode
|
||||
cell widths are unchanged.
|
||||
|
||||
**Deviations from spec / code review:**
|
||||
- A pure `resolveWaveformClaim` predicate (`core/instrument/ui/spline_edit`) now resolves
|
||||
competing waveform-band clicks — contour node, crossfade tab, marker column, staged
|
||||
envelope node — by **smallest nominal target area among candidates that actually
|
||||
contain the click**, replacing resolution by check order.
|
||||
- The Gate-unavailable-while-drawn rule was consolidated into
|
||||
`enforceGateUnavailableWhileDrawn` (`core/instrument/engine/play_params.h`), now the
|
||||
single home of that rule, called by both `resolvePlay` and the editor's
|
||||
`applyControl`.
|
||||
|
||||
Reference in New Issue
Block a user