feat(bank_panel): docked bank grid with LICE waveform thumbnails (M5 Wave A)

Docked SWELL window toggled by a new action, drawing the current bank as
per-sample min/max thumbnails (PCM_source + peaks) with an in-memory cache.
Pure grid-layout/cache-key math in bank_grid (tested). Renames peaks::Sample
-> AudioSample to avoid colliding with bank_model::Sample.
This commit is contained in:
2026-07-22 21:02:22 -04:00
parent ec211a5620
commit 71a3b26a47
12 changed files with 936 additions and 33 deletions
+50 -1
View File
@@ -21,6 +21,7 @@
#include <string>
#include "bank_model.h"
#include "bank_panel.h"
#include "capture.h"
#include "persist.h"
@@ -44,6 +45,11 @@ reaper_plugin_info_t* g_rec = nullptr; // REAPER's dispatch struct
// (user keybindings key off it) — see the prefix note above.
static int g_cmdCaptureMasterSpike = 0;
// 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;
// The persistence session (M4): owns the in-memory BankIndex 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(); after a capture we
@@ -57,6 +63,10 @@ static reasampler::ReaSamplerSession g_session;
static void OnTimer()
{
g_session.poll();
// Reflect a live bank change (capture / project load) in the docked grid.
// Cheap when the bank is unchanged (a fingerprint compare); repaints only on
// an actual change. No-op when the panel is closed.
reasampler::bankPanelRefresh();
}
// Runs the M3 spike: render the time-selection master mix, add the Sample, log.
@@ -107,11 +117,22 @@ static bool OnHookCommand(int command, int /*flag*/)
{
if (command == 0) return false;
if (command == g_cmdCaptureMasterSpike) { RunCaptureMasterSpike(); return true; }
if (command == g_cmdToggleBankPanel) { reasampler::bankPanelToggle(); return true; }
return false;
}
// REAPER polls this to render each of OUR actions' checked state in menus/toolbars.
// 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)
return reasampler::bankPanelIsOpen() ? 1 : 0;
return -1; // not ours / non-toggling
}
// gaccel storage must outlive registration — REAPER holds the pointer.
static gaccel_register_t g_accelCaptureMaster{};
static gaccel_register_t g_accelToggleBankPanel{};
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t* rec)
@@ -123,11 +144,18 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
if (g_rec)
{
g_rec->Register("-timer", (void*)&OnTimer);
g_rec->Register("-toggleaction", (void*)&OnToggleAction);
g_rec->Register("-hookcommand", (void*)&OnHookCommand);
g_rec->Register("-gaccel", (void*)&g_accelToggleBankPanel);
g_rec->Register("-command_id",
(void*)(REASAMPLER_ACTION_PREFIX "TOGGLE_BANK_PANEL"));
g_rec->Register("-gaccel", (void*)&g_accelCaptureMaster);
g_rec->Register("-command_id",
(void*)(REASAMPLER_ACTION_PREFIX "CAPTURE_MASTER_SPIKE"));
}
// Destroy the docked window and release cached thumbnails before we drop
// the API pointers (DockWindowRemove/DestroyWindow need them live).
reasampler::bankPanelShutdown();
g_rec = nullptr;
return 0;
}
@@ -153,9 +181,30 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
g_accelCaptureMaster.accel.cmd = g_cmdCaptureMasterSpike;
g_accelCaptureMaster.desc = "ReaSampler: capture master mix (spike)";
rec->Register("gaccel", (void*)&g_accelCaptureMaster);
rec->Register("hookcommand", (void*)&OnHookCommand);
}
// 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).
g_cmdToggleBankPanel = rec->Register(
"command_id",
(void*)(REASAMPLER_ACTION_PREFIX "TOGGLE_BANK_PANEL"));
if (g_cmdToggleBankPanel)
{
g_accelToggleBankPanel.accel.cmd = g_cmdToggleBankPanel;
g_accelToggleBankPanel.desc = "ReaSampler: toggle bank panel";
rec->Register("gaccel", (void*)&g_accelToggleBankPanel);
rec->Register("toggleaction", (void*)&OnToggleAction);
}
// One hookcommand routes every ReaSampler action (spike + toggle). Registered
// once, after both command ids are minted.
rec->Register("hookcommand", (void*)&OnHookCommand);
// Drive project-load / Save-As detection (M4 persist). The timer polls the
// active project each tick; on a project load it reloads the bank from ext
// state, on a Save-As it relocates the bank folder under the new .rpp.