Add tail manual fine-adjust (scroll) + per-project tail persistence

Relocate TailSetting into ReaSamplerSession; persist serializes it to
a new forever-stable "tail_setting" ext-state key (load-per-project,
save-on-save). Footer scroll-wheel adjusts Manual length in 250ms
steps; label now shows "Tail: Manual X.Xs". Pure step/label/JSON
round-trip covered by tests.
This commit is contained in:
2026-07-23 19:12:12 -04:00
parent 938c85a223
commit 37affc6a70
6 changed files with 365 additions and 38 deletions
+22
View File
@@ -20,6 +20,7 @@
#include <string>
#include "bank_model.h"
#include "tail_control.h"
#include "view_mode_model.h"
namespace reasampler {
@@ -38,6 +39,13 @@ inline constexpr const char* kProjExtIndexKey = "bank_index";
// FOREVER-STABLE: changing it orphans every already-saved project's view state.
inline constexpr const char* kProjExtViewKey = "view_state";
// The ext-state key the docked panel's TailSetting JSON (mode + manualMs) is stored
// under, so the tail choice travels inside the .rpp and loads per project. Distinct
// from the index/view keys — one namespace, three keys. FOREVER-STABLE: changing it
// orphans every already-saved project's tail setting (which then falls back to the
// default — graceful, but the user's saved choice would be lost).
inline constexpr const char* kProjExtTailKey = "tail_setting";
// The ext-state key holding a GUID we mint per project to establish CONTENT-BASED
// project identity (REAPER exposes no stable per-project GUID). poll() uses it to
// tell a genuine Save-As (same GUID, new .rpp path) apart from a project switch
@@ -80,6 +88,15 @@ public:
ViewModeModel& view() { return view_; }
const ViewModeModel& view() const { return view_; }
// The docked panel's tail setting (mode + manualMs), authoritative here — NOT in
// panel state — so it travels inside the .rpp: persist serializes it on save and
// replaces it on project load exactly as it treats the bank and view model. The
// panel reads/writes it through this seam (bank_panel holds the session), and the
// capture actions read it via bankPanelTailSetting. Default None / 2 s manual for
// an unsaved or pre-feature project (no stored key -> this default survives load).
TailSetting& tail() { return tail_; }
const TailSetting& tail() const { return tail_; }
// Serialize the current bank to the active project's ext state (namespace
// "reasampler"). Non-destructive beyond writing our own ext-state key. Safe
// to call when there is no active/saved project (it no-ops).
@@ -110,6 +127,11 @@ private:
// view_state (older project), so an absent key is graceful, not a crash.
ViewModeModel view_;
// The tail setting. Default None / kDefaultManualTailMs; loadFromProject resets it
// to this default when a project has no stored tail_setting key (older / never-
// adjusted project), so an absent key is graceful. Peer to bank_/view_.
TailSetting tail_;
// The project identity last observed by poll(), used to detect load/Save-As.
// The GUID is the PRIMARY signal (a different stored GUID = a different project
// of record = Load, immune to pointer recycling). The pointer disambiguates the