docs(phase-s): settle S-VIEW velocity persistence + draggable nodes, single-pass on L3
F1 -> preview velocity persists via VST3 component state (v5->v6). F2 -> envelope nodes draggable via new pure envelope_edit module. Drop the two-pass L3 restyle framing; build on the merged L3 LnF. F3 remains open.
This commit is contained in:
+103
-24
@@ -2277,11 +2277,32 @@ view wholesale, with two changes:
|
||||
A **performance choice**, never written to `Sample` or the bank. Back-compat: an absent field on
|
||||
an older blob → 100% (bit-identical playback).
|
||||
- **Preview velocity** — a **utility** setting for the Sample view's preview-trigger button, not a
|
||||
musical parameter of the capture. **Lean: transient editor state** (like a metronome level) —
|
||||
it does not need to persist in component state, so it adds no version bump and no bank-fact risk.
|
||||
*(Fork S-VIEW-F1, Daniel's call: transient vs. a small persisted editor convenience. Lean
|
||||
transient — simplest, no compat surface, and a preview level is not something users expect to
|
||||
survive a reload.)*
|
||||
musical parameter of the capture. **SETTLED (S-VIEW-F1, Daniel 2026-07-27): it PERSISTS across
|
||||
reloads.** The seam is **VST3 component state**, not the extension's `persist` ext-state module.
|
||||
This distinction is load-bearing and was verified against the existing VST source, not recalled:
|
||||
- **Wrong seam — the extension's `persist` module.** `persist` writes REAPER *project* ext-state
|
||||
(`SetProjExtState`, namespace `"reasampler"`) and is owned by the **extension**, not the
|
||||
instrument. Preview velocity is a **per-instance** instrument-editor setting; putting it in
|
||||
project ext-state would (a) make it project-global rather than per-instance (two ReaSampler 9000
|
||||
instances would share one preview level), (b) route an instrument-owned setting through a seam
|
||||
the instrument only *reads* from (bank state), violating the read-only-over-bank contract, and
|
||||
(c) leak an instrument concern into the extension's key space. Rejected.
|
||||
- **Right seam — the instrument's own VST3 component state**, the same blob the processor already
|
||||
round-trips via `ReaSamplerProcessor::getState`/`setState` over `IBStream`, whose format is the
|
||||
**envelope-versioned `ComponentState`** in `src/vst/sample_map.h` (currently **v5**: version tag
|
||||
+ channel-mode byte + last-consumed-assignment marker + selection-id + zones payload). Preview
|
||||
velocity is a **top-level instance concern** — a sibling of `channelMode` and
|
||||
`lastConsumedAssignGeneration`, **not** a per-`PerformanceZone` field (it is one setting per
|
||||
instance, not per zone). It therefore lands as a **new field on `ComponentState`** added by
|
||||
bumping the **envelope version to v6** (a new `float previewVelocity` after the assignment
|
||||
marker, before the selection-id), leaving the **zones payload untouched** — exactly the
|
||||
independent-version-axes composition the header already documents (envelope grows a field; the
|
||||
zone-record payload version does not move). Round-trip: `serializeComponentState` appends it,
|
||||
`deserializeComponentState` reads it; a v6 blob restores it directly, and **every older blob
|
||||
(v5 and down) lifts to a sensible default** (e.g. 100/127 ≈ 0.79 or a chosen mid level), so
|
||||
already-saved instances are unchanged and no compat surface is broken. This is the same additive
|
||||
envelope-bump discipline S7 (v4→v5) already used — a well-trodden move in this codebase, not new
|
||||
machinery.
|
||||
|
||||
### The envelope overlay — visual component (new)
|
||||
|
||||
@@ -2293,9 +2314,51 @@ drawn shape lines up with the waveform under it. **Pure geometry:** an `envelope
|
||||
(mirror of `waveform_view` / `param_slider`) maps the AHDSR/Trigger params + the sample's
|
||||
frame-length to a polyline in the waveform rect (`param↔pixel` at the shared time base),
|
||||
unit-tested against known param sets; the shell traces it via kit line draws in an accent hue.
|
||||
Decorative + informative — it never captures input (the sliders remain the edit surface); dragging
|
||||
the envelope *nodes* directly is a deferred stretch (fork S-VIEW-F2, lean defer — sliders first,
|
||||
node-drag is polish once the overlay reads).
|
||||
|
||||
**The overlay is directly editable — DRAGGABLE NODES (SETTLED, S-VIEW-F2, Daniel 2026-07-27).**
|
||||
The envelope is not a read-only informative curve — its breakpoints are **draggable handles** that
|
||||
set the envelope parameters directly. Dragging a node and the existing sliders are **two surfaces
|
||||
onto one model**: the sliders stay as the precise numeric-entry surface, node-drag is the direct-
|
||||
manipulation surface, and **both read and write the same `PerformanceZone` envelope fields** — a
|
||||
drag updates the params, the sliders reflect them live, and a slider edit re-lays the nodes. There
|
||||
is exactly one source of truth (the zone's envelope params); the two surfaces never diverge. This
|
||||
is the same one-source-multiple-views instinct the whole system runs on.
|
||||
|
||||
- **Which nodes, and what each axis means.** The AHDSR shape (Gate) exposes handles at the segment
|
||||
breakpoints — **attack-end** (top of the attack ramp), **hold-end**, **decay-end / sustain-level**
|
||||
(the corner where decay settles to the sustain plateau), and **release-end**. The Trigger shape
|
||||
exposes **fade-in-end**, **%-length** (the `playEnd` anchor), and **fade-out-end**. For each node,
|
||||
**the horizontal axis maps to time** (the segment duration — attack/hold/decay/release seconds, or
|
||||
fade/length fractions) and **the vertical axis maps to level** where the node is a level breakpoint
|
||||
(the sustain node's vertical drag sets sustain 0..1; the peak nodes sit at unity). Time-only nodes
|
||||
(attack-end, hold-end, release-end) drag horizontally; the sustain node drags on **both** axes
|
||||
(its X sets decay time, its Y sets sustain level) — the standard ADSR-editor grammar (Ableton
|
||||
Simpler, Phase Plant, Serum all use exactly this).
|
||||
- **Constraints.** Nodes are **monotonic in time** — a node cannot be dragged left of its
|
||||
predecessor or right of its successor (attack-end can't pass hold-end, etc.); each segment stays
|
||||
≥ 0. Level drags clamp to **0..1** (sustain) / unity (peaks). Times clamp to the same per-param
|
||||
min/max the sliders already enforce (so node-drag can never produce a param the slider couldn't).
|
||||
The `playEnd`/%-length node additionally clamps to the sample's frame-length. **No snapping** by
|
||||
default (continuous drag, matching the sliders' resolution); a fine-drag modifier (drag with a
|
||||
held modifier for reduced sensitivity) is an optional polish, not required.
|
||||
- **Pure module owns the geometry/hit-test math (house pattern).** A pure REAPER/LICE-free module —
|
||||
name it **`envelope_edit`** (sibling to `envelope_overlay`; mirror of `card_drag` / `mode_switch`
|
||||
/ `param_slider`) — owns **node hit-testing** (point → which node, with a pick radius) and the
|
||||
**drag→param mapping** (a pixel delta on a given node → the resulting clamped envelope param set,
|
||||
respecting the monotonic + range constraints above). It is **unit-tested**: a known node + a known
|
||||
pixel delta asserts the expected param delta and the clamp/monotonic behavior at the boundaries.
|
||||
`envelope_overlay` keeps the params→polyline forward map (draw); `envelope_edit` owns the
|
||||
pixel→params inverse map (edit) + hit-test. The shell (`reasampler_editor.cpp`) does the LICE
|
||||
handle draw (small node markers at each breakpoint, hover/drag-lit via kit states) and routes
|
||||
mouse events through `envelope_edit`, then commits the resulting params to the zone through the
|
||||
same off-audio-thread path the sliders use (no new RT surface — a node-drag is a param edit, same
|
||||
as a slider drag). Zones and the single-capture Sample face share this one edit surface (one
|
||||
storage site, S15-F2).
|
||||
- **Sync with sliders (load-bearing).** Because both surfaces write the same `PerformanceZone`
|
||||
envelope fields, keeping them in sync is **structural, not a listener chain**: the editor re-reads
|
||||
the zone's params every paint, so a slider edit re-lays the nodes and a node-drag re-positions the
|
||||
sliders with no explicit cross-wiring. The single source of truth makes divergence impossible by
|
||||
construction.
|
||||
|
||||
### The real piano-key pattern — visual component (new)
|
||||
|
||||
@@ -2357,23 +2420,33 @@ face without scrolling, and cannot be resized below the constraint floor.
|
||||
### Module architecture (preserve the pure/shell split)
|
||||
|
||||
- **Pure (new/extended):** `envelope_overlay` (AHDSR/Trigger params + frame-length → polyline in a
|
||||
rect; unit-tested); `keyboard_strip` extended with the natural/accidental predicate; the sampler
|
||||
core extended with the **key-track scalar** in the repitch math (unit-tested against known
|
||||
rect; unit-tested); `envelope_edit` (NEW — node hit-test + pixel-delta→clamped-param inverse map
|
||||
for the draggable envelope handles; mirror of `card_drag`; unit-tested at the monotonic/clamp
|
||||
boundaries); `keyboard_strip` extended with the natural/accidental predicate; the sampler core
|
||||
extended with the **key-track scalar** in the repitch math (unit-tested against known
|
||||
note/root/keyTrack → ratio); `sample_map` (`PerformanceZone`) extended with the additive
|
||||
`keyTrack` field + component-state version bump + back-compat default.
|
||||
`keyTrack` field, plus the **envelope-v6 `previewVelocity` field on `ComponentState`** (S-VIEW-F1)
|
||||
— both additive, component-state version bumped, back-compat defaults on read.
|
||||
- **Shell (`reasampler_editor.cpp`):** re-partition the paint/hit-test into the three views
|
||||
(Sample face, Browse modal overlay, Zone surface) replacing the two-view toggle; add the
|
||||
preview-trigger button + velocity knob wired to an off-audio-thread preview note through the
|
||||
voice engine; draw the envelope overlay + piano-key overlay via the kit; set the larger default
|
||||
`ViewRect` + `checkSizeConstraint`. All layout/hit-test math stays in the pure geometry modules.
|
||||
voice engine (the velocity **persists** via the envelope-v6 `ComponentState` field, S-VIEW-F1);
|
||||
draw the envelope overlay + **its draggable node handles** (routing mouse events through the pure
|
||||
`envelope_edit` module and committing params via the same off-thread path the sliders use,
|
||||
S-VIEW-F2) + piano-key overlay via the kit; set the larger default `ViewRect` +
|
||||
`checkSizeConstraint`. All layout/hit-test math stays in the pure geometry modules.
|
||||
|
||||
### Precision / invariant implications
|
||||
|
||||
- **Read-only bank consumer (unchanged).** Key-tracking, preview velocity, and every marker/mode
|
||||
control are the instrument's **performance map** (D-B) — never written to `Sample` or the bank.
|
||||
- **Additive, back-compat component state.** `keyTrack` is a new optional field defaulting to 100%;
|
||||
an older blob (absent field) restores to 100% → **playback bit-identical** under the same engine.
|
||||
No existing field changes.
|
||||
- **Read-only bank consumer (unchanged).** Key-tracking, preview velocity, envelope-node edits, and
|
||||
every marker/mode control are the instrument's **performance map / editor state** (D-B) — held in
|
||||
the instrument's own VST3 component state, **never written to `Sample` or the bank.**
|
||||
- **Additive, back-compat component state.** Two additive fields land: `keyTrack` (per-
|
||||
`PerformanceZone`, default 100%) inside the zones payload, and `previewVelocity` (per-instance)
|
||||
as a new top-level `ComponentState` field via an **envelope bump to v6** — the zones-payload
|
||||
version axis is untouched (independent version axes). Every older blob lifts on read (absent
|
||||
`keyTrack` → 100%, absent `previewVelocity` → the chosen mid default), so already-saved instances
|
||||
restore **playback bit-identical** under the same engine. No existing field changes.
|
||||
- **RT discipline (unchanged).** The preview-trigger fires a note through the existing voice engine
|
||||
via the off-audio-thread commit path (`commitMapAndReload` idiom); no new `process`-thread work,
|
||||
no allocation on the audio thread.
|
||||
@@ -2384,14 +2457,13 @@ face without scrolling, and cannot be resized below the constraint floor.
|
||||
|
||||
### Open questions / forks (Daniel / Phase S team)
|
||||
|
||||
- **S-VIEW-F1 — preview velocity persistence.** Transient editor state (lean) vs. a small persisted
|
||||
editor convenience. Lean transient — no compat surface, no version bump.
|
||||
- **S-VIEW-F2 — envelope-overlay interactivity.** Read-only informative overlay + sliders as the
|
||||
edit surface (lean, ship first) vs. draggable envelope nodes on the overlay (deferred stretch —
|
||||
polish once the overlay reads).
|
||||
*(S-VIEW-F1 and S-VIEW-F2 are SETTLED — Daniel 2026-07-27 — and folded into the spec above:
|
||||
preview velocity **persists** via envelope-v6 `ComponentState`; the envelope overlay's nodes are
|
||||
**draggable** via the pure `envelope_edit` module. They are no longer open questions.)*
|
||||
|
||||
- **S-VIEW-F3 — Browse modal presentation.** Full-window overlay (lean — the modal-picker feel) vs.
|
||||
a large centered sheet with a dimmed Sample behind. Presentation detail; either satisfies "easy
|
||||
to summon and dismiss."
|
||||
to summon and dismiss." **Still Daniel's call** — the only S-VIEW fork left open.
|
||||
|
||||
### Must-verify before build (S-VIEW)
|
||||
|
||||
@@ -2404,6 +2476,13 @@ face without scrolling, and cannot be resized below the constraint floor.
|
||||
- **Preview note through the voice engine off-thread** — confirm the existing `commitMapAndReload`
|
||||
/ off-thread reload idiom is the right seam to fire a one-shot preview note without touching
|
||||
`process` on the UI thread; no torn state on the atomic voice-engine pointer.
|
||||
- **Preview-velocity persistence seam (S-VIEW-F1)** — the envelope-v6 `ComponentState` field is the
|
||||
prescribed seam (verified against `src/vst/sample_map.h` + `reasampler_processor.cpp`
|
||||
getState/setState this pass); confirm at build that appending a `float previewVelocity` after the
|
||||
assignment marker keeps the v5→v6 lift clean and the zones-payload byte offsets unchanged.
|
||||
- **Envelope node-edit inverse map (S-VIEW-F2)** — confirm the pure `envelope_edit` pixel→param
|
||||
inverse map produces params the sliders' own min/max already permit (so the two surfaces can
|
||||
never diverge), and that the monotonic time-node constraint holds at the segment boundaries.
|
||||
- **Envelope-overlay time base** — confirm the seconds→frames resolution the overlay draws against
|
||||
matches the voice engine's live-rate resolution so the drawn shape lines up with the waveform.
|
||||
|
||||
|
||||
@@ -190,10 +190,14 @@ is **ReaSampler 9000**. Framing + propagation surfaces:
|
||||
> §Addendum r9. When a point lands, doc-keeper moves it to `COMPLETED.md`.
|
||||
>
|
||||
> **Depends on Phase S being on dev** (this redesigns Phase S editor shells —
|
||||
> `reasampler_editor.cpp` + the pure `src/vst/` geometry modules). The Phase L L3 restyle and
|
||||
> this S-VIEW redesign both touch the editor; sequence so they do not collide (S-VIEW is a
|
||||
> layout/param change, L3 is a palette/kit change — best landed as one editor pass or S-VIEW then
|
||||
> L3-restyle-the-new-layout).
|
||||
> `reasampler_editor.cpp` + the pure `src/vst/` geometry modules). **Phase L L3 is FINISHED and
|
||||
> merged** (2026-07-27, commit `c53683e` — `reasampler_editor.cpp` + `reasampler_embed.cpp`
|
||||
> restyled through the L1 kit). S-VIEW builds the new three-view layout **directly on the current
|
||||
> L3 look-and-feel as its baseline — one implementation pass, styled correctly from the start.**
|
||||
> There is **no two-pass "land S-VIEW then restyle through L3"** — L3 already happened; every new
|
||||
> S-VIEW surface (Sample face, Browse modal, Zone surface, envelope overlay + node handles,
|
||||
> piano-key strip, preview cluster) is drawn through the L1 kit at build time, inheriting the L3
|
||||
> palette and component-draw grammar. Restyle-after is not a concern.
|
||||
|
||||
**Goal:** A three-view editor that makes "pick a capture, tune it, play it" fast, easy, and fun —
|
||||
Sample is home, Browse is an easy-to-summon modal picker, Zone is the deliberate keymap surface.
|
||||
@@ -212,15 +216,26 @@ piano-key pattern work; drop-a-capture-onto-FX instantiates a playing instance.
|
||||
Pitch engine / AHDSR|Trigger / AD pitch env — moved from Zone's param panel, single-capture
|
||||
one-zone storage per S15-F2). Reference grammar: Simpler / Phase Plant (labelled value-strip
|
||||
under a hero waveform).
|
||||
- [ ] **S-VIEW-3 — envelope overlay on the hero waveform (new visual).** Draw the amp envelope
|
||||
(AHDSR for Gate, fade/%-length for Trigger) as a curve over the Sample waveform at accurate
|
||||
wall-clock time. New pure `envelope_overlay` module (params + frame-length → polyline; unit-
|
||||
tested); shell traces it in an accent hue. Informative + decorative — sliders stay the edit
|
||||
surface (node-drag deferred, fork S-VIEW-F2).
|
||||
- [ ] **S-VIEW-3 — envelope overlay + DRAGGABLE NODES on the hero waveform (new visual + edit
|
||||
surface).** Draw the amp envelope (AHDSR for Gate, fade/%-length for Trigger) as a curve over the
|
||||
Sample waveform at accurate wall-clock time. New pure `envelope_overlay` module (params + frame-
|
||||
length → polyline; unit-tested); shell traces it in an accent hue. **The breakpoints are draggable
|
||||
handles** (S-VIEW-F2 SETTLED — in scope, not deferred): X → segment time, Y → level on level-
|
||||
breakpoint nodes (sustain drags both axes), monotonic-in-time + range-clamped so a drag can never
|
||||
produce a param the slider couldn't. New pure `envelope_edit` module (node hit-test + pixel-delta→
|
||||
clamped-param inverse map; mirror of `card_drag`; unit-tested at the clamp/monotonic boundaries).
|
||||
Sliders stay the precise numeric surface; **both surfaces read/write the same `PerformanceZone`
|
||||
envelope fields** — one source of truth, kept in sync by re-reading params each paint (no listener
|
||||
chain). Node-drag commits via the same off-audio-thread path a slider edit uses (no new RT
|
||||
surface).
|
||||
- [ ] **S-VIEW-4 — preview-trigger + velocity knob (new, Sample view).** A button firing the
|
||||
sampler at the loaded capture's root note through the live voice engine (off the audio-thread
|
||||
commit path — no MIDI controller needed) + an adjacent velocity knob. Preview velocity leans
|
||||
**transient editor state** (fork S-VIEW-F1). RT discipline: no `process`-thread work.
|
||||
commit path — no MIDI controller needed) + an adjacent velocity knob. **Preview velocity PERSISTS**
|
||||
(S-VIEW-F1 SETTLED): it lands as a new top-level `previewVelocity` field on `ComponentState` via an
|
||||
**envelope bump to v6** (`src/vst/sample_map.h`), round-tripped through the processor's existing
|
||||
`getState`/`setState` over `IBStream` — **not** the extension's project-scoped `persist` ext-state
|
||||
(wrong owner + wrong scope; see CONTEXT.md §S-VIEW). Zones payload untouched (independent version
|
||||
axes); older blobs lift to a mid default. RT discipline: no `process`-thread work.
|
||||
- [ ] **S-VIEW-5 — Browse reduced to *choosing* (modal over Sample).** Keep search + bank tabs +
|
||||
captures grid + scroll + selection; add **confirm/cancel** (double-click loads). **Remove** the
|
||||
large waveform preview, Mono/Stereo toggle, root keyboard-strip (all moved to Sample), and the
|
||||
@@ -262,10 +277,9 @@ piano-key pattern work; drop-a-capture-onto-FX instantiates a playing instance.
|
||||
the new default showing the full Sample face without scroll; cannot shrink below the floor.
|
||||
|
||||
### Phase S editor redesign — forks (Daniel's to call)
|
||||
- **S-VIEW-F1 — preview velocity persistence:** transient editor state (**lean**) vs. a small
|
||||
persisted convenience. Lean transient — no compat surface, no version bump.
|
||||
- **S-VIEW-F2 — envelope-overlay interactivity:** read-only overlay + sliders edit (**lean**, ship
|
||||
first) vs. draggable envelope nodes (deferred stretch).
|
||||
*(S-VIEW-F1 and S-VIEW-F2 SETTLED 2026-07-27 — folded into S-VIEW-4 / S-VIEW-3 above. F1: preview
|
||||
velocity persists via envelope-v6 `ComponentState`. F2: envelope nodes are draggable via the pure
|
||||
`envelope_edit` module. Only F3 remains open.)*
|
||||
- **S-VIEW-F3 — Browse modal presentation:** full-window overlay (**lean**) vs. a centered sheet
|
||||
over a dimmed Sample. Presentation detail.
|
||||
|
||||
|
||||
@@ -7,8 +7,15 @@ the **product framing behind a scoped phase**. Its build roadmap lives in **PLAN
|
||||
*why* (the plugin-format reasoning, the bare-VST3-vs-JUCE assessment, the settled
|
||||
decision record).
|
||||
|
||||
Status: framed by product-designer (2026-07-26), **revised 2026-07-26 (r8)**. r8 records
|
||||
Daniel's **duration-preserving correction** (2026-07-26, verbatim: *"isn't that ratio stuff
|
||||
Status: framed by product-designer (2026-07-26), **revised 2026-07-27 (r9)**. r9 records the
|
||||
**three-view editor redesign** (Sample / Browse / Zone) and its follow-up settlements: the
|
||||
S-VIEW-F1 and S-VIEW-F2 forks are now **SETTLED** (2026-07-27) — **preview velocity persists** via
|
||||
the instrument's own VST3 component state (envelope bump to v6, *not* the extension's project
|
||||
ext-state), and the **envelope overlay's nodes are draggable** via a new pure `envelope_edit`
|
||||
module — and S-VIEW builds **directly on the finished, merged Phase L L3 look-and-feel** in a
|
||||
**single implementation pass** (no two-pass restyle-after; L3 landed 2026-07-27, commit `c53683e`).
|
||||
Only S-VIEW-F3 (Browse modal presentation) remains open. Full r9 record in the Addendum below. r8
|
||||
records Daniel's **duration-preserving correction** (2026-07-26, verbatim: *"isn't that ratio stuff
|
||||
going to change the playback rate? I want duration-preserving repitching"*): the ratio path is
|
||||
**varispeed** (pitch/duration coupled), so S16 is reshaped from "pitch envelope only" into a
|
||||
**pitch-engine mode — Varispeed vs Preserve — plus the pitch envelope** riding either engine.
|
||||
@@ -1043,20 +1050,33 @@ grammar, and it is the grammar the redesign adopts:
|
||||
higher:* the useful musical range is 0–200%; past 200% it is noise, and a bounded slider reads
|
||||
cleaner than an open one.
|
||||
- **Preview velocity (Sample view).** The velocity level the preview-trigger button fires at,
|
||||
set by an adjacent knob. Not persisted as a musical parameter of the sample — it is a
|
||||
*utility* setting for the audition button (like a metronome level), so it can live as
|
||||
transient editor state or a small persisted convenience; the spec leans transient (see
|
||||
§S-VIEW). This is what makes the preview button *fun*: tap it hard or soft without reaching for
|
||||
a controller.
|
||||
set by an adjacent knob. It is a *utility* setting for the audition button (like a metronome
|
||||
level), not a musical parameter of the sample — but **it PERSISTS across reloads** (S-VIEW-F1
|
||||
SETTLED, Daniel 2026-07-27). The seam matters and was verified against the existing VST source,
|
||||
not recalled: it lives in the **instrument's own VST3 component state** (the envelope-versioned
|
||||
`ComponentState` blob the processor round-trips through `getState`/`setState`, bumped to v6 for a
|
||||
new top-level `previewVelocity` field), **not** the extension's `persist` project ext-state —
|
||||
that module is REAPER-project-scoped and extension-owned, so it would make the level
|
||||
project-global instead of per-instance and route an instrument concern through a bank-read-only
|
||||
seam. See CONTEXT.md §S-VIEW for the round-trip and back-compat lift. This is what makes the
|
||||
preview button *fun*: tap it hard or soft without reaching for a controller — and it remembers.
|
||||
|
||||
**Two visual components the redesign commits to:**
|
||||
|
||||
- **The envelope overlay** on the Sample view's hero waveform: the amp envelope (AHDSR for Gate,
|
||||
the fade/%-length shape for Trigger) drawn as a curve *at accurate wall-clock time* across the
|
||||
waveform, the way Simpler draws its amp envelope over the sample and Phase Plant traces it in
|
||||
bright blue. This is the single biggest "reads like a real sampler, not a spreadsheet" move —
|
||||
the envelope stops being four abstract sliders and becomes a *shape you see over the sound*.
|
||||
The sliders stay (precise entry), but the picture leads.
|
||||
- **The envelope overlay — with DRAGGABLE nodes** — on the Sample view's hero waveform: the amp
|
||||
envelope (AHDSR for Gate, the fade/%-length shape for Trigger) drawn as a curve *at accurate
|
||||
wall-clock time* across the waveform, the way Simpler draws its amp envelope over the sample and
|
||||
Phase Plant traces it in bright blue. This is the single biggest "reads like a real sampler, not
|
||||
a spreadsheet" move — the envelope stops being four abstract sliders and becomes a *shape you see
|
||||
over the sound*. And it is **directly editable** (S-VIEW-F2 SETTLED, Daniel 2026-07-27 — in
|
||||
scope, not deferred): the breakpoints are **draggable handles** (X → segment time, Y → level on
|
||||
the sustain node; monotonic-in-time, range-clamped), the exact ADSR-editor grammar Simpler /
|
||||
Phase Plant / Serum all use. The sliders stay as the precise numeric-entry surface, and **both
|
||||
surfaces read/write the same envelope params** — one source of truth, so a drag moves the sliders
|
||||
and a slider edit re-lays the nodes with no divergence. The geometry/hit-test math lives in a new
|
||||
pure `envelope_edit` module (mirror of `card_drag`, unit-tested at the clamp/monotonic
|
||||
boundaries); the shell only draws the handles and routes the mouse. This is the affordance that
|
||||
makes envelope-shaping *feel* like a sampler instead of a form.
|
||||
- **The real piano-keyboard pattern** on the Zone view's keyboard strip. Today's spectral strip
|
||||
is pretty but does not *read as a keyboard* — Daniel's note. The redesign keeps the pastel
|
||||
spectral hue but overlays the **actual alternating white/black key pattern** (bright/dark per
|
||||
@@ -1069,9 +1089,11 @@ consumer** — every new control (key-tracking, preview velocity, the loop/start
|
||||
inherits) is *performance map*, never written to `Sample` or the bank (D-B). LICE/SWELL drawing
|
||||
only, all layout/hit-test in pure geometry modules (D-A). RT discipline untouched — the
|
||||
preview-trigger fires a note through the same voice engine off the audio-thread commit path, no
|
||||
new audio-thread work. The VST3 class UID is unchanged — a view reorganization is not a compat
|
||||
event. And this is an *editor* redesign: the S3 voice engine, the keymap resolution, the
|
||||
component-state format (extended additively for key-tracking) are the same load-bearing core.
|
||||
new audio-thread work. The VST3 class UID is unchanged — a view reorganization plus additive
|
||||
persisted fields is not a compat event; saved instances rebind and restore. And this is an
|
||||
*editor* redesign: the S3 voice engine, the keymap resolution, and the component-state format
|
||||
(extended additively — `keyTrack` per-zone, `previewVelocity` per-instance via an envelope bump to
|
||||
v6, both with back-compat defaults on read) are the same load-bearing core.
|
||||
|
||||
**Where the spec lives:** CONTEXT.md §Phase S — editor view-model redesign (S-VIEW); PLAN.md
|
||||
§Phase S — editor view-model redesign. This Addendum is the *why*; those are the *what/how*.
|
||||
|
||||
Reference in New Issue
Block a user