docs: settle Design View two-canvas sub-phase (fixed-lane per-item modes)
Item-level mode separation via REAPER fixed lanes; new content auto-tags to the active mode; inactive lanes hidden and silenced; a managed-vs-manual lane index keeps toggles off user comp lanes; capture placement mode-aware. Precision invariants unaffected; REAPER 7 floor.
This commit is contained in:
+164
-5
@@ -184,9 +184,27 @@ are **Arrange** mode (the default). Toggling to a mode **hides and disables** th
|
|||||||
tracks that don't belong to it. Workflow value: mental separation + clutter
|
tracks that don't belong to it. Workflow value: mental separation + clutter
|
||||||
elimination — be in Design view, resample into the bank, flip to Arrange, place it.
|
elimination — be in Design view, resample into the bank, flip to Arrange, place it.
|
||||||
|
|
||||||
**This is not a separate canvas.** REAPER has exactly one arrange timeline. Design
|
Design View is a **mode projection over REAPER's single arrange timeline** —
|
||||||
View is the *same timeline* with a curated, filtered track set and the inactive
|
reaching both **tracks** (parked per mode) and **items** (lane-split per mode on
|
||||||
tracks parked — not a second surface, window, or duplicated project.
|
shared tracks). It approximates two canvases without a second surface: same project,
|
||||||
|
same timeline, but each stance sees its own tracks and its own items. It **never**
|
||||||
|
duplicates the project or opens a second window.
|
||||||
|
|
||||||
|
> **Two-canvas reach — settled (2026-07-23).** The original single-canvas framing
|
||||||
|
> here was derived from a REAPER constraint, not chosen as a product stance. Daniel
|
||||||
|
> reopened it — "as close as possible to two separate design and arrange canvases,
|
||||||
|
> same project, different items and leaves" — and has now signed off on the
|
||||||
|
> mechanism and resolved all forks. The "different leaves" half is delivered by
|
||||||
|
> track-parking (D1). The "different items" half is delivered by **per-item mode
|
||||||
|
> membership via REAPER fixed lanes** (track-side `I_FREEMODE=2`, `I_NUMFIXEDLANES`,
|
||||||
|
> `C_LANEPLAYS:N`; item-side `I_FIXEDLANE`, `C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN`):
|
||||||
|
> each mode owns a lane on a shared track, and toggling shows/plays only the active
|
||||||
|
> mode's lane. This is an item-visibility projection over the one timeline — the
|
||||||
|
> exact analog of today's track-visibility projection — with still **no** literal
|
||||||
|
> second surface, window, or duplicate project. It is an **additive sub-phase (Phase
|
||||||
|
> D2 / Phase E)** on top of D1, spec'd in *§Two-canvas sub-phase* below. Full framing
|
||||||
|
> and the rejected alternatives (timebase-offset, subproject) live in
|
||||||
|
> `docs/product/design-view.md` §Two-canvas direction.
|
||||||
|
|
||||||
It is the visibility/processing analog of the capture pillar's load-bearing rule:
|
It is the visibility/processing analog of the capture pillar's load-bearing rule:
|
||||||
**designing and arranging are separate stances on one timeline**, and the tool
|
**designing and arranging are separate stances on one timeline**, and the tool
|
||||||
@@ -297,10 +315,21 @@ togglable per selection.
|
|||||||
|
|
||||||
## Non-goals / guardrails
|
## Non-goals / guardrails
|
||||||
|
|
||||||
- **No second canvas.** Do not build a parallel arrange surface — reject any such
|
- **No *literal* second canvas.** A literal second arrange surface, a second window,
|
||||||
path in review.
|
or a duplicated project stays **rejected** — reject any such path in review. This
|
||||||
|
guardrail was **narrowed (2026-07-23), not lifted**: per-item mode separation via
|
||||||
|
REAPER fixed lanes on shared tracks (an item-visibility projection over the one
|
||||||
|
timeline) is now **allowed** and specified in *§Two-canvas sub-phase* below. Paths
|
||||||
|
that remain **rejected**: subproject / second-project-file approaches, and
|
||||||
|
overloading item `D_POSITION` with mode semantics (timebase-offset regions) — the
|
||||||
|
latter collides with the capture null test. See `docs/product/design-view.md`
|
||||||
|
§Two-canvas direction for why those were rejected.
|
||||||
- **Never touch mute/solo.** Any code path reading/writing `B_MUTE` / `I_SOLO` is a
|
- **Never touch mute/solo.** Any code path reading/writing `B_MUTE` / `I_SOLO` is a
|
||||||
bug.
|
bug.
|
||||||
|
- **Never drive a manual lane** (Phase D2). A mode toggle touches only **managed**
|
||||||
|
lanes (minted by the mode system, keyed in the lane-ownership index). Any code path
|
||||||
|
that shows, hides, silences, or re-lanes a **manual** lane — a user-created comp/take
|
||||||
|
lane outside the mode system — is a bug. The tool drives only lanes it created.
|
||||||
- **Every leaf is managed.** The mode system owns all leaf tracks: an untagged leaf
|
- **Every leaf is managed.** The mode system owns all leaf tracks: an untagged leaf
|
||||||
is an Arrange member, and when the active mode is not Arrange it is fully parked
|
is an Arrange member, and when the active mode is not Arrange it is fully parked
|
||||||
and snapshot-restored exactly like a tagged leaf out of its mode. **show-both** is
|
and snapshot-restored exactly like a tagged leaf out of its mode. **show-both** is
|
||||||
@@ -325,6 +354,136 @@ togglable per selection.
|
|||||||
- Interaction with the user having a screenset active (Design View drives the same
|
- Interaction with the user having a screenset active (Design View drives the same
|
||||||
flags a screenset recall would; last writer wins — confirm no surprising fight).
|
flags a screenset recall would; last writer wins — confirm no surprising fight).
|
||||||
|
|
||||||
|
## Two-canvas sub-phase (Phase D2 / Phase E) — additive to D1
|
||||||
|
|
||||||
|
> **Additive sub-phase, settled 2026-07-23.** Extends D1's track-level mode
|
||||||
|
> projection to **item level** so each stance owns its own items as well as its own
|
||||||
|
> tracks. Nothing in D1 changes; this wraps it. Runtime floor rises to **REAPER 7**
|
||||||
|
> for this sub-phase (fixed lanes shipped in v7). Product framing in
|
||||||
|
> `docs/product/design-view.md` §Two-canvas direction.
|
||||||
|
|
||||||
|
### What it adds
|
||||||
|
|
||||||
|
On a track present in **both** stances (a show-both track, or a folder carrying its
|
||||||
|
own media), each mode owns a **fixed lane**: the active mode's lane shows and plays;
|
||||||
|
the inactive mode's lane is hidden and silent. A Design take and an Arrange take can
|
||||||
|
then live on the *same track, same time position*, without colliding on the view.
|
||||||
|
Track-only-in-one-mode content is still handled by D1 track-parking, unchanged.
|
||||||
|
|
||||||
|
### Settled decisions
|
||||||
|
|
||||||
|
- **Mechanism: fixed item lanes.** Map mode → lane; toggle drives per-lane play/show
|
||||||
|
so only the active mode's lane is present. Items keep their real position and real
|
||||||
|
track — nothing is moved in time or deleted. SDK surface (**verified present in
|
||||||
|
`vendor/reaper-sdk`**): track-side `I_FREEMODE = 2`, `I_NUMFIXEDLANES`,
|
||||||
|
`C_LANEPLAYS:N`; item-side `I_FIXEDLANE`, `C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN`.
|
||||||
|
**`I_FREEMODE` changes require `UpdateTimeline()`** to take visible effect.
|
||||||
|
- **Membership: auto-tag by active mode at creation.** New content — **both new
|
||||||
|
tracks and new items** — is tagged to whatever mode is active when it is created.
|
||||||
|
Pre-existing content defaults to **Arrange**. Membership is **exclusive**:
|
||||||
|
Design-created content never appears in Arrange and vice versa, except via the
|
||||||
|
existing **show-both** escape hatch. (This is the same tag-to-active-mode rule as
|
||||||
|
D1 track membership, now reaching items on shared tracks.)
|
||||||
|
- **Inactive-mode content is hidden AND silenced.** The off-mode lane is set
|
||||||
|
`C_LANEPLAYS = 0` — neither shown nor played — consistent with exclusive
|
||||||
|
membership and with D1's "flipping modes is a real change, not cosmetic." Show-both
|
||||||
|
is the deliberate opt-out for a lane that must stay audible across modes.
|
||||||
|
- **Managed vs. manual lanes — indexed and distinct.** Fixed lanes are also REAPER's
|
||||||
|
native comping surface: a user may keep **their own** manual lanes (comp takes,
|
||||||
|
alternate reads) on a track alongside the mode system's lanes. The tool maintains a
|
||||||
|
**lane-ownership index** — per (track GUID, lane): **managed** (which mode owns it)
|
||||||
|
vs **manual** (user-minted, outside the mode system). Mode operations touch **only
|
||||||
|
managed lanes**; manual lanes are never shown, hidden, silenced, or re-laned by a
|
||||||
|
toggle, and their `C_LANEPLAYS` stays exactly as the user set it. Items a user adds
|
||||||
|
to a **manual** lane are **not** auto-tagged (auto-tag governs normal timeline
|
||||||
|
content, not hand-managed lanes). The ownership index rides in `"reasampler"`
|
||||||
|
`view_state` alongside the membership index, GUID-keyed and portable.
|
||||||
|
- **Capture placement is mode-aware.** An **explicit** placement while in Design mode
|
||||||
|
— including capture-and-place — lands the item in the **Design lane**; the same
|
||||||
|
rule governs manual insertion. **The capture load-bearing principle is untouched:**
|
||||||
|
capture still writes a file + index entry and **never** auto-inserts; this governs
|
||||||
|
only *where an explicit placement lands*.
|
||||||
|
- **REAPER floor: v7.** No version-gate branch — below v7 this sub-phase is simply
|
||||||
|
unavailable.
|
||||||
|
|
||||||
|
### Precision invariants (unaffected — called out explicitly)
|
||||||
|
|
||||||
|
The capture precision invariants — **null test, bit-identical repeats,
|
||||||
|
non-destructive, exact bounds, relative-paths-only** — are **entirely unaffected** by
|
||||||
|
this sub-phase. No capture path changes; lanes are a placement/view concern
|
||||||
|
downstream of the written file. Lane assignment and `C_LANEPLAYS` are **reversible
|
||||||
|
flags**: the item is never relocated in time or deleted, so the sub-phase extends the
|
||||||
|
D1 non-destructive snapshot/restore contract to a new (item-lane) flag family rather
|
||||||
|
than introducing any destructive operation.
|
||||||
|
|
||||||
|
**New invariant — mode operations touch only managed lanes.** A mode toggle drives
|
||||||
|
only lanes the mode system minted (managed lanes, keyed in the lane-ownership index).
|
||||||
|
Manual lanes — user-created comp/take lanes outside the mode system — are **never**
|
||||||
|
shown, hidden, silenced, or re-laned by a toggle; their `C_LANEPLAYS` is left exactly
|
||||||
|
as the user set it. This is the fixed-lane analog of *never touch `B_MUTE`/`I_SOLO`*
|
||||||
|
and *never touch master*: **the tool drives only what it created.** Enforceable and
|
||||||
|
testable in the pure layer — the "which lanes may this toggle touch" decision is a
|
||||||
|
pure query over the ownership index; only reading REAPER's live lane state is shell.
|
||||||
|
|
||||||
|
### Module architecture (preserve the pure/shell split)
|
||||||
|
|
||||||
|
- **Pure (`view_mode_model` extension).** Lane math — which lane maps to which mode,
|
||||||
|
which `C_LANEPLAYS` value per mode, the item-lane op family alongside the existing
|
||||||
|
track-flag op family — is **REAPER-free and unit-tested**, mirroring the D1
|
||||||
|
planner. The **lane-ownership index** (per (track GUID, lane): managed-which-mode vs
|
||||||
|
manual) and the **"which lanes may this toggle touch" query** (managed only) are
|
||||||
|
pure and unit-tested — the planner emits lane ops for managed lanes only and never
|
||||||
|
for manual lanes. The **auto-tag decision** is pure too: given *a set of new
|
||||||
|
track/item GUIDs + the active mode* — and, for items, whether the item landed in a
|
||||||
|
manual lane (exempt) — produce the membership writes.
|
||||||
|
- **Shell.** Two shell responsibilities. (1) **Apply** the planner's item-lane ops
|
||||||
|
(`I_FREEMODE`/`I_FIXEDLANE`/`C_LANEPLAYS`/`B_FIXEDLANE_HIDDEN` via the media-item
|
||||||
|
info setters, `UpdateTimeline()` after `I_FREEMODE` changes) — for managed lanes
|
||||||
|
only. (2) **Detect** new content and read live lane state — see below.
|
||||||
|
- **Persistence.** The tool persists which lane maps to which mode **and the
|
||||||
|
managed/manual ownership index** (a small addition to the `"reasampler"` view
|
||||||
|
section); REAPER stores fixed lanes and lane-plays in the `.rpp` natively.
|
||||||
|
|
||||||
|
### New-content detection (implementation design point)
|
||||||
|
|
||||||
|
REAPER exposes **no clean "item added" / "track added" event callback.** Auto-tagging
|
||||||
|
therefore requires the shell to **diff project state on the panel's existing timer** —
|
||||||
|
the `bank_panel` already polls and fingerprints the bank; this extends that machinery
|
||||||
|
to the timeline's tracks and items.
|
||||||
|
|
||||||
|
- Each poll, compare the live track/item GUID set against the previous poll's set;
|
||||||
|
**any GUID new since the last poll is tagged to the then-active mode.**
|
||||||
|
- Correctness the implementation must handle: the **first poll after project open must
|
||||||
|
not mass-tag** pre-existing content (pre-existing defaults to Arrange, per the
|
||||||
|
membership rule).
|
||||||
|
- **Manual-lane exemption (design point).** An item added to a **manual** lane is
|
||||||
|
**not** auto-tagged — auto-tag governs normal timeline content, not lanes the user
|
||||||
|
hand-manages. Distinguishing the two may need a heuristic at detection (e.g., an item
|
||||||
|
whose `I_FIXEDLANE` is marked manual in the ownership index is exempt; content
|
||||||
|
outside any managed lane on a mode-managed track follows the active-mode rule). The
|
||||||
|
precise rule is an **open implementation design point**; the settled boundary is that
|
||||||
|
manual-lane content is off-limits to auto-tag.
|
||||||
|
- **Lane-identity fragility (design point).** `I_FIXEDLANE` is the lane's identity and
|
||||||
|
is how the ownership index keys to a lane. Whether the index survives lane
|
||||||
|
reorder/renumber/deletion without going stale is an **implementation design point**
|
||||||
|
(same class as GUID-keyed reorder-safety for tracks) — flag, don't solve here.
|
||||||
|
- Pure/shell seam: the **detection** (diffing REAPER's live set each tick, reading live
|
||||||
|
lane ownership) is shell; the **tagging decision** and the **managed/manual lane
|
||||||
|
query** (new GUIDs + active mode + manual-lane exemption ⇒ membership + lane writes)
|
||||||
|
are pure and unit-tested.
|
||||||
|
|
||||||
|
### REAPER API surface (verify all signatures)
|
||||||
|
|
||||||
|
- Fixed lanes — track: `I_FREEMODE` (=2), `I_NUMFIXEDLANES`, `C_LANEPLAYS:N` via
|
||||||
|
`GetMediaTrackInfo_Value`/`SetMediaTrackInfo_Value`; item: `I_FIXEDLANE`,
|
||||||
|
`C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN` via `GetMediaItemInfo_Value` /
|
||||||
|
`SetMediaItemInfo_Value`. Call `UpdateTimeline()` after `I_FREEMODE` changes.
|
||||||
|
- Detection reuses the `bank_panel` timer + GUID fingerprinting already in place;
|
||||||
|
item GUIDs via the item's `GUID` (`GetSetMediaItemInfo_String` `"GUID"`), track
|
||||||
|
GUIDs via `GetTrackGUID` as in D1.
|
||||||
|
- **Verify every name/signature against the SDK header before use** — the surface is
|
||||||
|
verified *present*, but confirm argument order, types, and flag values.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Multi-bank — additive phase spec
|
# Multi-bank — additive phase spec
|
||||||
|
|||||||
+340
-8
@@ -6,8 +6,10 @@ detail in `CONTEXT.md` (§Design View). This doc holds the *why* — the workflo
|
|||||||
narrative, the N-mode reasoning, the screenset differentiation, and the
|
narrative, the N-mode reasoning, the screenset differentiation, and the
|
||||||
design-direction recommendations — so those don't clutter the build docs.
|
design-direction recommendations — so those don't clutter the build docs.
|
||||||
|
|
||||||
Status: settled with Daniel (2026-07-22). Open items for Daniel are listed at the
|
Status: settled with Daniel (2026-07-22). The **two-canvas direction** (item-level
|
||||||
bottom.
|
mode separation via fixed lanes) is also settled with Daniel (2026-07-23) — see the
|
||||||
|
§Two-canvas direction section, which **supersedes** the "not a second canvas"
|
||||||
|
framing below. Open items for Daniel are listed above that section.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -25,12 +27,19 @@ place it. The design scaffolding never touches the arrangement while you're
|
|||||||
arranging, and the arrangement never buries the design bench while you're
|
arranging, and the arrangement never buries the design bench while you're
|
||||||
designing.
|
designing.
|
||||||
|
|
||||||
**It is not a second canvas.** REAPER has exactly one arrange timeline. Design
|
**It is not a *literal* second canvas.** REAPER has exactly one arrange timeline.
|
||||||
View does not create a parallel surface, a separate window, or a duplicate
|
Design View does not create a parallel surface, a separate window, or a duplicate
|
||||||
project. It is the *same timeline* with a curated, filtered track set and the
|
project — that stays true and is enforced in review. It is the *same timeline* with
|
||||||
inactive tracks parked. Anyone expecting a second arrange view will be
|
a curated, filtered track set and the inactive content parked.
|
||||||
disappointed — and that expectation must be headed off in the spec, not
|
|
||||||
discovered in review.
|
> **Superseded in one respect (2026-07-23).** The original claim here was that
|
||||||
|
> Design View separates only *tracks*, not *items*. That is now extended: the
|
||||||
|
> settled §Two-canvas direction adds **item-level** mode separation on shared tracks
|
||||||
|
> via REAPER fixed lanes — so each stance can own its own items as well as its own
|
||||||
|
> tracks. It is still not a literal second surface/window/project; it is a mode
|
||||||
|
> *projection* over the one timeline that now reaches items. Read §Two-canvas
|
||||||
|
> direction for the settled shape; the sentence "It does not separate items" no
|
||||||
|
> longer holds.
|
||||||
|
|
||||||
This is the visibility/processing analog of the capture pillar's load-bearing
|
This is the visibility/processing analog of the capture pillar's load-bearing
|
||||||
rule. The capture pillar says *capture and placement are separate acts*. Design
|
rule. The capture pillar says *capture and placement are separate acts*. Design
|
||||||
@@ -345,3 +354,326 @@ tooltip on the switch.
|
|||||||
deleted or a folder is restructured while parked, the index holds a stale GUID.
|
deleted or a folder is restructured while parked, the index holds a stale GUID.
|
||||||
The pure model should tolerate unknown GUIDs (ignore-and-prune on next reconcile)
|
The pure model should tolerate unknown GUIDs (ignore-and-prune on next reconcile)
|
||||||
— noted as a hardening item for the build, not a phase-defining decision.
|
— noted as a hardening item for the build, not a phase-defining decision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Two-canvas direction — settled (2026-07-23)
|
||||||
|
|
||||||
|
> **Status: settled with Daniel (2026-07-23).** This section reopens and supersedes
|
||||||
|
> the "not a second canvas" decision above. Daniel's stated intent (verbatim): *"I
|
||||||
|
> wasn't the one who specified single-canvas, that emerged from REAPER's design. I
|
||||||
|
> want to come as close as possible to supporting two separate design and arrange
|
||||||
|
> canvases, which are actually the same project but have different items and
|
||||||
|
> leaves."* The single-canvas framing above was a **derivation from a REAPER
|
||||||
|
> constraint**, not a product choice — so it was reopened, and Daniel has now signed
|
||||||
|
> off on the mechanism (fixed item lanes) and resolved all five forks. Everything
|
||||||
|
> under this heading **supersedes** the "not a second canvas" language in *What it is
|
||||||
|
> (and what it is not)*: Design View is now a mode projection that reaches **both
|
||||||
|
> tracks and items**. This is an **additive sub-phase** (Phase D2 / Phase E) on top
|
||||||
|
> of the shipped track-parking work — not a rewrite of it.
|
||||||
|
|
||||||
|
## What actually changed in the ask
|
||||||
|
|
||||||
|
The shipped model separates modes by **track membership** on one shared timeline:
|
||||||
|
tag a leaf into Design, and when you flip to Arrange that leaf is parked — hidden,
|
||||||
|
out of the mix, FX offline. Because items ride their tracks, a Design track's items
|
||||||
|
already vanish from the Arrange view. **The "different leaves" half of Daniel's ask
|
||||||
|
is already delivered.** Flipping modes already gives you a different *track set*.
|
||||||
|
|
||||||
|
The genuinely new half is **"different items."** Daniel wants two canvases that
|
||||||
|
differ not only in which tracks are present but in which *items* are present —
|
||||||
|
including items that live on a track shared by both stances. Concretely: a track
|
||||||
|
that exists in both Arrange and Design (a show-both reference track, or a folder
|
||||||
|
carrying its own media) should be able to hold a design take that only appears in
|
||||||
|
Design and an arrangement take that only appears in Arrange, on the *same track*,
|
||||||
|
without the two colliding on the timeline. Track-parking can't express that,
|
||||||
|
because parking is all-or-nothing per track — it can hide a whole track, never a
|
||||||
|
subset of its items.
|
||||||
|
|
||||||
|
So the reframe is precise: **the feature grows from track-level mode separation to
|
||||||
|
track-level *plus* item-level mode separation.** "Two canvases" = each stance owns
|
||||||
|
its own item set, layered over the shared project, with tracks it doesn't want
|
||||||
|
parked as today.
|
||||||
|
|
||||||
|
## The hard constraint (and why it's not fatal)
|
||||||
|
|
||||||
|
REAPER has exactly one arrange timeline and one project item graph. There is no
|
||||||
|
API to instantiate a second arrange surface, and the "no second window/no
|
||||||
|
duplicate project" observation in the shipped framing is *structurally true* — you
|
||||||
|
cannot conjure a literal second canvas. What is reversed is only the **conclusion
|
||||||
|
drawn from it**: that item separation is therefore off the table. It isn't. Items
|
||||||
|
carry per-item state (`I_FIXEDLANE`, `C_LANEPLAYS`, `D_POSITION`, track
|
||||||
|
assignment) that the tool can drive to make an item present-or-absent *per mode*
|
||||||
|
without destroying it. "Two canvases" becomes an **item-visibility projection over
|
||||||
|
one timeline**, exactly as "two track sets" is a track-visibility projection over
|
||||||
|
one timeline today. Same discipline, one level deeper.
|
||||||
|
|
||||||
|
## Mechanism (settled) and the alternatives it beat
|
||||||
|
|
||||||
|
**The settled mechanism is Option A — fixed lanes as per-mode item layers.** The two
|
||||||
|
alternatives (B timebase-offset, C subproject) are kept below because *why they were
|
||||||
|
rejected* is load-bearing: both collide with settled invariants, and recording that
|
||||||
|
keeps them from being re-proposed.
|
||||||
|
|
||||||
|
### Option A — Fixed lanes as per-mode item layers (SETTLED)
|
||||||
|
|
||||||
|
REAPER's **fixed-lane** feature lets a single track hold multiple horizontal lanes
|
||||||
|
of items sharing one timebase, with **per-lane play/visibility control**. Map each
|
||||||
|
mode to a lane: Arrange items live in the Arrange lane, Design items in the Design
|
||||||
|
lane. Toggling a mode sets `C_LANEPLAYS` so only the active mode's lane plays and
|
||||||
|
shows; the collapse/hidden flags hide the inactive lane. Items keep their real
|
||||||
|
positions and their real track — nothing is moved off the timeline, nothing is
|
||||||
|
deleted.
|
||||||
|
|
||||||
|
**SDK surface (verified present in `vendor/reaper-sdk`).** Track-side:
|
||||||
|
`I_FREEMODE = 2` (fixed-lane mode), `I_NUMFIXEDLANES`, per-lane `C_LANEPLAYS:N`.
|
||||||
|
Item-side: `I_FIXEDLANE`, `C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN`. Two operational
|
||||||
|
notes for the implementation spec: **`I_FREEMODE` changes require
|
||||||
|
`UpdateTimeline()`** to take visible effect, and fixed lanes shipped in **REAPER 7**,
|
||||||
|
which sets the runtime floor (see fork 5).
|
||||||
|
|
||||||
|
- **Fidelity to the two-canvas feel:** high. Same track, same time position, but the
|
||||||
|
design take and the arrange take are genuinely distinct item sets that never
|
||||||
|
co-occupy the view. This is exactly "same project, different items."
|
||||||
|
- **Non-destructiveness:** strong. Lane assignment and `C_LANEPLAYS` are
|
||||||
|
reversible flags; the item is never relocated in time or deleted. Snapshot the
|
||||||
|
prior lane state, restore on toggle — a direct extension of the existing
|
||||||
|
snapshot/restore contract to a new flag family.
|
||||||
|
- **Fit with park/restore:** clean. It's the same architecture — the planner gains
|
||||||
|
an item-lane op family alongside the track-flag op family. Pure model stays pure:
|
||||||
|
lane math (which lane = which mode, which `C_LANEPLAYS` value per mode) is
|
||||||
|
REAPER-free and unit-testable, mirroring the existing flag planner.
|
||||||
|
- **Capture-pillar precision:** untouched. Capture writes files to the bank and
|
||||||
|
never inserts into the timeline; lanes are a placement/view concern downstream of
|
||||||
|
capture. The null test and bit-identical invariants are unaffected because no
|
||||||
|
capture path changes.
|
||||||
|
- **Portability/persistence:** native. Fixed lanes and lane-plays are stored in the
|
||||||
|
`.rpp` by REAPER itself; the tool only persists which lane maps to which mode
|
||||||
|
(a small addition to the `"reasampler"` view section).
|
||||||
|
- **Cost / caveats:** fixed lanes are a real REAPER mode on a track — enabling
|
||||||
|
`I_FREEMODE=2` changes how that track behaves for the user (lane buttons, comp
|
||||||
|
behavior), and users unfamiliar with fixed lanes see new UI on managed tracks.
|
||||||
|
Under the settled **auto-tag** membership rule (see *Membership — settled* below),
|
||||||
|
a track becomes lane-split when it first needs to hold content for a second mode,
|
||||||
|
not by a manual "make this a two-canvas track" opt-in. Requires **REAPER 7**, where
|
||||||
|
fixed lanes shipped — this sets the runtime floor.
|
||||||
|
|
||||||
|
### Option B — Timebase-region offset (each mode owns a stretch of the timeline)
|
||||||
|
|
||||||
|
Give each mode a disjoint region of the single timeline — Arrange from 0, Design
|
||||||
|
starting at some large offset (say bar 1000). Items are authored in their mode's
|
||||||
|
region; toggling a mode scrolls/zooms the arrange view to that region
|
||||||
|
(`GetSet_ArrangeView2`) and optionally sets the loop/time selection there.
|
||||||
|
Different items appear because you're *looking at a different part of the timeline*.
|
||||||
|
|
||||||
|
- **Fidelity:** medium. It reads as two workspaces if the scroll is snappy, but
|
||||||
|
they share tracks vertically — a Design track is still a lane on the same track
|
||||||
|
strip, just at a different horizontal offset. It doesn't give per-track item
|
||||||
|
layering; a shared track shows both stances' items if you scroll between them.
|
||||||
|
- **Non-destructiveness:** high (items just have positions), but **positions become
|
||||||
|
semantically loaded** — an item's `D_POSITION` now encodes its mode. That
|
||||||
|
collides with the capture pillar's null test, which re-inserts a capture *at its
|
||||||
|
source position* to verify it nulls. If source positions live in the Design
|
||||||
|
region, the null-test placement logic has to be region-aware. Friction with a
|
||||||
|
load-bearing invariant.
|
||||||
|
- **Fit with park/restore:** awkward. Parking is per-track; this is per-region.
|
||||||
|
Two orthogonal separation axes that don't compose cleanly.
|
||||||
|
- **Portability:** fine (just positions + a stored offset), but the offset is a
|
||||||
|
magic constant that leaks into every position calculation.
|
||||||
|
- **Verdict:** clever, but it overloads timeline position with mode semantics and
|
||||||
|
rubs against the capture invariants. Not recommended.
|
||||||
|
|
||||||
|
### Option C — Dual sub-projects / project tabs (true separation, heavy)
|
||||||
|
|
||||||
|
REAPER supports project tabs and subprojects (a project rendered as an item in a
|
||||||
|
parent). Model Design as a genuinely separate project surface, imported/rendered
|
||||||
|
back into Arrange. This is the *only* option that yields a literally separate
|
||||||
|
canvas.
|
||||||
|
|
||||||
|
- **Fidelity:** highest — it *is* two canvases.
|
||||||
|
- **Cost:** enormous, and it fights the whole premise. "Same project" is Daniel's
|
||||||
|
explicit constraint; a subproject is a *different* project file with its own
|
||||||
|
item graph, its own render boundary, and a copy/sync problem between the two. The
|
||||||
|
capture pillar's "one bank per project, GUID-keyed project identity, travels with
|
||||||
|
the .rpp" assumption breaks — which project owns the bank? Persistence,
|
||||||
|
reconcile, and the show-both/derivation model all have to be rethought across a
|
||||||
|
project boundary. This is a different product.
|
||||||
|
- **Verdict:** rejected. It satisfies "two canvases" literally by violating "same
|
||||||
|
project." Daniel asked for *close as possible* within one project, not a second
|
||||||
|
project.
|
||||||
|
|
||||||
|
## The settled shape
|
||||||
|
|
||||||
|
**Option A — fixed lanes as per-mode item layers — layered on top of the shipped
|
||||||
|
track-parking model.** The shipped model already delivers "different leaves"; this
|
||||||
|
adds "different items" using a native REAPER mechanism that (a) preserves every
|
||||||
|
precision invariant, (b) extends the existing snapshot/restore architecture rather
|
||||||
|
than replacing it, (c) keeps the pure/shell split intact (lane math is pure, lane
|
||||||
|
writes are shell), and (d) persists natively. It is the transplant-a-known-pattern
|
||||||
|
move: REAPER already solved "multiple item sets, one track, one timebase,
|
||||||
|
switchable visibility" for comping — we repurpose comping lanes as mode lanes.
|
||||||
|
|
||||||
|
The mental model: **modes are a projection, and now the projection reaches items as
|
||||||
|
well as tracks.** A track can be Arrange-only (parked in Design as today),
|
||||||
|
Design-only (parked in Arrange as today), or **shared-and-lane-split** (present in
|
||||||
|
both, but showing each mode its own lane of items). The third case is the new
|
||||||
|
capability and the concrete meaning of "two canvases."
|
||||||
|
|
||||||
|
Phase namespace: **Phase D2 / Phase E** (per the lettered-phase convention for
|
||||||
|
parallel features) — additive to the shipped D1 track-mode work, not a rewrite.
|
||||||
|
|
||||||
|
## Membership — settled (auto-tag by active mode at creation)
|
||||||
|
|
||||||
|
The earlier draft floated a manual per-track opt-in ("make this a two-canvas
|
||||||
|
track"). **That is superseded.** The settled rule, verbatim from Daniel:
|
||||||
|
|
||||||
|
> *"Items AND tracks added to the timeline in arrange mode or design mode should be
|
||||||
|
> tagged as for that mode only. Existing tracks and items default to arrange. If an
|
||||||
|
> item is added to arrange mode, it does not appear in design mode. If an item is
|
||||||
|
> added in design mode, it is NOT part of the arrange mode."*
|
||||||
|
|
||||||
|
Unpacked into the settled model:
|
||||||
|
|
||||||
|
- **Auto-tag at creation, not manual opt-in.** New content — **both new tracks and
|
||||||
|
new items** — is tagged to whatever mode is **active at the moment it is created**.
|
||||||
|
There is no separate "convert this track" gesture; membership follows the active
|
||||||
|
stance automatically.
|
||||||
|
- **Pre-existing content defaults to Arrange.** Every track and item that existed
|
||||||
|
before the mode system (or before this sub-phase) is an Arrange member, exactly as
|
||||||
|
the track-level default already established.
|
||||||
|
- **Membership is exclusive.** Design-created content never appears in Arrange, and
|
||||||
|
Arrange-created content never appears in Design — the one exception is the existing
|
||||||
|
**show-both** escape hatch, which continues to pin a leaf visible across modes.
|
||||||
|
- **Track level unchanged; item level is the new reach.** At the track level this is
|
||||||
|
the same tag-to-active-mode rule already settled. What is new: when a **shared**
|
||||||
|
track (present in both stances — a show-both track, or a folder carrying its own
|
||||||
|
media) gains an item in Design mode, that item lands in the track's **Design lane**
|
||||||
|
and is invisible/silent in Arrange, and vice versa. That per-item split is the
|
||||||
|
fixed-lane mechanism doing its job.
|
||||||
|
|
||||||
|
This resolves the old fork 2 (opt-in vs. automatic) in favor of **automatic**, and
|
||||||
|
it is the through-line that also settles fork 4 (capture placement) below.
|
||||||
|
|
||||||
|
## Inactive-mode items — settled (hidden AND silenced)
|
||||||
|
|
||||||
|
When you are in one mode, the other mode's items on a shared track are **both hidden
|
||||||
|
and silenced**: the off-mode lane is set `C_LANEPLAYS = 0` — neither shown nor
|
||||||
|
played. This matches the exclusive-membership rule (Design-created content is *not
|
||||||
|
part of* Arrange, so it must not sound there) and the track-level principle already
|
||||||
|
settled: **flipping modes is a real change, not a cosmetic one.** A user who wants
|
||||||
|
the design lane audible under the arrangement uses show-both, which is the deliberate
|
||||||
|
opt-out; the default is full separation.
|
||||||
|
|
||||||
|
## Managed vs. manual lanes — settled (2026-07-23)
|
||||||
|
|
||||||
|
Fixed lanes are not only the mechanism we borrow for mode-item layering — they are
|
||||||
|
also **REAPER's native comping surface.** A user may want *their own* multiple lanes
|
||||||
|
on a track (manual comp takes, alternate reads) that have nothing to do with the mode
|
||||||
|
system. So a fixed-lane track can host **both** kinds of lane at once, and the tool
|
||||||
|
must not confuse them. Daniel's constraint, verbatim intent: *"as long as I can still
|
||||||
|
opt in manually to put multiple lanes on a track. we must index which lanes are
|
||||||
|
managed and which lanes are manual."*
|
||||||
|
|
||||||
|
The settled distinction:
|
||||||
|
|
||||||
|
- **Managed lanes** — minted and owned by the mode system to represent a mode's item
|
||||||
|
set on that track. Mode toggles drive these: the active mode's managed lane shows
|
||||||
|
and plays (`C_LANEPLAYS`), inactive-mode managed lanes are hidden and silenced.
|
||||||
|
These are the lanes the two-canvas mechanism creates and controls.
|
||||||
|
- **Manual lanes** — lanes the user created outside the mode system (native REAPER
|
||||||
|
comping, alternate takes). The mode system did **not** mint these and **must never
|
||||||
|
touch them**: a mode toggle never shows, hides, silences, re-lanes, or re-plays a
|
||||||
|
manual lane. Its `C_LANEPLAYS` state is the user's, left exactly as they set it.
|
||||||
|
|
||||||
|
This is the fixed-lane analog of the two invariants already load-bearing in D1 —
|
||||||
|
*never touch `B_MUTE`/`I_SOLO`* and *never touch the master* — extended to a third
|
||||||
|
surface: **never drive a lane the tool did not mint.** It is the same non-destructive
|
||||||
|
promise (the tool owns only what it created) reaching one level deeper, into the lane
|
||||||
|
dimension.
|
||||||
|
|
||||||
|
### Lane-ownership index (the new data)
|
||||||
|
|
||||||
|
The tool maintains a **lane-ownership index**: per (track GUID, lane) it records
|
||||||
|
whether the lane is **managed** (and if so, which mode owns it) or **manual**. This
|
||||||
|
rides in the `"reasampler"` `view_state` alongside the membership index, GUID-keyed
|
||||||
|
and portable — same travel-with-the-`.rpp` guarantee as everything else in the view
|
||||||
|
section. Deciding *which lanes a toggle may touch* (managed only) is a **pure,
|
||||||
|
testable** query over this index; reading REAPER's live lane state is **shell**. The
|
||||||
|
seam is the same as everywhere else in the phase.
|
||||||
|
|
||||||
|
> **Lane-identity fragility — flag for the implementation spec, not solved here.**
|
||||||
|
> REAPER's `I_FIXEDLANE` is the lane's identity and is how the ownership index keys to
|
||||||
|
> a specific lane. Whether `I_FIXEDLANE` values survive lane reorder / renumber /
|
||||||
|
> deletion without the index going stale is a **design point the technical spec must
|
||||||
|
> resolve** (the same class of concern as GUID-keyed reorder-safety for tracks). The
|
||||||
|
> product decision is only that managed and manual lanes are indexed and distinct; how
|
||||||
|
> the index stays correct under lane reordering is an implementation call, not a
|
||||||
|
> product one.
|
||||||
|
|
||||||
|
### What a mode toggle does to manual lanes (intended behavior)
|
||||||
|
|
||||||
|
A mode toggle **leaves every manual lane exactly as the user set it** — its
|
||||||
|
`C_LANEPLAYS` (play/show) state is untouched across the toggle. Only managed lanes are
|
||||||
|
partitioned into active (shown/played) and inactive (hidden/silenced). Concretely: if
|
||||||
|
a shared track has an Arrange managed lane, a Design managed lane, and two manual comp
|
||||||
|
lanes the user is auditioning, flipping Arrange↔Design swaps which managed lane plays
|
||||||
|
and leaves both manual lanes precisely as they were.
|
||||||
|
|
||||||
|
**Interaction with auto-tag.** Auto-tag applies to content the user adds as **normal
|
||||||
|
timeline content** — items dropped, recorded, or placed onto the arrange surface in
|
||||||
|
the active mode. It does **not** reach into a lane the user is hand-managing: items a
|
||||||
|
user adds to a **manual** lane are **not** auto-tagged to the active mode, because the
|
||||||
|
user has taken explicit lane control there and the tool respects that boundary — the
|
||||||
|
manual/managed distinction is exactly the signal that this content is outside the mode
|
||||||
|
system.
|
||||||
|
|
||||||
|
> **Auto-tag/manual-lane boundary heuristic — flag for the implementation spec.**
|
||||||
|
> Distinguishing "item added as normal timeline content (auto-tag it)" from "item added
|
||||||
|
> to a manual lane the user hand-manages (leave it alone)" may need a heuristic at the
|
||||||
|
> detection layer — e.g., an item landing in a lane the ownership index marks manual is
|
||||||
|
> exempt from auto-tag; an item landing outside any managed lane on a mode-managed
|
||||||
|
> track follows the active-mode rule. The precise rule is an **open implementation
|
||||||
|
> design point**, not invented here. The product decision is the boundary itself:
|
||||||
|
> manual-lane content is off-limits to auto-tag.
|
||||||
|
|
||||||
|
## Capture placement — settled (mode-aware)
|
||||||
|
|
||||||
|
Daniel's rule is general over *"items added to the timeline."* So an **explicit**
|
||||||
|
placement while in Design mode — including capture-and-place — lands the item in the
|
||||||
|
**Design lane**; the same rule governs manual insertion. This is the same auto-tag
|
||||||
|
rule reaching the placement pillar, not a new special case.
|
||||||
|
|
||||||
|
**The capture load-bearing principle is untouched.** Capture still writes a file to
|
||||||
|
the bank and an index entry and **never** auto-inserts into the timeline. This rule
|
||||||
|
governs only *where an explicit placement lands* once the user chooses to place —
|
||||||
|
it does not create any auto-insertion path. The precision invariants (null test,
|
||||||
|
bit-identical repeats, non-destructive, exact bounds, relative paths) are entirely
|
||||||
|
unaffected: no capture path changes; lanes are a placement/view concern downstream
|
||||||
|
of the file.
|
||||||
|
|
||||||
|
## REAPER version floor — settled (v7)
|
||||||
|
|
||||||
|
Fixed lanes shipped in **REAPER 7**, so this sub-phase sets the runtime floor at
|
||||||
|
**v7**. The full API surface (track-side `I_FREEMODE=2` / `I_NUMFIXEDLANES` /
|
||||||
|
`C_LANEPLAYS:N`, item-side `I_FIXEDLANE` / `C_LANEPLAYS` / `B_FIXEDLANE_HIDDEN`) is
|
||||||
|
**verified present in the vendored SDK header**. No version gate branch is needed —
|
||||||
|
below v7 the sub-phase is simply unavailable.
|
||||||
|
|
||||||
|
## Technical consideration — detecting new content (for the implementation spec)
|
||||||
|
|
||||||
|
**REAPER exposes no clean "item added" / "track added" event callback.** The auto-tag
|
||||||
|
rule therefore requires the shell to detect new track/item GUIDs by **diffing project
|
||||||
|
state on the panel's existing timer**. The `bank_panel` already polls and fingerprints
|
||||||
|
the bank; this extends that same machinery to the timeline's tracks and items.
|
||||||
|
|
||||||
|
- **Cadence / correctness.** Each poll, the shell compares the live track/item GUID
|
||||||
|
set against the previous poll's set; **any GUID new since the last poll is tagged to
|
||||||
|
the then-active mode.** The diffing cadence and the "new-since-last-poll ⇒ tag"
|
||||||
|
correctness rule are a design point the implementation must handle (e.g., first poll
|
||||||
|
after project open must not mass-tag pre-existing content — pre-existing defaults to
|
||||||
|
Arrange, per the membership rule).
|
||||||
|
- **Pure/shell split.** The **tagging decision** — given a set of new GUIDs plus the
|
||||||
|
active mode, produce the membership writes — is **pure and unit-testable**, mirroring
|
||||||
|
the existing planner. The **detection** — diffing REAPER's live item/track set each
|
||||||
|
timer tick — is **shell**. Keep the seam here: the shell hands the pure model "these
|
||||||
|
GUIDs are new, the active mode is M," and the model returns the membership mutations.
|
||||||
|
|||||||
Reference in New Issue
Block a user