feat(version): V4 beta-in-isolation channel via -DREASAMPLER_CHANNEL
Compile-time channel flag forks a fully isolated reaper_reasampler_beta (namespace, command-id prefix, action names, dock ident, -beta render) from one auditable app_version definition. Stable identity byte-unchanged.
This commit is contained in:
+121
-69
@@ -18,6 +18,7 @@
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -34,12 +35,28 @@
|
||||
#include "track_guid.h"
|
||||
#include "view.h"
|
||||
|
||||
// Persistent action-id prefix for the ReaSampler action family.
|
||||
// Every bindable action (capture / insert / slot / verify) mints its command id
|
||||
// from a string beginning with this prefix, e.g. "CEREBELLUM_REASAMPLER_CAPTURE_TRACK".
|
||||
// FOREVER-STABLE once shipped: user keybindings key off these strings, so the
|
||||
// prefix and any minted id must never change after release.
|
||||
#define REASAMPLER_ACTION_PREFIX "CEREBELLUM_REASAMPLER_"
|
||||
// Persistent action-id family (Phase V, V4 — channel-qualified). Every bindable action
|
||||
// mints its command id from commandIdPrefix() + a per-action SUFFIX, and its Actions-list
|
||||
// name from actionDisplayPrefix() + a phrase, both derived from the ONE channel bit in the
|
||||
// pure app_version module (channelCommandId / channelActionName). Stable rebuilds 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
|
||||
// shipped suffix is as permanent as the prefix; user keybindings key off the composed id.
|
||||
//
|
||||
// The composed id strings are held here for the module's lifetime (idStore) so both the
|
||||
// register call and the mirroring '-command_id' unregister pass the SAME stable pointer.
|
||||
// A std::deque (NOT vector) is used deliberately: it never invalidates references to
|
||||
// existing elements on push_back, so a c_str() handed out early stays valid after later
|
||||
// interning — the unload path re-presents these same pointers.
|
||||
static std::deque<std::string> g_idStore;
|
||||
|
||||
// Interns a composed command-id string for the module lifetime and returns its C string.
|
||||
// Appended-to only during startup registration and read on unload; never cleared until
|
||||
// process exit, and deque guarantees the returned pointer stays valid.
|
||||
static const char* internCmdId(const std::string& suffix) {
|
||||
g_idStore.push_back(reasampler::channelCommandId(suffix));
|
||||
return g_idStore.back().c_str();
|
||||
}
|
||||
|
||||
// Globals other files reference via `extern`.
|
||||
REAPER_PLUGIN_HINSTANCE g_hInst = nullptr; // this module's instance handle
|
||||
@@ -63,9 +80,16 @@ reaper_plugin_info_t* g_rec = nullptr; // REAPER's dispatch struct
|
||||
// scope and sized to the table. FOREVER-STABLE id strings live in the table.
|
||||
static std::vector<int> g_captureCmdIds;
|
||||
static std::vector<gaccel_register_t> g_captureAccels;
|
||||
// Channel-qualified capture-action labels, one per table row. REAPER holds each gaccel's
|
||||
// `desc` pointer, so the composed strings live here for the module lifetime (parallel to
|
||||
// g_captureAccels; never resized after the registration loop sets it).
|
||||
static std::vector<std::string> g_captureDescs;
|
||||
|
||||
// Retired capture-action command-id strings. Kept ONLY to mirror-unregister them on
|
||||
// unload so a user's stale keybindings are cleaned up. Never re-register these.
|
||||
// Retired capture-action command-id SUFFIXES. Kept ONLY to mirror-unregister them on
|
||||
// unload so a user's stale keybindings are cleaned up. Never re-register these. Composed
|
||||
// through the channel prefix at unload (channelCommandId) so a beta unload clears beta-
|
||||
// qualified retired ids and a stable unload clears stable's — each channel cleans up only
|
||||
// its own family.
|
||||
// * The M7 four-mode ids (tracks/items/razor WET).
|
||||
// * CAPTURE_MASTER and CAPTURE_MASTER_REALTIME — the master offline scope and the
|
||||
// master realtime action are REMOVED (capture is now item + track only; realtime
|
||||
@@ -73,14 +97,14 @@ static std::vector<gaccel_register_t> g_captureAccels;
|
||||
// * CAPTURE_ITEM_TAIL and CAPTURE_TRACK_TAIL — the former per-action tail variants
|
||||
// are REMOVED; tail is now a panel-setting toggle, not a paired action. Retired so
|
||||
// old keybindings clear.
|
||||
static const char* const kRetiredCaptureCmdStrings[] = {
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_TRACKS_WET",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_ITEMS_WET",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_RAZOR_WET",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_MASTER",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_MASTER_REALTIME",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_ITEM_TAIL",
|
||||
"CEREBELLUM_REASAMPLER_CAPTURE_TRACK_TAIL",
|
||||
static const char* const kRetiredCaptureCmdSuffixes[] = {
|
||||
"CAPTURE_TRACKS_WET",
|
||||
"CAPTURE_ITEMS_WET",
|
||||
"CAPTURE_RAZOR_WET",
|
||||
"CAPTURE_MASTER",
|
||||
"CAPTURE_MASTER_REALTIME",
|
||||
"CAPTURE_ITEM_TAIL",
|
||||
"CAPTURE_TRACK_TAIL",
|
||||
};
|
||||
|
||||
// Command id for "ReaSampler: toggle bank panel" (M5). FOREVER-STABLE string.
|
||||
@@ -811,6 +835,25 @@ static gaccel_register_t g_accelCaptureTrackRealtime{};
|
||||
static gaccel_register_t g_accelCancelRealtime{};
|
||||
static gaccel_register_t g_accelShowVersion{};
|
||||
|
||||
// gaccel desc storage. The Actions-list label is channel-qualified at runtime
|
||||
// (channelActionName) so it cannot be a string literal; REAPER holds the gaccel's `desc`
|
||||
// pointer, so each label lives here for the module lifetime. Composed once at registration.
|
||||
static std::string g_descToggleBankPanel;
|
||||
static std::string g_descInsertSelected;
|
||||
static std::string g_descInsertSelectedConform;
|
||||
static std::string g_descCaptureTrackRealtime;
|
||||
static std::string g_descCancelRealtime;
|
||||
static std::string g_descShowVersion;
|
||||
|
||||
// Composed command-id strings (channel-qualified), interned so register and the mirroring
|
||||
// '-command_id' unregister pass the SAME pointer. Set during registration; read on unload.
|
||||
static const char* g_idToggleBankPanel = nullptr;
|
||||
static const char* g_idInsertSelected = nullptr;
|
||||
static const char* g_idInsertSelectedConform = nullptr;
|
||||
static const char* g_idCaptureTrackRealtime = nullptr;
|
||||
static const char* g_idCancelRealtime = nullptr;
|
||||
static const char* g_idShowVersion = nullptr;
|
||||
|
||||
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t* rec)
|
||||
{
|
||||
@@ -841,40 +884,42 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
reasampler::designViewUnregisterActions(g_rec);
|
||||
// Tear down the multi-bank action family (B3) — same mirror-unregister.
|
||||
reasampler::bankUnregisterActions(g_rec);
|
||||
// Each '-command_id' re-presents the SAME interned, channel-qualified pointer
|
||||
// used at register (g_id*), so the mirror-unregister matches exactly.
|
||||
g_rec->Register("-gaccel", (void*)&g_accelShowVersion);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "SHOW_VERSION"));
|
||||
g_rec->Register("-command_id", (void*)g_idShowVersion);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCancelRealtime);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "CANCEL_REALTIME_CAPTURE"));
|
||||
g_rec->Register("-command_id", (void*)g_idCancelRealtime);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCaptureTrackRealtime);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "CAPTURE_TRACK_REALTIME"));
|
||||
g_rec->Register("-command_id", (void*)g_idCaptureTrackRealtime);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelInsertSelectedConform);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "INSERT_SELECTED_CONFORM"));
|
||||
g_rec->Register("-command_id", (void*)g_idInsertSelectedConform);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelInsertSelected);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "INSERT_SELECTED"));
|
||||
g_rec->Register("-command_id", (void*)g_idInsertSelected);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelToggleBankPanel);
|
||||
g_rec->Register("-command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "TOGGLE_BANK_PANEL"));
|
||||
// Mirror-unregister the capture family: gaccel + command_id per row,
|
||||
// with '-'-prefixed strings (per the contract). The FOREVER-STABLE id
|
||||
// strings come from the same table used to register them.
|
||||
g_rec->Register("-command_id", (void*)g_idToggleBankPanel);
|
||||
// Mirror-unregister the capture family: gaccel + command_id per row, with
|
||||
// '-'-prefixed strings (per the contract). The command id is re-composed from
|
||||
// the same suffix + channel prefix used at register — identical string.
|
||||
{
|
||||
const auto& table = reasampler::captureActionTable();
|
||||
for (std::size_t i = 0; i < table.size(); ++i)
|
||||
{
|
||||
if (i < g_captureAccels.size())
|
||||
g_rec->Register("-gaccel", (void*)&g_captureAccels[i]);
|
||||
g_rec->Register("-command_id", (void*)table[i].commandString);
|
||||
const std::string id =
|
||||
reasampler::channelCommandId(table[i].commandSuffix);
|
||||
g_rec->Register("-command_id", (void*)id.c_str());
|
||||
}
|
||||
}
|
||||
// Retire the removed M7 command ids (command_id only — we never held a
|
||||
// gaccel for them this session). Clears stale user keybindings on unload.
|
||||
for (const char* id : kRetiredCaptureCmdStrings)
|
||||
g_rec->Register("-command_id", (void*)id);
|
||||
// Retire the removed M7 command ids (command_id only — we never held a gaccel
|
||||
// for them this session). Clears stale user keybindings on unload. Composed
|
||||
// per channel so a beta clears beta-qualified retired ids, stable clears its own.
|
||||
for (const char* suffix : kRetiredCaptureCmdSuffixes)
|
||||
{
|
||||
const std::string id = reasampler::channelCommandId(suffix);
|
||||
g_rec->Register("-command_id", (void*)id.c_str());
|
||||
}
|
||||
}
|
||||
// Destroy the docked window and release cached thumbnails before we drop
|
||||
// the API pointers (DockWindowRemove/DestroyWindow need them live).
|
||||
@@ -903,15 +948,21 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
const auto& table = reasampler::captureActionTable();
|
||||
g_captureCmdIds.assign(table.size(), 0);
|
||||
g_captureAccels.assign(table.size(), gaccel_register_t{});
|
||||
g_captureDescs.assign(table.size(), std::string{});
|
||||
for (std::size_t i = 0; i < table.size(); ++i)
|
||||
{
|
||||
// Compose the channel-qualified id (prefix + suffix) and label
|
||||
// ("ReaSampler[ beta]: " + phrase). The id is interned so unregister re-presents
|
||||
// the same pointer; the label lives in g_captureDescs for the gaccel's lifetime.
|
||||
const int cmd =
|
||||
rec->Register("command_id", (void*)table[i].commandString);
|
||||
rec->Register("command_id", (void*)internCmdId(table[i].commandSuffix));
|
||||
g_captureCmdIds[i] = cmd;
|
||||
if (cmd)
|
||||
{
|
||||
g_captureDescs[i] =
|
||||
reasampler::channelActionName(table[i].descriptionPhrase);
|
||||
g_captureAccels[i].accel.cmd = cmd;
|
||||
g_captureAccels[i].desc = table[i].description;
|
||||
g_captureAccels[i].desc = g_captureDescs[i].c_str();
|
||||
rec->Register("gaccel", (void*)&g_captureAccels[i]);
|
||||
}
|
||||
}
|
||||
@@ -923,14 +974,14 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
reasampler::bankPanelInit(&g_session);
|
||||
|
||||
// Register the M5 "toggle bank panel" action (command_id -> gaccel ->
|
||||
// hookcommand + toggleaction for the checked state).
|
||||
g_cmdToggleBankPanel = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "TOGGLE_BANK_PANEL"));
|
||||
// hookcommand + toggleaction for the checked state). Id + label are channel-qualified.
|
||||
g_idToggleBankPanel = internCmdId("TOGGLE_BANK_PANEL");
|
||||
g_cmdToggleBankPanel = rec->Register("command_id", (void*)g_idToggleBankPanel);
|
||||
if (g_cmdToggleBankPanel)
|
||||
{
|
||||
g_descToggleBankPanel = reasampler::channelActionName("toggle bank panel");
|
||||
g_accelToggleBankPanel.accel.cmd = g_cmdToggleBankPanel;
|
||||
g_accelToggleBankPanel.desc = "ReaSampler: toggle bank panel";
|
||||
g_accelToggleBankPanel.desc = g_descToggleBankPanel.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelToggleBankPanel);
|
||||
rec->Register("toggleaction", (void*)&OnToggleAction);
|
||||
}
|
||||
@@ -938,65 +989,66 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
// Register the M6 insert actions (command_id -> gaccel -> hookcommand). Two
|
||||
// variants: native-length (default, no stretch) and the EXPLICIT conform-to-
|
||||
// tempo opt-in. Both read the bank panel selection and place at the edit cursor.
|
||||
g_cmdInsertSelected = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "INSERT_SELECTED"));
|
||||
g_idInsertSelected = internCmdId("INSERT_SELECTED");
|
||||
g_cmdInsertSelected = rec->Register("command_id", (void*)g_idInsertSelected);
|
||||
if (g_cmdInsertSelected)
|
||||
{
|
||||
g_descInsertSelected =
|
||||
reasampler::channelActionName("insert selected sample at edit cursor");
|
||||
g_accelInsertSelected.accel.cmd = g_cmdInsertSelected;
|
||||
g_accelInsertSelected.desc =
|
||||
"ReaSampler: insert selected sample at edit cursor";
|
||||
g_accelInsertSelected.desc = g_descInsertSelected.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelInsertSelected);
|
||||
}
|
||||
|
||||
g_cmdInsertSelectedConform = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "INSERT_SELECTED_CONFORM"));
|
||||
g_idInsertSelectedConform = internCmdId("INSERT_SELECTED_CONFORM");
|
||||
g_cmdInsertSelectedConform = rec->Register("command_id", (void*)g_idInsertSelectedConform);
|
||||
if (g_cmdInsertSelectedConform)
|
||||
{
|
||||
g_descInsertSelectedConform = reasampler::channelActionName(
|
||||
"insert selected sample at edit cursor (conform to tempo)");
|
||||
g_accelInsertSelectedConform.accel.cmd = g_cmdInsertSelectedConform;
|
||||
g_accelInsertSelectedConform.desc =
|
||||
"ReaSampler: insert selected sample at edit cursor (conform to tempo)";
|
||||
g_accelInsertSelectedConform.desc = g_descInsertSelectedConform.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelInsertSelectedConform);
|
||||
}
|
||||
|
||||
// Register the "capture selected track (realtime)" action (command_id -> gaccel ->
|
||||
// hookcommand). Realtime sibling of the offline CAPTURE_TRACK scope: records the
|
||||
// selected track's own output in realtime into a hidden temp track, moves it into
|
||||
// the bank. Dialog-free. NEW FOREVER-STABLE id string.
|
||||
g_cmdCaptureTrackRealtime = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "CAPTURE_TRACK_REALTIME"));
|
||||
// the bank. Dialog-free. Channel-qualified FOREVER-STABLE id.
|
||||
g_idCaptureTrackRealtime = internCmdId("CAPTURE_TRACK_REALTIME");
|
||||
g_cmdCaptureTrackRealtime = rec->Register("command_id", (void*)g_idCaptureTrackRealtime);
|
||||
if (g_cmdCaptureTrackRealtime)
|
||||
{
|
||||
g_descCaptureTrackRealtime =
|
||||
reasampler::channelActionName("capture selected track (realtime)");
|
||||
g_accelCaptureTrackRealtime.accel.cmd = g_cmdCaptureTrackRealtime;
|
||||
g_accelCaptureTrackRealtime.desc =
|
||||
"ReaSampler: capture selected track (realtime)";
|
||||
g_accelCaptureTrackRealtime.desc = g_descCaptureTrackRealtime.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCaptureTrackRealtime);
|
||||
}
|
||||
|
||||
// Cancel-in-flight sibling: aborts a running realtime capture (stop + restore).
|
||||
// FOREVER-STABLE id string.
|
||||
g_cmdCancelRealtime = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "CANCEL_REALTIME_CAPTURE"));
|
||||
// Channel-qualified FOREVER-STABLE id.
|
||||
g_idCancelRealtime = internCmdId("CANCEL_REALTIME_CAPTURE");
|
||||
g_cmdCancelRealtime = rec->Register("command_id", (void*)g_idCancelRealtime);
|
||||
if (g_cmdCancelRealtime)
|
||||
{
|
||||
g_descCancelRealtime = reasampler::channelActionName("cancel realtime capture");
|
||||
g_accelCancelRealtime.accel.cmd = g_cmdCancelRealtime;
|
||||
g_accelCancelRealtime.desc = "ReaSampler: cancel realtime capture";
|
||||
g_accelCancelRealtime.desc = g_descCancelRealtime.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCancelRealtime);
|
||||
}
|
||||
|
||||
// Register the Phase V "show version" action (command_id -> gaccel -> hookcommand).
|
||||
// On-demand only — prints the CMake-sourced version to the console when fired; no
|
||||
// startup print. FOREVER-STABLE id string.
|
||||
g_cmdShowVersion = rec->Register(
|
||||
"command_id",
|
||||
(void*)(REASAMPLER_ACTION_PREFIX "SHOW_VERSION"));
|
||||
// startup print. Channel-qualified FOREVER-STABLE id; label carries the channel prefix
|
||||
// so a beta's "show version" is distinguishable from stable's in the Actions list.
|
||||
g_idShowVersion = internCmdId("SHOW_VERSION");
|
||||
g_cmdShowVersion = rec->Register("command_id", (void*)g_idShowVersion);
|
||||
if (g_cmdShowVersion)
|
||||
{
|
||||
g_descShowVersion = reasampler::channelActionName("show version");
|
||||
g_accelShowVersion.accel.cmd = g_cmdShowVersion;
|
||||
g_accelShowVersion.desc = "ReaSampler: show version";
|
||||
g_accelShowVersion.desc = g_descShowVersion.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelShowVersion);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user