#pragma once // lane_keys — the pure, REAPER-free convention that maps a REAPER fixed lane's // durable NAME (P_LANENAME:n) to the opaque lane-key the pure view_mode_model uses, // and the managed/manual heuristic that rides on it. // // PURE MODULE (CLAUDE.md §load-bearing split): NO REAPER types, NO SWELL. std only. // Unit-tested outside the DAW. The shell (view.cpp) reads each lane's P_LANENAME:n // string from REAPER and asks this module whether the lane is tool-managed and what // its stable lane-key is; the shell never re-derives the prefix rule itself. // // -- Design point #2 (lane-identity robustness) resolution -------------------- // // REAPER exposes no durable per-lane GUID. The only lane identity is the ordinal // I_FIXEDLANE, which REAPER RENUMBERS when lanes are reordered or deleted — so keying // the ownership index by raw ordinal would silently corrupt managed/manual ownership // on any reorder. REAPER DOES expose a writable, durable lane NAME (P_LANENAME:n) that // travels with the lane across renumber. So the tool names each lane it mints with a // stable, prefixed identity ("reasampler:") and keys the ownership index by that // NAME, not the ordinal. On each apply the shell walks the track's lanes by current // ordinal, reads each name, and reconciles ordinal<->laneKey — so a C_LANEPLAYS:N // write always targets the lane's CURRENT ordinal for a given durable key even after a // reorder. A lane WITHOUT the prefix was not minted by the tool: it is manual and // off-limits (the fixed-lane analog of "never touch mute/solo"). // // -- Design point #1 (manual-lane exemption) resolution ----------------------- // // The SAME prefix rule is the manual/managed heuristic for auto-tag: an item on a lane // whose name lacks the "reasampler:" prefix is on a manual lane and is EXEMPT from // auto-tag. isManagedLaneName is the single predicate both the toggle-apply path and // the new-content detection path consult, so the boundary is defined in one place and // unit-tested. #include #include namespace reasampler { // The prefix the tool stamps on every lane NAME it mints. A lane name carrying this // prefix is a managed lane the tool created; any other name (or an empty/unnamed lane) // is a user-minted manual lane. Stable-forever: changing it would strand the ownership // of every lane in every already-saved project, so treat it like an action id string. inline constexpr const char* kManagedLanePrefix = "reasampler:"; // True iff `laneName` is a tool-minted managed-lane name (carries kManagedLanePrefix). // This is the load-bearing managed/manual predicate for BOTH design points #1 and #2. bool isManagedLaneName(const std::string& laneName); // The opaque lane-key the pure model keys by, for a lane with REAPER name `laneName`. // For a managed lane the key IS the durable name (stable across ordinal renumber). For // a manual/unnamed lane there is no managed key: returns std::nullopt so the caller // treats the lane as manual (never driven, items on it exempt from auto-tag). std::optional managedLaneKey(const std::string& laneName); // The lane NAME the tool mints for the lane owned by `modeId` (kManagedLanePrefix + // modeId). The inverse of managedLaneKey for a managed lane: managedLaneKey( // laneNameForMode(m)) == kManagedLanePrefix + m. Exposed for the Wave-3 lane-minting // path and for tests; the apply path in this wave only READS names, but the round-trip // contract is asserted here so minting and reading cannot drift. std::string laneNameForMode(const std::string& modeId); // The owning mode id encoded in a managed lane NAME — the suffix after the managed // prefix. std::nullopt for a manual/unnamed lane (no managed prefix) or a name that is // EXACTLY the prefix with no mode suffix (illegal — a managed lane always names a mode). // The exact inverse of laneNameForMode: modeIdFromLaneName(laneNameForMode(m)) == m. // Used by the load-time reconcile to recover managed ownership from REAPER's durable // lane name (the source of truth for identity across sessions — design point #2). std::optional modeIdFromLaneName(const std::string& laneName); // True iff an item on a fixed-lane track with the given lane name is on a MANUAL lane // (i.e. exempt from auto-tag). The two inputs are: // isFixedLaneTrack — whether the item's track has I_FREEMODE==2. On a normal // (non-fixed-lane) track the concept of a "manual lane" does not // apply; the item follows the normal auto-tag rule (return false). // laneName — the durable P_LANENAME of the lane the item sits on. A lane // that carries kManagedLanePrefix is a tool-minted managed lane // (not manual); any other name — including empty (unnamed) — is // a user-minted manual lane (exempt from auto-tag). // // This is the SINGLE predicate that governs BOTH the apply path (which lanes may be // driven) and the auto-tag exemption path (which items are exempt). It is unit-tested // here so both paths share exactly one definition; the shell supplies the two REAPER // inputs (I_FREEMODE result, P_LANENAME string) and never re-derives this logic. bool isOnManualLane(bool isFixedLaneTrack, const std::string& laneName); } // namespace reasampler