Q-W6: registration table (OCP) in main.cpp; bank verbs -> shell/bank_ops(Session&); persist.h + wav_trim + namespaces.h shims deleted; 61/61
capture.h realtime seam split to capture_realtime_shell.h; GetProjExtState grow-loop rehomed to core/wire/ext_state_read; stale persist.cpp/bank_panel.cpp comment refs fixed; CLAUDE.md persist/bank_book/actions bullets updated. Command-id suffixes, display phrases, and undo labels byte-identical.
This commit is contained in:
+135
-386
@@ -14,24 +14,25 @@
|
||||
// storage for those global pointers. Every other .cpp includes
|
||||
// reaper_plugin_functions.h WITHOUT the define and gets `extern` declarations.
|
||||
//
|
||||
// Since Q-W3 this TU is ONLY pointers + entry + dispatch: the capture
|
||||
// orchestration it used to carry lives in shell/capture/ (capture_orchestrator /
|
||||
// capture_batch / scope_resolve / realtime_lifecycle). The registration blocks
|
||||
// below are slated for Q-W6's registration table.
|
||||
// Since Q-W3 this TU is ONLY pointers + entry + dispatch; since Q-W6 its own
|
||||
// action family registers through the DATA-DRIVEN TABLE below (kMainActionRows +
|
||||
// action_registry's registerActionTable/actionTableHandleCommand/
|
||||
// unregisterActionTable) — adding a bindable action here means adding ONE row and
|
||||
// its handler function, nothing else (OCP). The design_view / bank / ingest
|
||||
// families keep their own register/handle/unregister triples, called from entry.
|
||||
|
||||
#define REAPERAPI_IMPLEMENT
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/capture/render_settings.h" // captureActionTable
|
||||
#include "core/version/app_version.h" // channelCommandId / channelActionName / appVersion
|
||||
#include "core/version/app_version.h" // channelCommandId / appVersion
|
||||
#include "ingest.h"
|
||||
#include "persist.h"
|
||||
#include "shell/actions/action_registry.h" // the Q-W6 registration table
|
||||
#include "shell/actions/bank_actions.h" // multi-bank action family (B3; Q-W4 home)
|
||||
#include "shell/actions/design_view_actions.h" // Design View action family (D4; Q-W4 home)
|
||||
#include "shell/capture/capture_batch.h" // batch + recapture action bodies
|
||||
@@ -39,75 +40,26 @@
|
||||
#include "shell/capture/realtime_lifecycle.h" // in-flight realtime state + tick driver
|
||||
#include "shell/panel/panel_input.h" // bankPanelRefresh / bankPanelNotifyProjectLoaded
|
||||
#include "shell/panel/panel_window.h" // panel lifecycle (init/toggle/open-query/shutdown)
|
||||
#include "shell/persist/session.h" // ReaSamplerSession
|
||||
#include "shell/view/view.h" // reconcileManagedLanes / applyMode
|
||||
|
||||
namespace capture = reasampler::capture;
|
||||
using reasampler::version::channelActionName;
|
||||
using reasampler::version::channelCommandId;
|
||||
|
||||
// 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(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
|
||||
reaper_plugin_info_t* g_rec = nullptr; // REAPER's dispatch struct
|
||||
|
||||
// ---- Capture action family (two FX scopes) ---------------------------------
|
||||
// Two bindable SCOPE actions from captureActionTable() (render_settings, pure):
|
||||
// capture item / track. Each infers its range (razor-else-time) and enforces the
|
||||
// FX-scope invariant via FX-bypass-around-render (FxBypassGuard, now in
|
||||
// capture_orchestrator):
|
||||
// Item -> take/item FX only (bypass the item's track + ancestors + master).
|
||||
// Track -> item FX + track's own FX (bypass ancestors + master).
|
||||
// There is NO master scope — to capture the master you render a track. (The master
|
||||
// track's FX/gain/pan are STILL neutralized for both scopes as the out-of-scope
|
||||
// chain — master is a bypass target, not a capture scope.) The retired M7
|
||||
// CAPTURE_TRACKS_WET / CAPTURE_ITEMS_WET / CAPTURE_RAZOR_WET ids AND the removed
|
||||
// CAPTURE_MASTER / CAPTURE_MASTER_REALTIME ids are mirror-unregistered on unload so
|
||||
// old keybindings clear cleanly.
|
||||
//
|
||||
// The minted command ids parallel the table rows 1:1 (same index). gaccel storage
|
||||
// must outlive registration (REAPER holds each pointer), so both vectors are file-
|
||||
// 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 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.
|
||||
// Retired 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 (channelIdFor) 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
|
||||
// taps the selected track). Their shipped ids are retired so old keybindings clear.
|
||||
// * 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.
|
||||
// are REMOVED; tail is now a panel-setting toggle, not a paired action.
|
||||
static const char* const kRetiredCaptureCmdSuffixes[] = {
|
||||
"CAPTURE_TRACKS_WET",
|
||||
"CAPTURE_ITEMS_WET",
|
||||
@@ -118,60 +70,6 @@ static const char* const kRetiredCaptureCmdSuffixes[] = {
|
||||
"CAPTURE_TRACK_TAIL",
|
||||
};
|
||||
|
||||
// Command id for "ReaSampler: toggle bank panel" (M5). FOREVER-STABLE string.
|
||||
// The docked grid window is display-only this wave (Wave A) — the action just
|
||||
// shows/hides it; it never captures, inserts, or mutates the bank.
|
||||
static int g_cmdToggleBankPanel = 0;
|
||||
|
||||
// Command ids for the M6 insert actions. FOREVER-STABLE strings. Two variants that
|
||||
// differ ONLY in the InsertOptions they build: the default inserts at native length
|
||||
// (no stretch, no conform); the "conform" variant is the EXPLICIT opt-in to REAPER's
|
||||
// try-to-match-project-tempo path (CONTEXT.md §insert: conform is opt-in, never
|
||||
// silent). Both read the bank panel's current selection and place at the edit cursor.
|
||||
static int g_cmdInsertSelected = 0;
|
||||
static int g_cmdInsertSelectedConform = 0;
|
||||
|
||||
// Command ids for the M11 batch-capture actions. NEW FOREVER-STABLE strings. One action
|
||||
// fires N captures: CAPTURE_BATCH_ITEMS -> one bank sample per selected item (item scope);
|
||||
// CAPTURE_BATCH_RAZOR -> one bank sample per razor area (track scope, each area's range).
|
||||
// Each unit honors every precision invariant; the original selection is restored on every
|
||||
// exit path. Bank-only, never places on the timeline (load-bearing principle).
|
||||
static int g_cmdCaptureBatchItems = 0;
|
||||
static int g_cmdCaptureBatchRazor = 0;
|
||||
|
||||
// Command id for the "capture selected track (realtime)" action. NEW FOREVER-STABLE
|
||||
// string. Records the selected track's OWN output in realtime (transport-driven) into
|
||||
// a hidden temp track via RealtimeRecordBackend, then moves the recorded file into the
|
||||
// bank. The realtime SIBLING of the offline CAPTURE_TRACK scope action: same range
|
||||
// logic (razor-else-time), same track selection, same bank/persist path, different
|
||||
// backend. Dialog-free. (Replaces the removed CAPTURE_MASTER_REALTIME action.)
|
||||
static int g_cmdCaptureTrackRealtime = 0;
|
||||
|
||||
// Command id for the M10 "re-capture from source" action. NEW FOREVER-STABLE string
|
||||
// (suffix RECAPTURE_FROM_SOURCE). Regenerates the bank panel's selected PROVENANCED
|
||||
// sample from its recorded source's current state and updates the Sample in place —
|
||||
// BANK-ONLY, never places on the timeline (load-bearing principle).
|
||||
static int g_cmdRecaptureFromSource = 0;
|
||||
|
||||
// Command id for the S8 "capture selected item / time-selection into bank + assign"
|
||||
// action. NEW FOREVER-STABLE string (suffix CAPTURE_ITEM_ASSIGN). Reuses the offline
|
||||
// Item-scope capture path (RunCapture) verbatim, then writes an S8 assignment request.
|
||||
// Lives in the capture family (not the ingest family) because it leans on the capture
|
||||
// render machinery (capture_orchestrator).
|
||||
static int g_cmdCaptureItemAssign = 0;
|
||||
|
||||
// Command id for the M8 "cancel realtime capture" action. FOREVER-STABLE string.
|
||||
// Aborts the in-flight realtime capture (stop + restore, non-destructive). No-op
|
||||
// (with a note) when nothing is in flight.
|
||||
static int g_cmdCancelRealtime = 0;
|
||||
|
||||
// Command id for the Phase V "show version" action. FOREVER-STABLE string. On demand
|
||||
// ONLY — prints the CMake-sourced version string to the console when fired. This is the
|
||||
// SOLE new console output the versioning wave adds; there is no unconditional startup
|
||||
// version print (routine console chatter was deliberately removed — it pops the console
|
||||
// window). The user copies this line into a bug report.
|
||||
static int g_cmdShowVersion = 0;
|
||||
|
||||
// The persistence session (M4): owns the in-memory BankModel and bridges it to
|
||||
// project ext state. A timer tick drives g_session.poll() to detect project
|
||||
// load / Save-As; capture adds Samples to g_session.bank() — which (B2) resolves to
|
||||
@@ -180,6 +78,102 @@ static int g_cmdShowVersion = 0;
|
||||
// with the .rpp. Replaces the M3 session-only g_bank.
|
||||
static reasampler::ReaSamplerSession g_session;
|
||||
|
||||
// Command id of the TOGGLE_BANK_PANEL row, resolved from the table once at load so
|
||||
// OnToggleAction's checked-state poll is a single int compare (no per-poll lookup).
|
||||
static int g_cmdToggleBankPanel = 0;
|
||||
|
||||
// --- Action handlers (the table's function pointers) --------------------------
|
||||
//
|
||||
// Each is a thin stateless routing shim: (session, per-row arg) -> the action body
|
||||
// hoisted in Q-W3/Q-W4 (shell/capture/, shell/panel/). The bodies own all behavior;
|
||||
// these exist only so the table rows can be plain data with flat function pointers.
|
||||
|
||||
// Capture scope family: `arg` is the captureActionTable() row index — the table rows
|
||||
// below are built by iterating that pure taxonomy, so the routing stays 1:1 by
|
||||
// construction (never a hand-kept parallel list).
|
||||
static void RunCaptureScopeRow(int arg) {
|
||||
capture::RunCapture(g_session,
|
||||
capture::captureActionTable()[static_cast<std::size_t>(arg)]);
|
||||
}
|
||||
static void RunToggleBankPanel(int) { reasampler::bankPanelToggle(); }
|
||||
static void RunCaptureItemAssign(int) { capture::RunCaptureItemAssign(g_session); }
|
||||
// Insert: `arg` != 0 is the EXPLICIT conform-to-project-tempo opt-in (CONTEXT.md
|
||||
// §insert: conform is opt-in, never silent); 0 inserts at native length.
|
||||
static void RunInsertSelected(int arg) {
|
||||
capture::RunInsertSelected(g_session, arg != 0);
|
||||
}
|
||||
static void RunBatchCaptureItems(int) { capture::RunBatchCaptureItems(g_session); }
|
||||
static void RunBatchCaptureRazor(int) { capture::RunBatchCaptureRazor(g_session); }
|
||||
static void RunCaptureRealtime(int) { capture::RunCaptureRealtimeTrack(g_session); }
|
||||
static void RunCancelRealtime(int) { capture::RunCancelRealtime(g_session); }
|
||||
static void RunRecaptureFromSource(int) { capture::RunRecaptureFromSource(g_session); }
|
||||
static void RunShowVersion(int) {
|
||||
// On-demand version readout — the ONLY version output on any path (Phase V: no
|
||||
// unconditional startup print; routine console chatter pops the console window).
|
||||
ShowConsoleMsg(("ReaSampler " + reasampler::version::appVersion() + "\n").c_str());
|
||||
}
|
||||
|
||||
// --- The registration table (Q-W6) --------------------------------------------
|
||||
//
|
||||
// ONE row per bindable action this TU owns: FOREVER-STABLE id suffix (channel prefix
|
||||
// composed at register — stable rebuilds the exact shipped id, e.g.
|
||||
// "CEREBELLUM_REASAMPLER_CAPTURE_TRACK"; beta its isolated forever-family), the
|
||||
// Actions-list phrase (after the "ReaSampler[ beta]: " lead), the handler, and its
|
||||
// per-row arg. Registration, hookcommand dispatch, and the unload mirror-unregister
|
||||
// all iterate this data — adding an action = adding a row + a handler above.
|
||||
//
|
||||
// The capture scope rows (CAPTURE_ITEM / CAPTURE_TRACK) come first, sourced from the
|
||||
// pure captureActionTable() taxonomy (render_settings) — suffix/phrase live in that
|
||||
// one testable list, and `arg` carries the row index back to RunCapture. The
|
||||
// remaining rows are this TU's singles, in the pre-table registration order.
|
||||
static std::vector<reasampler::ActionTableRow> buildMainActionTable() {
|
||||
using reasampler::ActionTableRow;
|
||||
std::vector<ActionTableRow> rows;
|
||||
|
||||
const auto& cap = capture::captureActionTable();
|
||||
for (std::size_t i = 0; i < cap.size(); ++i)
|
||||
rows.push_back(ActionTableRow{cap[i].commandSuffix, cap[i].descriptionPhrase,
|
||||
&RunCaptureScopeRow, static_cast<int>(i)});
|
||||
|
||||
// M5: show/hide the docked bank panel (display-only; never captures/inserts).
|
||||
rows.push_back({"TOGGLE_BANK_PANEL", "toggle bank panel", &RunToggleBankPanel});
|
||||
// S8: Item-scope capture + assignment-request write (capture family because it
|
||||
// leans on the capture render machinery; the other ingest surfaces live in the
|
||||
// ingest family and the panel drop callback).
|
||||
rows.push_back({"CAPTURE_ITEM_ASSIGN",
|
||||
"capture selected item into bank + assign to active instance",
|
||||
&RunCaptureItemAssign});
|
||||
// M6: place the panel's selected sample at the edit cursor. Two variants that
|
||||
// differ ONLY in InsertOptions — native length vs the explicit conform opt-in.
|
||||
rows.push_back({"INSERT_SELECTED", "insert selected sample at edit cursor",
|
||||
&RunInsertSelected, 0});
|
||||
rows.push_back({"INSERT_SELECTED_CONFORM",
|
||||
"insert selected sample at edit cursor (conform to tempo)",
|
||||
&RunInsertSelected, 1});
|
||||
// M11: one action fires N captures (per selected item / per razor area); the
|
||||
// original selection is restored on every exit path. Bank-only, never places.
|
||||
rows.push_back({"CAPTURE_BATCH_ITEMS",
|
||||
"batch capture selected items (one per item)",
|
||||
&RunBatchCaptureItems});
|
||||
rows.push_back({"CAPTURE_BATCH_RAZOR", "batch capture razor areas (one per area)",
|
||||
&RunBatchCaptureRazor});
|
||||
// M8: realtime sibling of the offline CAPTURE_TRACK scope — records the selected
|
||||
// track's own output into a hidden temp track, dialog-free — plus its
|
||||
// cancel-in-flight companion (stop + restore, non-destructive).
|
||||
rows.push_back({"CAPTURE_TRACK_REALTIME", "capture selected track (realtime)",
|
||||
&RunCaptureRealtime});
|
||||
rows.push_back({"CANCEL_REALTIME_CAPTURE", "cancel realtime capture",
|
||||
&RunCancelRealtime});
|
||||
// M10: regenerate the selected PROVENANCED sample from its recorded source's
|
||||
// current state, in place. Bank-only, never places on the timeline.
|
||||
rows.push_back({"RECAPTURE_FROM_SOURCE", "re-capture from source",
|
||||
&RunRecaptureFromSource});
|
||||
// Phase V: on-demand version readout for bug reports.
|
||||
rows.push_back({"SHOW_VERSION", "show version", &RunShowVersion});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
// The timer callback REAPER runs periodically (registered via "timer"). It only
|
||||
// forwards to the session poll — cheap per tick (reads the active project id and
|
||||
// its .rpp path, acts only on a change).
|
||||
@@ -275,33 +269,12 @@ static project_config_extension_t g_projectConfig{
|
||||
};
|
||||
|
||||
// REAPER calls this for EVERY action fired anywhere; claim only our own id,
|
||||
// return false otherwise so REAPER keeps looking.
|
||||
// return false otherwise so REAPER keeps looking. This TU's own family dispatches
|
||||
// through the registration table; the Q-W4 families claim their own ids after it.
|
||||
static bool OnHookCommand(int command, int /*flag*/)
|
||||
{
|
||||
if (command == 0) return false;
|
||||
// Three-scope capture family: command ids parallel captureActionTable() 1:1 by index.
|
||||
// Claim the fired id if it is one of ours and route to its table row.
|
||||
for (std::size_t i = 0; i < g_captureCmdIds.size(); ++i)
|
||||
if (command == g_captureCmdIds[i])
|
||||
{
|
||||
capture::RunCapture(g_session, capture::captureActionTable()[i]);
|
||||
return true;
|
||||
}
|
||||
if (command == g_cmdToggleBankPanel) { reasampler::bankPanelToggle(); return true; }
|
||||
if (command == g_cmdCaptureItemAssign) { capture::RunCaptureItemAssign(g_session); return true; }
|
||||
if (command == g_cmdInsertSelected) { capture::RunInsertSelected(g_session, false); return true; }
|
||||
if (command == g_cmdInsertSelectedConform) { capture::RunInsertSelected(g_session, true); return true; }
|
||||
if (command == g_cmdCaptureBatchItems) { capture::RunBatchCaptureItems(g_session); return true; }
|
||||
if (command == g_cmdCaptureBatchRazor) { capture::RunBatchCaptureRazor(g_session); return true; }
|
||||
if (command == g_cmdCaptureTrackRealtime) { capture::RunCaptureRealtimeTrack(g_session); return true; }
|
||||
if (command == g_cmdCancelRealtime) { capture::RunCancelRealtime(g_session); return true; }
|
||||
if (command == g_cmdRecaptureFromSource) { capture::RunRecaptureFromSource(g_session); return true; }
|
||||
if (command == g_cmdShowVersion)
|
||||
{
|
||||
// On-demand version readout — the ONLY version output on any path.
|
||||
ShowConsoleMsg(("ReaSampler " + reasampler::version::appVersion() + "\n").c_str());
|
||||
return true;
|
||||
}
|
||||
if (reasampler::actionTableHandleCommand(command)) return true;
|
||||
// Design View action family (D4). Claims only its own ids; returns false for the
|
||||
// rest so this hook keeps looking (per the contract).
|
||||
if (reasampler::designViewHandleCommand(command)) return true;
|
||||
@@ -316,51 +289,11 @@ static bool OnHookCommand(int command, int /*flag*/)
|
||||
// Return 1 (on) / 0 (off) for ids we own, -1 for everything else (per the contract).
|
||||
static int OnToggleAction(int command)
|
||||
{
|
||||
if (command == g_cmdToggleBankPanel)
|
||||
if (command != 0 && command == g_cmdToggleBankPanel)
|
||||
return reasampler::bankPanelIsOpen() ? 1 : 0;
|
||||
return -1; // not ours / non-toggling
|
||||
}
|
||||
|
||||
// gaccel storage must outlive registration — REAPER holds the pointer.
|
||||
// (The capture family's accels live in g_captureAccels, sized to the table.)
|
||||
static gaccel_register_t g_accelToggleBankPanel{};
|
||||
static gaccel_register_t g_accelCaptureItemAssign{};
|
||||
static gaccel_register_t g_accelInsertSelected{};
|
||||
static gaccel_register_t g_accelInsertSelectedConform{};
|
||||
static gaccel_register_t g_accelCaptureBatchItems{};
|
||||
static gaccel_register_t g_accelCaptureBatchRazor{};
|
||||
static gaccel_register_t g_accelCaptureTrackRealtime{};
|
||||
static gaccel_register_t g_accelCancelRealtime{};
|
||||
static gaccel_register_t g_accelRecaptureFromSource{};
|
||||
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_descCaptureItemAssign;
|
||||
static std::string g_descInsertSelected;
|
||||
static std::string g_descInsertSelectedConform;
|
||||
static std::string g_descCaptureBatchItems;
|
||||
static std::string g_descCaptureBatchRazor;
|
||||
static std::string g_descCaptureTrackRealtime;
|
||||
static std::string g_descCancelRealtime;
|
||||
static std::string g_descRecaptureFromSource;
|
||||
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_idCaptureItemAssign = nullptr;
|
||||
static const char* g_idInsertSelected = nullptr;
|
||||
static const char* g_idInsertSelectedConform = nullptr;
|
||||
static const char* g_idCaptureBatchItems = nullptr;
|
||||
static const char* g_idCaptureBatchRazor = nullptr;
|
||||
static const char* g_idCaptureTrackRealtime = nullptr;
|
||||
static const char* g_idCancelRealtime = nullptr;
|
||||
static const char* g_idRecaptureFromSource = 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)
|
||||
{
|
||||
@@ -387,49 +320,15 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
reasampler::bankUnregisterActions(g_rec);
|
||||
// Tear down the S8 ingest action family — same mirror-unregister.
|
||||
reasampler::ingestUnregisterActions(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*)g_idShowVersion);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelRecaptureFromSource);
|
||||
g_rec->Register("-command_id", (void*)g_idRecaptureFromSource);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCancelRealtime);
|
||||
g_rec->Register("-command_id", (void*)g_idCancelRealtime);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCaptureTrackRealtime);
|
||||
g_rec->Register("-command_id", (void*)g_idCaptureTrackRealtime);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCaptureBatchRazor);
|
||||
g_rec->Register("-command_id", (void*)g_idCaptureBatchRazor);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCaptureBatchItems);
|
||||
g_rec->Register("-command_id", (void*)g_idCaptureBatchItems);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelInsertSelectedConform);
|
||||
g_rec->Register("-command_id", (void*)g_idInsertSelectedConform);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelInsertSelected);
|
||||
g_rec->Register("-command_id", (void*)g_idInsertSelected);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelCaptureItemAssign);
|
||||
g_rec->Register("-command_id", (void*)g_idCaptureItemAssign);
|
||||
g_rec->Register("-gaccel", (void*)&g_accelToggleBankPanel);
|
||||
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 = capture::captureActionTable();
|
||||
for (std::size_t i = 0; i < table.size(); ++i)
|
||||
{
|
||||
if (i < g_captureAccels.size())
|
||||
g_rec->Register("-gaccel", (void*)&g_captureAccels[i]);
|
||||
const std::string id = 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
|
||||
// Tear down this TU's own family from the registration table (reverse
|
||||
// table order; each '-command_id' re-presents the SAME interned,
|
||||
// channel-qualified pointer used at register).
|
||||
reasampler::unregisterActionTable(g_rec);
|
||||
// Retire the REMOVED 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.
|
||||
// per channel so a beta clears beta-qualified retired ids, stable its own.
|
||||
for (const char* suffix : kRetiredCaptureCmdSuffixes)
|
||||
{
|
||||
const std::string id = channelCommandId(suffix);
|
||||
g_rec->Register("-command_id", (void*)id.c_str());
|
||||
}
|
||||
g_rec->Register("-command_id", (void*)reasampler::channelIdFor(suffix));
|
||||
}
|
||||
// Destroy the docked window and release cached thumbnails before we drop
|
||||
// the API pointers (DockWindowRemove/DestroyWindow need them live).
|
||||
@@ -450,173 +349,23 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
g_hInst = hInstance;
|
||||
g_rec = rec;
|
||||
|
||||
// Register the three-scope capture action family (command_id -> gaccel per table row).
|
||||
// The single hookcommand below routes every fired id back to its row by index.
|
||||
// g_captureAccels must be sized BEFORE the loop and never reallocated after —
|
||||
// REAPER holds a pointer to each element until we mirror-unregister it.
|
||||
{
|
||||
const auto& table = capture::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*)internCmdId(table[i].commandSuffix));
|
||||
g_captureCmdIds[i] = cmd;
|
||||
if (cmd)
|
||||
{
|
||||
g_captureDescs[i] = channelActionName(table[i].descriptionPhrase);
|
||||
g_captureAccels[i].accel.cmd = cmd;
|
||||
g_captureAccels[i].desc = g_captureDescs[i].c_str();
|
||||
rec->Register("gaccel", (void*)&g_captureAccels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Point the bank panel at the live session BEFORE registering its action, so
|
||||
// a toggle firing immediately has a session to read (M5). Does not open the
|
||||
// window — only stores the session pointer.
|
||||
reasampler::bankPanelInit(&g_session);
|
||||
|
||||
// Register the M5 "toggle bank panel" action (command_id -> gaccel ->
|
||||
// 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);
|
||||
// Register this TU's whole action family from the table: command_id -> gaccel
|
||||
// per row, all channel-qualified, all FOREVER-STABLE per channel.
|
||||
{
|
||||
const std::vector<reasampler::ActionTableRow> rows = buildMainActionTable();
|
||||
reasampler::registerActionTable(rec, rows.data(), rows.size());
|
||||
}
|
||||
|
||||
// The panel toggle renders a checked state — resolve its minted id once and
|
||||
// register the toggleaction hook that reports it.
|
||||
g_cmdToggleBankPanel = reasampler::actionTableCommandId("TOGGLE_BANK_PANEL");
|
||||
if (g_cmdToggleBankPanel)
|
||||
{
|
||||
g_descToggleBankPanel = channelActionName("toggle bank panel");
|
||||
g_accelToggleBankPanel.accel.cmd = g_cmdToggleBankPanel;
|
||||
g_accelToggleBankPanel.desc = g_descToggleBankPanel.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelToggleBankPanel);
|
||||
rec->Register("toggleaction", (void*)&OnToggleAction);
|
||||
}
|
||||
|
||||
// Register the S8 "capture selected item / time-selection into bank + assign" action
|
||||
// (command_id -> gaccel -> hookcommand). Reuses the Item-scope offline capture path and
|
||||
// writes an assignment request so the active instance plays the new sample. Channel-
|
||||
// qualified FOREVER-STABLE id (suffix CAPTURE_ITEM_ASSIGN). MIDI-bindable like every
|
||||
// capture action. Registered in the capture family (main.cpp) because it leans on the
|
||||
// capture render machinery; the other two ingest surfaces live in the ingest family
|
||||
// (Media-Explorer import) and the panel drop callback.
|
||||
g_idCaptureItemAssign = internCmdId("CAPTURE_ITEM_ASSIGN");
|
||||
g_cmdCaptureItemAssign = rec->Register("command_id", (void*)g_idCaptureItemAssign);
|
||||
if (g_cmdCaptureItemAssign)
|
||||
{
|
||||
g_descCaptureItemAssign = channelActionName(
|
||||
"capture selected item into bank + assign to active instance");
|
||||
g_accelCaptureItemAssign.accel.cmd = g_cmdCaptureItemAssign;
|
||||
g_accelCaptureItemAssign.desc = g_descCaptureItemAssign.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCaptureItemAssign);
|
||||
}
|
||||
|
||||
// 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_idInsertSelected = internCmdId("INSERT_SELECTED");
|
||||
g_cmdInsertSelected = rec->Register("command_id", (void*)g_idInsertSelected);
|
||||
if (g_cmdInsertSelected)
|
||||
{
|
||||
g_descInsertSelected = channelActionName("insert selected sample at edit cursor");
|
||||
g_accelInsertSelected.accel.cmd = g_cmdInsertSelected;
|
||||
g_accelInsertSelected.desc = g_descInsertSelected.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelInsertSelected);
|
||||
}
|
||||
|
||||
g_idInsertSelectedConform = internCmdId("INSERT_SELECTED_CONFORM");
|
||||
g_cmdInsertSelectedConform = rec->Register("command_id", (void*)g_idInsertSelectedConform);
|
||||
if (g_cmdInsertSelectedConform)
|
||||
{
|
||||
g_descInsertSelectedConform = channelActionName(
|
||||
"insert selected sample at edit cursor (conform to tempo)");
|
||||
g_accelInsertSelectedConform.accel.cmd = g_cmdInsertSelectedConform;
|
||||
g_accelInsertSelectedConform.desc = g_descInsertSelectedConform.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelInsertSelectedConform);
|
||||
}
|
||||
|
||||
// Register the M11 batch-capture actions (command_id -> gaccel -> hookcommand). Each
|
||||
// fires N captures (one bank sample per selected item / per razor area), honoring every
|
||||
// precision invariant per unit and restoring the original selection on every path.
|
||||
// Channel-qualified FOREVER-STABLE ids.
|
||||
g_idCaptureBatchItems = internCmdId("CAPTURE_BATCH_ITEMS");
|
||||
g_cmdCaptureBatchItems = rec->Register("command_id", (void*)g_idCaptureBatchItems);
|
||||
if (g_cmdCaptureBatchItems)
|
||||
{
|
||||
g_descCaptureBatchItems =
|
||||
channelActionName("batch capture selected items (one per item)");
|
||||
g_accelCaptureBatchItems.accel.cmd = g_cmdCaptureBatchItems;
|
||||
g_accelCaptureBatchItems.desc = g_descCaptureBatchItems.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCaptureBatchItems);
|
||||
}
|
||||
|
||||
g_idCaptureBatchRazor = internCmdId("CAPTURE_BATCH_RAZOR");
|
||||
g_cmdCaptureBatchRazor = rec->Register("command_id", (void*)g_idCaptureBatchRazor);
|
||||
if (g_cmdCaptureBatchRazor)
|
||||
{
|
||||
g_descCaptureBatchRazor =
|
||||
channelActionName("batch capture razor areas (one per area)");
|
||||
g_accelCaptureBatchRazor.accel.cmd = g_cmdCaptureBatchRazor;
|
||||
g_accelCaptureBatchRazor.desc = g_descCaptureBatchRazor.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCaptureBatchRazor);
|
||||
}
|
||||
|
||||
// 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. 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 =
|
||||
channelActionName("capture selected track (realtime)");
|
||||
g_accelCaptureTrackRealtime.accel.cmd = g_cmdCaptureTrackRealtime;
|
||||
g_accelCaptureTrackRealtime.desc = g_descCaptureTrackRealtime.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCaptureTrackRealtime);
|
||||
}
|
||||
|
||||
// Cancel-in-flight sibling: aborts a running realtime capture (stop + restore).
|
||||
// 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 = channelActionName("cancel realtime capture");
|
||||
g_accelCancelRealtime.accel.cmd = g_cmdCancelRealtime;
|
||||
g_accelCancelRealtime.desc = g_descCancelRealtime.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelCancelRealtime);
|
||||
}
|
||||
|
||||
// Register the M10 "re-capture from source" action (command_id -> gaccel ->
|
||||
// hookcommand). Regenerates the selected provenanced sample from its recorded
|
||||
// source's current state; bank-only, never places on the timeline. Channel-
|
||||
// qualified FOREVER-STABLE id (suffix RECAPTURE_FROM_SOURCE).
|
||||
g_idRecaptureFromSource = internCmdId("RECAPTURE_FROM_SOURCE");
|
||||
g_cmdRecaptureFromSource = rec->Register("command_id", (void*)g_idRecaptureFromSource);
|
||||
if (g_cmdRecaptureFromSource)
|
||||
{
|
||||
g_descRecaptureFromSource = channelActionName("re-capture from source");
|
||||
g_accelRecaptureFromSource.accel.cmd = g_cmdRecaptureFromSource;
|
||||
g_accelRecaptureFromSource.desc = g_descRecaptureFromSource.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelRecaptureFromSource);
|
||||
}
|
||||
|
||||
// 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. 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 = channelActionName("show version");
|
||||
g_accelShowVersion.accel.cmd = g_cmdShowVersion;
|
||||
g_accelShowVersion.desc = g_descShowVersion.c_str();
|
||||
rec->Register("gaccel", (void*)&g_accelShowVersion);
|
||||
}
|
||||
|
||||
// Register the Design View action family (D4): toggle/activate mode, tag/untag/
|
||||
// show-both selected tracks. Each mints its own command_id + gaccel; the single
|
||||
@@ -632,11 +381,11 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
|
||||
|
||||
// Register the S8 ingest action family: the Media-Explorer import-into-bank+assign
|
||||
// action. Shares g_session with the other families; routed by the same hookcommand via
|
||||
// ingestHandleCommand. (The arrange capture+assign action is registered in the capture
|
||||
// family above; the drop path is a bank_panel callback, not a bindable action.)
|
||||
// ingestHandleCommand. (The arrange capture+assign action is a table row above; the
|
||||
// drop path is a bank_panel callback, not a bindable action.)
|
||||
reasampler::ingestRegisterActions(rec, &g_session);
|
||||
|
||||
// One hookcommand routes every ReaSampler action (spike + toggle + Design View).
|
||||
// One hookcommand routes every ReaSampler action (table + the three families).
|
||||
// Registered once, after all command ids are minted.
|
||||
rec->Register("hookcommand", (void*)&OnHookCommand);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user