docs(product): scope ReaSampler 9000 UX overhaul (S10-S13) + record instrument name
Post-DAW-test: name the instrument ReaSampler 9000; spec the better-than-RS5K overhaul — S10 keyboard-strip keymap editor, S11 waveform + draggable loop points, S12 scroll/search/direct-entry/ADSR, S13 drop-to-load via the S8 relay. VST3 class UID locked unchangeable; S-NAME-1 (binary filename) is Daniel's call.
This commit is contained in:
@@ -383,6 +383,168 @@ alongside S8.
|
||||
stale value by design — it reloads on the *next* poll; confirm no torn-read hazard for the
|
||||
single integer generation key).
|
||||
|
||||
## S10 — direct-manipulation keymap editor ("ReaSampler 9000" UX overhaul, part 1)
|
||||
**Goal:** Replace the ±1 nudge-button zone editor with a **piano-keyboard-strip zone
|
||||
editor** where the keymap is edited by direct manipulation: a horizontal keyboard span
|
||||
along the top, each zone drawn as a bar over the keys it covers; **drag a zone's edge to
|
||||
change its low/high note, drag the bar's body to move the whole zone, click a key (or the
|
||||
zone's root marker) to set the root note.** No number is ever set by clicking a button 40
|
||||
times. This is the friction Daniel feels every test pass — the current row of seven
|
||||
mini-buttons per zone is the catastrophe. All layout/hit-test math is a **new pure
|
||||
geometry module** (`keyboard_strip` — mirror of `mode_switch`/`editor_geometry`); the LICE
|
||||
draw + drag-state machine is the editor shell. RT discipline untouched (edits still commit
|
||||
off-thread via `commitMapAndReload`). CONTEXT.md §Phase S (ReaSampler 9000 UX — keymap
|
||||
editor).
|
||||
**Verify (in DAW):** a zone's range is set by **dragging its edges on the keyboard strip**
|
||||
(not by nudge clicks); dragging a zone's body moves the whole range preserving its span;
|
||||
clicking a key sets/relocates the zone's root (with the repitched result audible on the
|
||||
next held note); overlapping zones render legibly and resolve first-match as before;
|
||||
zone add/select/delete still work; the pure geometry module is CTest-green (edge-grab
|
||||
regions, body-drag delta, key→note mapping, root hit-test) with no host types at its
|
||||
boundary; the ±1 nudge-button row is gone.
|
||||
**Depends on:** S5 (the `PerformanceMap`/zone model it edits), S1 (the LICE `IPlugView`
|
||||
drag/event routing — extends the click-only `wndProc` to `WM_MOUSEMOVE`/`WM_LBUTTONUP`).
|
||||
|
||||
- [ ] Pure `keyboard_strip` geometry module: map a MIDI key span across a strip width
|
||||
(128 keys → pixels, reusing the S6 `embed_strip` key-span idiom); per-zone bar rect from
|
||||
`[lowNote,highNote]`; **edge-grab hit regions** (a few px inside each end = resize handles,
|
||||
the middle = move handle); `pixel→note` and a `keyAtPoint` for click-to-set-root; a
|
||||
drag-delta resolver `(grabbedField, startNote, dxPixels) → newNote`. No VST3/REAPER/LICE
|
||||
types at the boundary; unit-tested outside the DAW (edge grabs, body-move delta, key
|
||||
mapping, clamps low≤high, boundary rounding). Mirror of `mode_switch`/`editor_geometry`.
|
||||
- [ ] Editor shell: draw the keyboard strip + zone bars in LICE (house palette), and drive
|
||||
a **drag-state machine** — `WM_LBUTTONDOWN` grabs an edge/body/key, `WM_MOUSEMOVE`
|
||||
updates the in-flight zone against the pure resolver, `WM_LBUTTONUP` commits via the
|
||||
existing `commitMapAndReload` (off-thread reload; RT path untouched). Live visual feedback
|
||||
while dragging; a single undo-coherent edit on release.
|
||||
- [ ] Click-to-set-root + zone move/resize replace the seven per-row nudge/delete
|
||||
mini-buttons; delete stays as one affordance (a small × on the zone bar or a keystroke).
|
||||
The `zoneHitTest`/±1 nudge path in `editor_geometry` is retired for the new strip (kept
|
||||
only if a numeric fallback is wanted for accessibility — build-time residual, not a fork).
|
||||
|
||||
## S11 — waveform view with draggable loop points (UX overhaul, part 2)
|
||||
**Goal:** Give each sample/zone a **waveform display** with **draggable start/end/loop
|
||||
markers** — the S2 loop-point intrinsics and the S5 performance map already carry the data;
|
||||
today there is no way to *see* a sample or *set* its loop by eye. Selecting a zone (or a
|
||||
bank sample) shows its waveform (peaks via the existing `peaks` module, fed the decoded
|
||||
PCM the shell already loads); drag the **loop-start / loop-end** markers to set the sustain
|
||||
loop, snapping to zero-crossings (the S2 spec's zero-crossing-aware requirement). Loop
|
||||
points are a **performance-map override on the zone** where set, seeded from the bank
|
||||
intrinsic (D-B split: the bank carries the file-fact default; the instrument's drag is the
|
||||
performance choice). All marker/waveform layout + hit-test is pure geometry; peaks compute
|
||||
reuses `peaks`; the draw + drag is the shell. CONTEXT.md §Phase S (ReaSampler 9000 UX —
|
||||
waveform view).
|
||||
**Verify (in DAW):** selecting a zone shows its sample's waveform; dragging the loop-start
|
||||
and loop-end markers sets the sustain loop and a held note audibly loops that region;
|
||||
markers snap to the nearest zero-crossing; a sample with no loop shows the "no loop" state
|
||||
and a held note past the end goes silent (existing core behavior); the waveform peaks match
|
||||
the audio (mirror of the `peaks` envelope assertion); the marker geometry module is
|
||||
CTest-green (px↔frame mapping, marker grab regions, clamp start≤end).
|
||||
**Depends on:** S2 (loop-point intrinsics), S3 (loop-aware sustain the markers drive), S5
|
||||
(the zone the loop attaches to), S10 (shares the editor's drag-state machine + shell). The
|
||||
zero-crossing snap is a small pure helper over the decoded PCM.
|
||||
|
||||
- [ ] Pure waveform/marker geometry: `frame↔pixel` mapping across the waveform rect, marker
|
||||
x-position from a frame index, marker grab regions (start/end/loop-start/loop-end),
|
||||
drag-delta `(grabbedMarker, dxPixels) → newFrame` with clamps (start≤end, in-bounds). A
|
||||
**zero-crossing snap** helper: nearest sign-change frame to a target (pure, over the
|
||||
decoded mono PCM). No host types; unit-tested.
|
||||
- [ ] Waveform draw: compute peaks with the existing `peaks` module from the shell's already-
|
||||
decoded PCM (no new decode path, no new WAV reader); draw the envelope in LICE in the
|
||||
house style; draw the loop markers over it. Reuses the S10 drag-state machine.
|
||||
- [ ] Loop-point edit → performance-map override: a dragged loop writes a per-zone loop
|
||||
override (seeded from the S2 bank intrinsic, D-B), committed off-thread via
|
||||
`commitMapAndReload`; the bank intrinsic is never written back (instrument is a read-only
|
||||
bank consumer). Extends `PerformanceZone` with an optional loop override (additive, same
|
||||
shape as `rootOverride`) + its component-state (de)serialize (version bump, back-compat
|
||||
with S5's v2 map blob — a truncated/older blob defaults the override absent).
|
||||
|
||||
## S12 — editor scale + ergonomics (UX overhaul, part 3; scrollable/searchable list, direct entry)
|
||||
**Goal:** Make the editor usable **at bank scale** and close the remaining RS5K-parity
|
||||
gaps: the sample list **scrolls** (today a long bank's rows run off the panel with no way
|
||||
to reach them) and has a **type-to-filter search**; add **direct numeric entry** for a
|
||||
zone's low/high/root (a click-to-type field over the strip, for precision the drag can't
|
||||
hit) and an **ADSR control** for the amp envelope (S3 already has the ADSR math; today it
|
||||
is fixed — expose attack/decay/sustain/release as draggable sliders, per-instance state).
|
||||
This is the "sensible list handling + direct manipulation of the parameters that exist"
|
||||
tier. All slider/scroll/search-box layout + hit-test is pure geometry; the shell draws +
|
||||
routes; ADSR/scroll/filter state is instrument-owned (component state / transient UI
|
||||
state). CONTEXT.md §Phase S (ReaSampler 9000 UX — scale + ergonomics).
|
||||
**Verify (in DAW):** a bank with more samples than fit **scrolls** (wheel + drag) and every
|
||||
sample is reachable; typing filters the list to matching names; a zone's low/high/root can
|
||||
be **typed** (not only dragged) via a click-to-edit field; the amp envelope's ADSR is
|
||||
**adjustable** (four draggable controls) and the change is audible + persists across project
|
||||
save/reopen (component state); the scroll/search/slider geometry is CTest-green.
|
||||
**Depends on:** S10 (the editor shell + drag-state machine), S3 (the `AdsrParams` the ADSR
|
||||
sliders drive — already wired into the voice engine; today they are fixed defaults), S5
|
||||
(the map the numeric fields edit).
|
||||
|
||||
- [ ] Scrollable, searchable sample list: a scroll offset (wheel + scrollbar drag) so long
|
||||
banks are fully reachable; a type-to-filter search that narrows the drawn rows to matching
|
||||
display names. Scroll/filter layout + hit-test is pure geometry (visible-row window,
|
||||
scrollbar thumb rect, search-box rect); filter/scroll state is transient UI state.
|
||||
- [ ] Direct numeric entry for zone low/high/root: a click-to-edit field over the strip
|
||||
(LICE text-entry idiom or a SWELL edit control on the child HWND) so a precise note can be
|
||||
typed, not only dragged. Commits via `commitMapAndReload` like every other edit.
|
||||
- [ ] ADSR editor: four draggable controls (attack/decay/sustain/release) over the S3
|
||||
`AdsrParams`; per-instance component state (additive to the map/selection blob, version-
|
||||
bumped, back-compat defaults to the current fixed envelope). Slider layout/hit-test pure;
|
||||
the audible envelope change goes through the same off-thread reload.
|
||||
|
||||
## S13 — drop-to-load: the S8 ingest story, folded into the editor UX (UX overhaul, part 4)
|
||||
**Goal:** Make "load a sample into the sampler" **one gesture from the editor**: dropping
|
||||
an OS file (or REAPER media item) **onto the editor window** ingests it into the bank and
|
||||
assigns it to this instance — the RS5K "drag a file straight onto it" affordance, which is
|
||||
the single biggest first-impression win RS5K has and we currently lack. This is the
|
||||
**editor-window end of S8's `option 1` ingest** — the instrument does **not** ingest itself
|
||||
(it stays a read-only bank consumer); the editor's drop handler **relays a bank-ingest
|
||||
request to the extension** over the agreed cross-artifact seam, and the extension performs
|
||||
the capture/import-into-bank + assign. S8 already flags drop-onto-editor as an unproven
|
||||
cross-artifact **spike** — this point is where that spike is either proven and shipped or
|
||||
falls back to the docked-panel drop path. CONTEXT.md §Phase S (ReaSampler 9000 UX — drop to
|
||||
load / S8 relay). **Cross-artifact relay is a spike, not a promise — sequence after S8.**
|
||||
**Verify (in DAW):** an OS file dropped onto the ReaSampler 9000 editor window ingests into
|
||||
the active bank and this instance plays it (via the S8 capture/import add-path + S9 refresh,
|
||||
or a direct reload if S9 absent) — **never** inserting a timeline item (capture/placement
|
||||
separation intact); the instrument itself performs no capture/import/write (the editor only
|
||||
*relays* the request to the extension); if the cross-artifact relay proves unworkable, the
|
||||
drop-onto-docked-`bank_panel` path (S8) is the shipped ingest and this degrades cleanly with
|
||||
a clear affordance pointing there.
|
||||
**Depends on:** S8 (owns the extension-side capture/import + assign, and the relay seam), S1
|
||||
(the editor HWND that accepts the drop), S9 (hands-free refresh after assign; functional
|
||||
without it via a direct reload). **Spike — do not promise the drop-onto-editor path until
|
||||
the relay is proven.**
|
||||
|
||||
- [ ] Editor-window drop target: accept `WM_DROPFILES`/`IDropTarget` on the editor child
|
||||
HWND (the same SWELL/Win32 surface `bank_panel` owns), extracting the dropped file
|
||||
path(s). Windows-only (D5). This is the *acceptance* half; the ingest is the extension's.
|
||||
- [ ] Cross-artifact ingest relay (the S8-flagged spike): the editor hands the dropped
|
||||
path + this instance's identity to the extension as a **bank-ingest request** over the
|
||||
agreed seam (the instrument never writes the bank). **Prove the relay mechanism before
|
||||
promising it**; if gnarly, fall back to the S8 docked-panel drop path and mark
|
||||
drop-onto-editor deferred.
|
||||
- [ ] UX degrade path: when the relay is unavailable/unproven, the editor shows a clear
|
||||
"drop files on the ReaSampler panel to add" affordance rather than silently swallowing the
|
||||
drop — the shipped ingest gesture stays discoverable either way.
|
||||
|
||||
## Phase S — product name (ReaSampler 9000)
|
||||
The MIDI-playback instrument's product name is **ReaSampler 9000** (Daniel, 2026-07-26,
|
||||
on DAW-testing the S1–S6 instrument). The extension remains **ReaSampler**; the instrument
|
||||
is **ReaSampler 9000**. Framing + propagation surfaces:
|
||||
`docs/product/midi-playback.md` §Product name.
|
||||
|
||||
- [ ] Propagate the display name **ReaSampler 9000** across user-visible surfaces: the VST3
|
||||
class **display name** string (in the factory registration), the `IPlugView` editor title
|
||||
band (currently "ReaSampler Instrument"), the S6 embed-strip label, and the Phase S docs.
|
||||
**Do NOT change the VST3 class UID** — instances in already-saved projects key off it; a
|
||||
UID change orphans every existing instance (fork below).
|
||||
- [ ] **Fork S-NAME-1 (Daniel's call): the binary filename.** Renaming the built VST3
|
||||
module (e.g. `reasampler_9000.vst3`) is a compat break — REAPER keys a saved project's
|
||||
plugin reference partly by filename, so a rename can require users to re-select the plugin
|
||||
on existing instances. Lean: **keep the current filename, change only display strings**
|
||||
(zero compat weight, the name shows everywhere the user reads), unless Daniel wants the
|
||||
on-disk name to match. Flag, do not decide.
|
||||
|
||||
## S17 — drop-and-load: drag a capture onto a track's FX button → instantiate ReaSampler 9000 with the capture loaded
|
||||
**Goal:** Turn a bank capture into a playable instrument in one gesture. Today a drag
|
||||
out of the `bank_panel` becomes an OS file drag once it leaves the panel (M11 —
|
||||
|
||||
Reference in New Issue
Block a user