Revert "docs(product): spec S7 drop-and-load — drag capture onto TCP FX button to instantiate ReaSampler 9000 preloaded"

This reverts commit b8e8d47af5.
This commit is contained in:
2026-07-26 23:18:46 -04:00
parent 0f2bad1617
commit 52ac9b5eea
2 changed files with 0 additions and 240 deletions
-133
View File
@@ -1311,134 +1311,6 @@ and embed message/lifecycle against `vendor/reaper-sdk/sdk/reaper_plugin_fx_embe
`IPlugView`↔LICE window/bitmap bridge at the spike (window creation, sizing, event `IPlugView`↔LICE window/bitmap bridge at the spike (window creation, sizing, event
routing) — the least-trodden edge of the phase. routing) — the least-trodden edge of the phase.
## Drop-and-load — drag a capture onto a track's FX button (S7 spec)
**The gesture.** While a capture is dragged out of the `bank_panel`, a track's TCP **FX
button** becomes a drop zone. Dropping the capture there **instantiates a ReaSampler 9000
on that track with the dragged capture already loaded and selected for playback** — one
gesture from bank to playable instrument. This is the *third* integration gesture: capture
(extension), placement-into-arrange (extension), and now **placement-of-the-player**
(this wave). It is drop-and-load, not drop-to-arrange — no media item touches the timeline.
**Why it needs a new drag mode (the CF_HDROP path can't carry it).** Today's drag-out
(M11) becomes an **OS file drag** (`CF_HDROP` via `drag_out` + `drag_out_win`) the moment
the pointer leaves the panel client rect. REAPER's TCP FX button is **not** a native drop
target that instantiates a plugin-with-a-file, so this feature cannot ride the OS-drag
path: an OS drop of a WAV onto the FX area does not create "an instrument preloaded with
that WAV." It requires an **internal drag** where the extension itself tracks the pointer
over REAPER's own UI, detects the FX-button hover, and on release **drives the insert
itself**. The extension is the actor for the whole gesture.
**The two-part mechanism.**
1. **Internal-drag hover detection (extension-side, pure + shell).** The `drag_out` pure
module gains a **third `DragGesture`** beyond `Internal` (bank-to-bank) and `OsDrag`
(M11) — `InstrumentDrop`. The gesture decision is refined: leaving the panel client
rect no longer *immediately* means OS-bound. Instead:
- Pointer **inside** the panel client rect → `Internal` (unchanged bank-to-bank drag).
- Pointer **outside the panel but still over REAPER's own window/UI** →
`InstrumentDrop` (new — the shell hover-tracks the TCP FX button and highlights it).
- Pointer **left REAPER entirely** (Explorer / another app) → `OsDrag` (unchanged M11).
The pure module stays REAPER-free: it decides `InstrumentDrop` vs. `OsDrag` from
position **plus an "over-REAPER's-own-UI" predicate the shell supplies** (the shell owns
the REAPER window/hit query; the pure layer owns the set/boundary algebra). Mirror of how
M11 kept `decideGesture` pure over a rect the shell supplied. The shell then resolves the
pointer to a track + FX-button hotspot, highlights it, and on release drives the drop.
2. **FX-button drop → add-VST + load-capture (extension-side shell, then instrument
seam).** On release over an FX button the shell:
- Adds a fresh instance: `TrackFX_AddByName(track, "VST3:ReaSampler 9000", /*recFX*/
false, /*instantiate*/ <negative>)`. **Verified present** in
`reaper_plugin_functions.h`:
`int TrackFX_AddByName(MediaTrack* track, const char* fxname, bool recFX, int
instantiate)` — a **negative** `instantiate` always creates a new effect (per the
header comment); the `"VST3:"` prefix selects the format. Captures the returned FX
index (or `-1` on failure).
- **Loads the dragged capture into that instance via the load-capture seam** (below).
- Wraps the whole thing in one REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`) so the
gesture is one Ctrl-Z — the same discipline the bank verbs use.
**The ReaSampler 9000 load-capture seam (the hard coupling — MUST be added; does not yet
exist).** The Phase S spec today gives the instrument a **live-state *read* seam** (it
reads bank index + mapping from `"reasampler"` ext-state via the bridge — §The two seams)
but **no entry point for an external actor to say "this fresh instance should play *this
specific* capture."** Reading the bank is not the same as being *pointed at one sample*.
This wave is the reason to add that seam, and the seam lands **inside the instrument**
(the `phase-s` artifact), not the extension. Two candidate mechanisms — **the choice is a
Phase S open question:**
- **(A) Fresh-instance ext-state handshake.** The extension writes a small "pending load"
hint into `"reasampler"` ext-state keyed to the target track/FX (e.g. the capture id +
a target GUID); a freshly-instantiated ReaSampler 9000 reads it on init via the same
bridge it already uses, claims + clears the hint, and self-selects that capture. **Pro:**
reuses the existing bridge; no new VST3 surface. **Con:** a cross-process handshake with
a claim/clear race to get right; "which instance claims which hint" needs a stable key.
- **(B) VST3 `setState` / preset injection.** The extension builds the instance's
component state (the same blob `getState`/`setState` round-trips) with the capture
pre-selected and injects it right after `TrackFX_AddByName`. **Pro:** deterministic, no
shared-state race, uses the instrument's own persistence format. **Con:** the extension
must know and construct the instrument's state blob format — a tighter cross-artifact
coupling to a format that is itself still being built in Phase S; verify whether the
extension can set an added FX's state through the REAPER API (candidate:
`TrackFX_SetNamedConfigParm` / a state-set path — **not yet confirmed against
`reaper_plugin_functions.h`**).
Lean: **(A)** keeps the artifacts loosely coupled through the one ext-state seam they
already share and avoids the extension hard-coding the instrument's state format — but
it inherits the claim/clear race. Daniel to decide (open question below).
**Coexistence with the OS drag-out (disambiguation contract).** The two OS-vs-internal
modes are disambiguated **by pointer location, not a mode toggle** — the user never picks
"OS drag" vs. "instrument drop"; the extension infers it from where the pointer is when
released. The M11 boundary (left the client rect) is *refined*, not replaced: leaving the
rect now asks "over REAPER's UI → InstrumentDrop, else → OsDrag." Both M11 OS drag-out and
the internal bank-to-bank drag must remain **byte-for-byte unchanged** in their own
regions — this wave only inserts a new middle case. Multi-capture payloads are a
disambiguation input too (see open question — instrument drop is naturally single-capture;
a multi-capture drag over the FX button is either rejected or loads the first).
**Precision / invariant implications (drop-and-load).**
- **Explicit user-driven placement — consistent with capture↔placement separation.** This
is a *deliberate placement gesture*: the user chooses to put a playing instrument on a
track, exactly as inserting an item into the arrange is a deliberate act. It does **not**
auto-capture (the file already exists in the bank) and does **not** insert a media item
into the timeline. It instantiates a *reader* of the bank on a track and points it at one
already-captured sample. Capture, placement, and playback stay three distinct acts; this
is placement-of-the-player, not a capture and not a timeline insert.
- **No private sample copy.** The instantiated instrument consumes the one authoritative
bank (it resolves the WAV via the shared M4 project-relative machinery like any
ReaSampler 9000 instance); the seam hands it a *reference* (a capture identity), never a
copied file. Any path that copies bytes into the instance is a bug.
- **The internal drag stays pure-decidable and testable.** The new `InstrumentDrop`
gesture is decided in the `drag_out` pure module (REAPER-free) over a shell-supplied
predicate; the M11 `drag_out` unit tests must not regress.
**Open questions (Daniel / Phase S team to decide).**
- **Load-capture seam mechanism (A vs. B above).** The single load-bearing Phase S design
choice this wave forces. Lean is (A) for loose coupling; needs Daniel's call.
- **Multi-capture drag over an FX button** — reject (only single-capture drags arm
`InstrumentDrop`), or load the first / a keymap of all? Tier-0 leans reject-or-first;
a multi-capture keymap load is a Tier-1 stretch.
- **FX-button hotspot vs. whole TCP.** Does the drop zone have to be the FX button
specifically, or is dropping anywhere on the target track's TCP enough (simpler hit
resolution, arguably clearer target)? Depends on what the SDK exposes (see must-verify).
- **Numbering vs. the `phase-s` worktree.** Drop-and-load is a sibling of the worktree's
ingest-relay / drop-to-load work (not on dev). Reconcile the S7 label when `phase-s`
merges (see PLAN.md §S7 reconciliation note) — possibly fold into the ingest-relay family.
**Must-verify before build (drop-and-load).**
- `TrackFX_AddByName` — **verified present** (`reaper_plugin_functions.h`): signature and
the `"VST3:"`-prefix + negative-`instantiate` semantics confirmed from the header.
- **Pointer→track / FX-button hit resolution during a drag** — **not yet confirmed.**
Candidates: `GetTrackFromPoint` / `GetThingFromPoint` (verify names + signatures against
`reaper_plugin_functions.h`); whether the FX button specifically is addressable vs. the
TCP as a whole is an open verification that also decides the "hotspot vs. whole TCP"
question.
- **Instance state injection (only if seam mechanism (B) is chosen)** — whether the
extension can set a just-added FX's state via the REAPER API
(`TrackFX_SetNamedConfigParm` or similar) — **not yet confirmed against
`reaper_plugin_functions.h`.** Moot if (A) is chosen.
## Non-goals / guardrails ## Non-goals / guardrails
- **The instrument never captures and never inserts into the arrange.** Playback is a - **The instrument never captures and never inserts into the arrange.** Playback is a
@@ -1458,11 +1330,6 @@ a multi-capture drag over the FX button is either rejected or loads the first).
M/D/B/R/V pillars. M/D/B/R/V pillars.
- **Do not spec Tier 2/3.** Tier 2 is held (noted, not specified); Tier 3 is - **Do not spec Tier 2/3.** Tier 2 is held (noted, not specified); Tier 3 is
optional-forever. Do not let their feature lists drive Tier 01's build shape. optional-forever. Do not let their feature lists drive Tier 01's build shape.
- **Drop-and-load must not regress the two existing drags.** S7 inserts a new middle case
(`InstrumentDrop`) between the M11 OS drag-out and the internal bank-to-bank drag; both
existing gestures stay byte-for-byte unchanged in their own regions. Drop-and-load never
inserts a media item into the arrange and never copies sample bytes into the instance —
it hands the new instance a *reference* to an already-captured bank sample.
- **Verify Steinberg SDK, bridge, embed, and LICE-view surfaces** against the vendored - **Verify Steinberg SDK, bridge, embed, and LICE-view surfaces** against the vendored
headers before use — several §1a claims are experienced estimates until the spike headers before use — several §1a claims are experienced estimates until the spike
confirms them. confirms them.
-107
View File
@@ -248,103 +248,6 @@ build:** the `IReaperUIEmbedInterface` contract + embed message/lifecycle agains
- [ ] Embed lifecycle (open/close/resize/hit-test inline) handled cleanly; reflects - [ ] Embed lifecycle (open/close/resize/hit-test inline) handled cleanly; reflects
the live keymap/levels. the live keymap/levels.
## S7 — 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 —
`drag_out` + `drag_out_win`, CF_HDROP). This wave adds a **second, internal drag mode**:
while a capture is dragged, a track's TCP **FX button** lights as a drop zone, and
dropping there instantiates a **ReaSampler 9000** (the Phase S VST3 sampler) on that
track with the dragged capture **already loaded and selected** for playback. The
extension drives the whole gesture itself — REAPER's FX button is not a native
plugin-with-file drop target, so this cannot ride the CF_HDROP path. CONTEXT.md §Phase S
(drop-and-load — internal-drag hover mode + the FX-button drop → add-VST + load-capture
seam). Product framing: `docs/product/midi-playback.md` (drop-and-load — the third
integration gesture).
**Consistent with the load-bearing principle (make the reasoning explicit):** this is an
**explicit, user-driven placement gesture** — the user is deliberately choosing to place
a playing instrument on a track, exactly as inserting an item into the arrange is a
deliberate placement act. It does **not** auto-capture (the file already exists in the
bank) and does **not** insert a media item into the timeline; it instantiates a *reader*
of the bank on a track and points it at one already-captured sample. Capture, placement,
and playback stay distinct acts; drop-and-load is a placement-of-the-player gesture, not
a capture and not a timeline insert.
**Two-part mechanism:**
- **(a) Internal-drag hover mode.** A drag armed with a *single* capture that stays
*inside* REAPER's own UI (does not cross to Explorer / another app) is tracked by the
extension: it detects the pointer hovering a track's TCP FX button, highlights it as a
drop target, and on release drives the insert. This is a *third* `DragGesture` beyond
the existing `Internal` (bank-to-bank) and `OsDrag` (M11) — call it `InstrumentDrop`.
- **(b) FX-button drop → add-VST + load-capture.** On drop, the extension adds a
ReaSampler 9000 instance to the target track via `TrackFX_AddByName` (verified present
in `reaper_plugin_functions.h`; signature
`int TrackFX_AddByName(MediaTrack*, const char* fxname, bool recFX, int instantiate)`
use `"VST3:ReaSampler 9000"` and a negative `instantiate` to always create a new
instance), then pushes the dragged capture's identity into that instance so it plays
that sample — via the **load-capture seam** (below).
**The ReaSampler 9000 load-capture seam (the hard Phase S coupling — MUST be added):**
The current Phase S spec gives the instrument a *live-state read* seam (it reads the bank
index + mapping from `"reasampler"` ext-state via the bridge) but **no entry point for an
external actor to say "instantiate playing *this specific* capture."** This wave is the
reason to add that seam. It is a Phase S dependency, not extension-side, and must land in
the instrument before drop-and-load's drop half can work end-to-end. See CONTEXT.md
§Phase S for the two candidate seam mechanisms (fresh-instance ext-state handshake vs.
VST3 `setState` preset injection) and the open question on which is chosen.
**Coexistence with the M11 OS drag-out (disambiguation, load-bearing):** the two drag
modes are disambiguated by **where the pointer goes**, not by a mode toggle. Inside the
panel client rect → `Internal` (unchanged). Left the panel but still over REAPER's own
window/UI → `InstrumentDrop` (new — hover-tracks the FX button). Left REAPER entirely
(Explorer / another app) → `OsDrag` (unchanged M11). The M11 `decideGesture` boundary
(pointer left the client rect) is **refined**, not replaced: leaving the client rect no
longer immediately means OS-bound; it means "resolve which of InstrumentDrop / OsDrag by
whether the pointer is over REAPER's UI." Single-capture vs. multi-capture also
disambiguates — see open questions.
**Verify (in DAW):** dragging a single capture from the dock over a track's FX button
highlights it; dropping instantiates ReaSampler 9000 on that track with the dragged
capture loaded, selected, and MIDI-playable immediately (no manual pick step); the OS
drag-out to Explorer / another DAW still works unchanged; the internal bank-to-bank drag
still works unchanged; no media item is ever inserted into the arrange; the instrument
holds no private copy (it reads the one authoritative bank).
**Depends on:** M11 (`drag_out` gesture machinery — the mode it extends); **Phase S S4**
(a loadable, playing ReaSampler 9000 instance must exist) **AND the new load-capture seam
added inside ReaSampler 9000**. Gated on **Phase S landing on dev** — the instrument and
its seam are on the `phase-s` worktree, not dev; this wave cannot be built on dev until
they merge (same gate posture as L3).
- [ ] Extend the `drag_out` pure module with the third gesture: `decideGesture` (or a
successor) returns `InstrumentDrop` when a drag armed with a single capture is over
REAPER's UI outside the panel client rect, `OsDrag` only when it has left REAPER
entirely, `Internal`/`OsDrag`/`None` otherwise unchanged. Pure over (drag state +
pointer + panel rect + an "over-own-UI" predicate the shell supplies). Unit-tested —
the existing `drag_out` invariants (M11) must not regress.
- [ ] Shell (extension): hover-track the pointer over REAPER's UI during the drag,
resolve the hovered track + its FX button (verify the TCP/FX-button hit surface against
the SDK — see must-verify), highlight it as a drop target, and on release drive the
drop. Extends the `bank_panel` drag hook alongside the M11 `drag_out_win` path.
- [ ] Shell (extension): on drop, `TrackFX_AddByName(track, "VST3:ReaSampler 9000",
false, /*instantiate*/ negative)` to always add a fresh instance; capture the returned
FX index; then invoke the load-capture seam to point the new instance at the dragged
capture. Batched into one REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`) so the
whole gesture is one Ctrl-Z (mirrors the bank-verb undo discipline).
- [ ] **ReaSampler 9000 (Phase S artifact):** add the **load-capture seam** — the entry
point that lets the just-added instance be told which capture to play (mechanism chosen
per the CONTEXT.md open question). This is the cross-artifact half; it lands in the
instrument, not the extension.
- [ ] Tests: gesture disambiguation (inside-panel / over-REAPER-UI / left-REAPER) across
single- and multi-capture payloads; FX-button hit resolution (pure geometry where it
can be factored out); M11 OS drag-out and internal bank-to-bank drag both unchanged.
> **Numbering reconciliation (load-bearing for doc-keeper).** On **dev**, Phase S runs
> S1S6; the next free label is **S7**, assigned here. **However**, the `phase-s`
> worktree carries additional, not-yet-merged Phase S points (a stereo point, an
> **ingest-relay** point, and an S10S13 UX overhaul — the "S10S13" the L3 coordination
> contract already references but which are not on dev). Drop-and-load is a **close
> sibling of the worktree's ingest-relay / drop-to-load work** (both are cross-artifact
> "get a capture into an instance" gestures). When `phase-s` merges to dev, **reconcile
> this S7 against the worktree's S7+ numbering** — likely by renumbering this wave and/or
> folding it into the ingest-relay family rather than shipping two overlapping
> drop-into-instrument specs. This label is provisional-on-dev, not a claim that the
> worktree's S7+ do not exist.
## Phase S — held and optional-forever (noted, not specified) ## Phase S — held and optional-forever (noted, not specified)
- **Tier 2 — "expressive" (HELD).** Velocity layers, round-robin (anti-machine-gun), - **Tier 2 — "expressive" (HELD).** Velocity layers, round-robin (anti-machine-gun),
full ADSR, per-sample tuning/gain trim, sustain loops. The next depth increment once full ADSR, per-sample tuning/gain trim, sustain loops. The next depth increment once
@@ -363,16 +266,6 @@ they merge (same gate posture as L3).
`video_processor.h` / `reaper_plugin_functions.h`. `video_processor.h` / `reaper_plugin_functions.h`.
- **`IReaperUIEmbedInterface`** — embed contract + message/lifecycle, against - **`IReaperUIEmbedInterface`** — embed contract + message/lifecycle, against
`reaper_plugin_fx_embed.h` (needed only at S6). `reaper_plugin_fx_embed.h` (needed only at S6).
- **Drop-and-load (S7) — three surfaces.** (1) `TrackFX_AddByName` — **verified present**
in `reaper_plugin_functions.h` (signature confirmed; the `"VST3:"` name prefix and the
negative-`instantiate`-always-adds semantics are documented in the header comment). (2)
**TCP / FX-button hit resolution** — how the extension resolves the pointer-under-cursor
to a track and its FX-button hotspot during a drag: **not yet confirmed against the
SDK/SWELL** — `GetTrackFromPoint` / `GetThingFromPoint` are candidates to verify against
`reaper_plugin_functions.h`; whether the FX button specifically is addressable (vs. the
TCP as a whole) is an open verification. (3) The **ReaSampler 9000 load-capture seam**
a *new* interface added inside the Phase S instrument; its mechanism is a Phase S design
choice (see CONTEXT.md §Phase S open question), not a pre-existing SDK surface.
--- ---