docs(product): spec S18 — VST3 channel isolation (beta ReaSampler 9000 pairs with beta extension only)
This commit is contained in:
+57
@@ -2001,6 +2001,63 @@ a multi-capture drag over the FX button is either rejected or loads the first).
|
||||
must construct exactly ReaSampler 9000's state bytes — the cross-artifact contract still
|
||||
being defined in Phase S. Blocks the drop half until the blob format is agreed.
|
||||
|
||||
## VST3 channel identity — the UID pair + the pairing surface (S18; extends Phase V V4)
|
||||
|
||||
**Decided (Daniel, 2026-07-26):** the beta/stable channel split Phase V V4 gave the
|
||||
*extension* extends to the **ReaSampler 9000 VST3 instrument** — a beta-built VST pairs with
|
||||
the beta extension only, a stable VST with stable only, both installable side-by-side in one
|
||||
REAPER. This is the instrument-side companion to V4 and mirrors its philosophy exactly:
|
||||
**one channel per binary; all channel identity derives from the ONE
|
||||
`REASAMPLER_CHANNEL_IS_BETA` bit via the pure `app_version` module — no scattered `#ifdef`s
|
||||
in the VST shell.**
|
||||
|
||||
**What is already isolated (structural, not added by S18).** The wire/data pairing is
|
||||
already done and needs no per-key work: `ext_keys.h`'s `kProjExtNamespace()` delegates to
|
||||
`app_version::extStateNamespace()`, so a beta-compiled VST's bridge reads `"reasampler_beta"`.
|
||||
Every wire key — `banks`, `assign_request`, S9's bank-generation key (in-flight), S17's
|
||||
component-state contract, and **any future key** — is a plain constant *under* that
|
||||
namespace, so channel data-isolation is **structural: no per-key opt-in, and a future key
|
||||
that forgets to isolate is impossible by construction** (it keys off the namespace accessor,
|
||||
not a raw literal). What S18 adds is only the missing *plugin identity* layer.
|
||||
|
||||
- **The UID-pair invariant (the permanent commitment).** The VST3 class UID is the plugin's
|
||||
identity — a saved REAPER project records it and rebinds a saved instance by it. Today
|
||||
`reasampler_vst.h` holds **one** forever-locked UID (`kReaSamplerProcessorUID`,
|
||||
`REASAMPLER_PROC_UID_1..4`, S-NAME-1). A beta VST with the **same** UID cannot coexist with
|
||||
stable in one install (same UID = identity collision / arbitrary rebind). So beta needs its
|
||||
**own** forever-stable UID: a second constant, minted once, locked exactly as the first.
|
||||
**Invariant: BOTH UIDs are frozen forever once shipped; the channel bit selects which is
|
||||
compiled into this binary** (one `DEF_CLASS2`, one class per binary — not both classes in
|
||||
one binary; that mirrors V4's fully-isolated-binary philosophy and keeps a beta build from
|
||||
ever presenting the stable identity). Saved-project isolation follows directly: a project
|
||||
saved with beta instances rebinds only to the beta VST; a stable-saved instance opened
|
||||
where only the beta extension has banks resolves the stable UID and shows a clean empty
|
||||
"pick a capture" state (S10 policy), not an error.
|
||||
- **Binary + display identity, channel-derived.** Mirror the extension's `OUTPUT_NAME` fork
|
||||
(`reaper_reasampler` / `reaper_reasampler_beta`): the VST3 module's on-disk name forks
|
||||
`reasampler_9000` / `reasampler_9000_beta`, its factory display name "ReaSampler 9000" /
|
||||
"ReaSampler 9000 beta", and its editor title band + S6 embed-strip label are channel-aware
|
||||
— **all sourced from `app_version` channel accessors (a VST-name accessor beside
|
||||
`binaryName()`/`dockTitle()`), never a literal in `reasampler_vst.h`/`vst_entry.cpp`.** The
|
||||
factory version string carries the `-beta` render where V4's `appVersion()` already does;
|
||||
vendor/url/email stay shared unless V4 qualified the equivalent (V4 kept the lane-name
|
||||
prefix shared — shared-where-V4-shares is the default).
|
||||
- **The complete pairing surface (the guarantee to state, not new code).** A channel's VST
|
||||
talks to that channel's extension **only**, because (1) plugin identity — UID + filename +
|
||||
display — is channel-forked (above), and (2) **all** wire keys live under the
|
||||
channel-derived `kProjExtNamespace()`. The two together make pairing complete and
|
||||
structural: identity keeps the *plugins* distinct; the namespace keeps the *data* distinct.
|
||||
No per-key or per-seam isolation work is ever needed — S8's assignment key, S9's generation
|
||||
key, and S17's blob-injection key all inherit it. **Verify all identity/factory wiring
|
||||
against the vendored Steinberg SDK** (`DEF_CLASS2` / `INLINE_UID` / `FUID` from
|
||||
`pluginfactory.h` + `funknown.h`); the pure `app_version` name accessors are CTest-tested.
|
||||
- **Fork S18-F1 (flagged — Daniel's call): mint the beta UID now vs. at first beta release.**
|
||||
Lean **mint now** — mirrors the stable UID (minted at the S1 spike, locked long before
|
||||
ship), removes a "remember to mint before shipping beta" landmine, zero cost for an
|
||||
unused-until-beta constant. The alternative (a locked-once placeholder replaced before the
|
||||
first beta VST ships) defers the commitment but adds a release-gate step. Flagged only
|
||||
because the UID is a forever commitment.
|
||||
|
||||
## Non-goals / guardrails
|
||||
|
||||
- **The instrument never captures and never inserts into the arrange.** Playback is a
|
||||
|
||||
Reference in New Issue
Block a user