fe55a9a96e
Fork the VST3 class UID (beta UID minted+frozen), on-disk name, and display name by the one channel bit via app_version; couple UID selection to the channel at compile time so a refactor can't split identity from data.
196 lines
12 KiB
C++
196 lines
12 KiB
C++
#pragma once
|
|
// app_version — the REAPER-free version-identity core (Phase V, V1 + V4). The single
|
|
// source of truth for the version STRING lives in CMake (a `REASAMPLER_VERSION`
|
|
// variable threaded in via configure_file -> version_generated.h); this module
|
|
// re-exports it as the canonical constant and owns every pure operation on it: the
|
|
// exact-string render, the parse/compare arithmetic a within-channel forward
|
|
// migration will lean on, and the "which version wrote this project" result that
|
|
// persist reads back from ext state (absent stamp = pre-versioning, never an error).
|
|
//
|
|
// V4 (beta-in-isolation) extends this module into the SINGLE SOURCE OF TRUTH FOR
|
|
// CHANNEL IDENTITY too. A compile-time flag (`-DREASAMPLER_CHANNEL=beta`, threaded
|
|
// through the same configure_file'd version_generated.h as REASAMPLER_CHANNEL_IS_BETA)
|
|
// selects stable (the default, absent-flag build — byte-for-byte today's identity) or
|
|
// a fully isolated beta build. Every channel-qualified identity string the shells
|
|
// register with REAPER — the display suffix, the ext-state namespace, the command-id
|
|
// prefix, the Actions-list name prefix, the binary/dock idents — is DERIVED HERE from
|
|
// the one channel bit, so no scattered #ifdef forks live across the translation units;
|
|
// the shells just consume these accessors. This keeps "what makes a beta a beta" one
|
|
// auditable definition and makes the channel-derived rendering unit-testable.
|
|
//
|
|
// PURE MODULE: NO REAPER types, NO SWELL, NO vendor/ includes. Standard library
|
|
// only. Builds and unit-tests without REAPER (mirror of bank_model / tail_control).
|
|
//
|
|
// Leading-zero fidelity (V1, Daniel-fixed): the displayed/stamped string is EXACTLY
|
|
// "0.9.01" — two-digit zero-padded patch. That exactness is why the version STRING is
|
|
// the authoritative artifact (sourced verbatim from the one CMake variable), not a
|
|
// reconstruction from numeric components — CMake's `project(VERSION)` may normalize a
|
|
// numeric patch field, so we never round-trip the string through integers to render it.
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
namespace reasampler {
|
|
|
|
// --- Channel identity (V4, beta-in-isolation) ---------------------------------------
|
|
//
|
|
// The build channel, fixed at compile time by REASAMPLER_CHANNEL_IS_BETA (0 = stable,
|
|
// the default absent-flag build; 1 = beta, from -DREASAMPLER_CHANNEL=beta). Stable is
|
|
// today's build with byte-identical identity in EVERY string below — any divergence on
|
|
// the stable channel is a defect. Beta forks every identity so a beta binary coexists
|
|
// with stable in one REAPER (both are dlopen'd at startup) without colliding on project
|
|
// ext-state, keybindings, or any REAPER-global registration.
|
|
enum class Channel { Stable, Beta };
|
|
|
|
// The channel this build was compiled for. Constant per binary.
|
|
Channel channel();
|
|
|
|
// True on the beta build only. Convenience over channel() == Channel::Beta.
|
|
bool isBeta();
|
|
|
|
// The user-visible version render. Stable: EXACTLY the CMake string ("0.9.01"). Beta:
|
|
// that string plus a plain "-beta" suffix ("0.9.01-beta") — a plain suffix, NOT a
|
|
// git-describe decoration (V2, Daniel-fixed). This is what the show-version action and
|
|
// the bank-panel readout display. It is NOT the ext-state stamp value (see stampVersion).
|
|
const std::string& appVersion();
|
|
|
|
// The ext-state STAMP value — the writing-version recorded into a saved project. This is
|
|
// the NUMERIC TRIPLE ONLY ("0.9.01") on BOTH channels: it deliberately carries NO channel
|
|
// suffix, so (a) parseVersion classifies it as Stamped when its own channel reads it back
|
|
// (a "-beta"-suffixed stamp would classify as Unknown — the V4 stamp-classifiability
|
|
// requirement), and (b) stable's stamp value is byte-identical regardless of the channel
|
|
// build. The channel is carried by the ISOLATED namespace (see extStateNamespace), never
|
|
// baked into the stamp. Distinct from appVersion() precisely so the display can say
|
|
// "-beta" while the stamp stays classifiable and stable-identical.
|
|
const std::string& stampVersion();
|
|
|
|
// The project ext-state namespace this channel reads and writes. Stable: "reasampler"
|
|
// (byte-identical to the pre-V4 build). Beta: "reasampler_beta". FOREVER-STABLE per
|
|
// channel once shipped — changing either orphans every already-saved project's state.
|
|
//
|
|
// ISOLATION SEMANTICS (V4, accepted — not a bug): a channel reads/writes ONLY its own
|
|
// namespace. A project saved by stable shows empty/default ReaSampler state when opened
|
|
// in beta, and vice versa. There is NO cross-namespace read, migration, or fallback in
|
|
// this wave — that isolation is the safety property (a beta can never read or rewrite a
|
|
// stable project's bank/view/tail state).
|
|
const std::string& extStateNamespace();
|
|
|
|
// The FOREVER-STABLE command-id prefix every bindable action mints its id from. Stable:
|
|
// "CEREBELLUM_REASAMPLER_" (byte-identical to the shipped ids). Beta:
|
|
// "CEREBELLUM_REASAMPLER_BETA_", a DISTINCT forever-family so beta and stable actions
|
|
// never collide in REAPER's one Actions list and their keybindings stay independent.
|
|
// Callers concatenate their per-action suffix onto this (e.g. prefix + "CAPTURE_TRACK").
|
|
// PERMANENT once a beta ships — mark any minted id FOREVER-STABLE like stable's.
|
|
const std::string& commandIdPrefix();
|
|
|
|
// The Actions-list DISPLAY-NAME prefix, so two coexisting channels are distinguishable in
|
|
// REAPER's Actions list. Stable: "ReaSampler: " (unchanged). Beta: "ReaSampler beta: ".
|
|
// Callers build a gaccel desc as actionDisplayPrefix() + "capture selected track", etc.
|
|
const std::string& actionDisplayPrefix();
|
|
|
|
// The binary/module OUTPUT NAME base. Stable: "reaper_reasampler". Beta:
|
|
// "reaper_reasampler_beta". Mirrors the CMake OUTPUT_NAME (which is the authoritative
|
|
// artifact name); exposed here for any in-binary self-identification. REAPER dlopen's
|
|
// any reaper_* module, so both channels load side-by-side.
|
|
const std::string& binaryName();
|
|
|
|
// The docked bank-panel identity strings, channel-qualified so the two panels are
|
|
// distinguishable and do not fight over one persisted dock slot (a REAPER-global
|
|
// collision surface — DockWindowAddEx's identstr keys the saved dock position).
|
|
// dockTitle() — the visible dock tab title. Stable: "ReaSampler Bank".
|
|
// Beta: "ReaSampler Bank beta".
|
|
// dockIdent() — the persisted dock-position ident. Stable: "reasampler_bank_panel".
|
|
// Beta: "reasampler_bank_panel_beta". FOREVER-STABLE per channel.
|
|
const std::string& dockTitle();
|
|
const std::string& dockIdent();
|
|
|
|
// --- VST3 instrument identity (S18, beta-in-isolation) ------------------------------
|
|
//
|
|
// The ReaSampler 9000 VST3 instrument forks its plugin identity per channel exactly as the
|
|
// extension forks its binary/dock idents above — one channel per binary, all derived from
|
|
// the ONE channel bit here, so the VST shell carries no #ifdef fork. These are the VST's
|
|
// analogues of binaryName()/dockTitle(): the on-disk module name and the human-facing name.
|
|
//
|
|
// vstOutputName() — the CMake OUTPUT_NAME base for the .vst3 module. Stable:
|
|
// "reasampler_9000" (byte-identical to pre-S18). Beta:
|
|
// "reasampler_9000_beta". Mirrors the CMake target's OUTPUT_NAME (the
|
|
// authoritative artifact name); exposed here so the one derivation lives
|
|
// in this module. FOREVER-STABLE per channel — the on-disk filename a
|
|
// REAPER project's saved instance path may reference.
|
|
// vstPluginName() — the factory display name (FX browser), editor title band, and S6
|
|
// embed-strip label. Stable: "ReaSampler 9000". Beta:
|
|
// "ReaSampler 9000 beta". Sourced from here, never a literal in
|
|
// reasampler_vst.h / vst_entry.cpp / the editor / the embed strip.
|
|
//
|
|
// NOTE: the VST3 CLASS UID is NOT here — a UID is not a string derivation but a compile-time
|
|
// FUID/INLINE_UID constant the factory needs in brace-init form; it lives in reasampler_vst.h,
|
|
// channel-selected by the same REASAMPLER_CHANNEL_IS_BETA bit. This module owns the string
|
|
// identity; reasampler_vst.h owns the binary UID identity. The version display the factory
|
|
// stamps into PClassInfo2 reuses appVersion() (it already renders "-beta" on beta) — no
|
|
// separate VST version accessor.
|
|
const std::string& vstOutputName();
|
|
const std::string& vstPluginName();
|
|
|
|
// --- Channel-qualified action id / name builders ------------------------------------
|
|
//
|
|
// The two composition helpers every action-registering shell (main.cpp, actions.cpp)
|
|
// funnels through, so command ids and Actions-list names are qualified IDENTICALLY on
|
|
// every channel from ONE definition — no shell re-implements the concatenation.
|
|
//
|
|
// channelCommandId(suffix): commandIdPrefix() + suffix. `suffix` is the per-action tail
|
|
// WITHOUT the family prefix (e.g. "CAPTURE_TRACK", "SHOW_VERSION"). Stable yields the
|
|
// exact shipped id ("CEREBELLUM_REASAMPLER_CAPTURE_TRACK"); beta yields the isolated
|
|
// forever-family id ("CEREBELLUM_REASAMPLER_BETA_CAPTURE_TRACK"). FOREVER-STABLE per
|
|
// channel — a suffix, once shipped, is as permanent as the prefix.
|
|
// channelActionName(phrase): actionDisplayPrefix() + phrase. `phrase` is the action's
|
|
// human description WITHOUT the "ReaSampler: " lead (e.g. "capture selected track(s)").
|
|
// Stable yields "ReaSampler: capture selected track(s)"; beta prefixes "ReaSampler beta: "
|
|
// so the two channels' actions are distinguishable in one Actions list.
|
|
std::string channelCommandId(const std::string& suffix);
|
|
std::string channelActionName(const std::string& phrase);
|
|
|
|
// A parsed semver triple. Kept minimal — major.minor.patch as integers, for ORDERING
|
|
// only. It deliberately does NOT round-trip back to the display string (the leading
|
|
// zero is a rendering concern owned by the authoritative string, not reconstructable
|
|
// from the integer patch). parseVersion returns nullopt on malformed input.
|
|
struct Version {
|
|
int major = 0;
|
|
int minor = 0;
|
|
int patch = 0;
|
|
};
|
|
|
|
// Parse "x.y.z" (each component a non-negative integer, leading zeros allowed) into a
|
|
// Version. Returns nullopt on anything malformed: wrong component count, non-digits, a
|
|
// leading `-`, empty components, or trailing garbage. Used for ordering two stamps and
|
|
// for validating a stored stamp before comparing.
|
|
std::optional<Version> parseVersion(const std::string& s);
|
|
|
|
// Numeric ordering by (major, minor, patch). a < b iff a precedes b. So
|
|
// 0.9.01 < 0.9.02 < 0.10.01 (numeric compare, NOT lexicographic — 10 > 9).
|
|
bool versionLess(const Version& a, const Version& b);
|
|
|
|
// The writing-version a project was last saved with, recovered from its ext-state
|
|
// stamp. A project saved before this feature shipped has NO stamp — that is the
|
|
// explicit `preVersioning` case (kind), not an error and not a warning. A present-but-
|
|
// malformed value is `unknown` (also silent — a corrupt stamp is ignored, never
|
|
// throws). A well-formed value is `stamped` and carries the exact stored string plus
|
|
// its parsed triple for comparison.
|
|
struct WritingVersion {
|
|
enum class Kind {
|
|
PreVersioning, // no stamp stored — a project written before versioning shipped
|
|
Unknown, // a stamp was stored but is not parseable — ignored, not an error
|
|
Stamped, // a well-formed stamp
|
|
};
|
|
Kind kind = Kind::PreVersioning;
|
|
std::string raw; // the exact stored string (empty for PreVersioning)
|
|
Version parsed; // meaningful only when kind == Stamped
|
|
};
|
|
|
|
// Classify a raw stored stamp value (exactly what GetProjExtState returned for the
|
|
// version key). Empty -> PreVersioning; non-empty but unparseable -> Unknown; parseable
|
|
// -> Stamped. Pure so the three-way classification is test-pinned; persist calls this
|
|
// with the raw ext-state read and never has to reason about the cases itself.
|
|
WritingVersion classifyWritingVersion(const std::string& rawStamp);
|
|
|
|
} // namespace reasampler
|