docs(product): spec S7 drop-and-load — drag capture onto TCP FX button to instantiate ReaSampler 9000 preloaded
This commit is contained in:
+133
@@ -1311,6 +1311,134 @@ 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
|
||||
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
|
||||
|
||||
- **The instrument never captures and never inserts into the arrange.** Playback is a
|
||||
@@ -1330,6 +1458,11 @@ and embed message/lifecycle against `vendor/reaper-sdk/sdk/reaper_plugin_fx_embe
|
||||
M/D/B/R/V pillars.
|
||||
- **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 0–1'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
|
||||
headers before use — several §1a claims are experienced estimates until the spike
|
||||
confirms them.
|
||||
|
||||
Reference in New Issue
Block a user