capture: refuse the multi-track ranged item render, silence the track's children and receives for it, and gate every exact-bounds capture on its frame count

This commit is contained in:
2026-08-01 20:58:33 -04:00
parent 5e3ea6c851
commit 7dc80e7a4f
20 changed files with 525 additions and 27 deletions
+1 -1
View File
@@ -2127,7 +2127,7 @@ internal-drag path").
| Track | Owns |
|---|---|
| **T1** `capture-range-exactness` | `core/capture/render_settings`, `shell/capture/capture.cpp`'s render-configuration block, `shell/capture/scope_resolve` (source-mode selection only), `tests/test_render_settings.cpp` |
| **T1** `capture-range-exactness` | `core/capture/render_settings`, `shell/capture/capture.cpp`'s render-configuration block, `shell/capture/scope_resolve` (source-mode selection only), `tests/test_render_settings.cpp` — **plus, as landed:** the new pure `core/capture/render_window` + `core/capture/track_topology`, the new shells `shell/capture/render_selection` + `shell/capture/render_isolation`, and `renderOffline`'s guard block in `shell/capture/capture_orchestrator.cpp` (the one seam a fresh capture and a recipe replay both cross, so the refusal and the transient guards had to live there rather than in `scope_resolve`) |
| **T2** `mode-switch-discipline` | `core/view/view_mode_model` (solo cache), `shell/view/view.cpp` (`applyMode` seams + WANT block), `shell/actions/design_view_actions.cpp` (refusal feedback), `shell/panel/panel_input.cpp` **footer mode-segment block only** (`:300-309`), the mode segment's disabled state in `core/ui/footer_bar` + `shell/panel/panel_render.cpp` |
| **T3** `media-explorer-section` | `shell/actions/ingest.cpp` (register/dispatch/unregister), **the registration block in `src/app/main.cpp`** (the `hookcommand2` hook + unload mirror), the root-`CLAUDE.md` contract amendment |
| **T4** `drop-target-resolution` | `core/ui/drag_out`, `core/wire/instrument_drop`, `shell/panel/panel_drag.cpp`, `shell/actions/instrument_drop_win.cpp`, `shell/actions/drag_out_win.cpp` (hand-off timing), `tests/test_drag_out.cpp` |
+67
View File
@@ -445,3 +445,70 @@ today.
bitmap and confirmed to place the stroke correctly, or it is redesigned to rasterize at
physical rather than logical resolution once `IPlugViewContentScaleSupport` (or
equivalent) makes scaling real.
## A multi-track TRACK capture renders one file per track and lands one of them
**Context (surfaced by Ψ-W1-T1, capture-range-exactness).** Track scope has always
rendered through `RENDER_SETTINGS &128` ("selected tracks via master"), and the
ranged item capture now joins it there. The SDK header (~3041) documents the
single-file bit `&(4<<16)` for "rendering selected items or razor edits" only, so it
does not apply to `&128`: the reading is that N selected tracks produce N files.
`capture.cpp` sets `RENDER_PATTERN` to one literal stem and detects success by
`std::filesystem::exists`, so N stems collapse onto one name and whichever file
survives lands as a successful capture carrying one track's audio.
**The wart.** `renderOffline` refuses this shape for the ranged ITEM capture
(`render_settings::isMultiTrackRangedItemRender`) because that path was newly routed
into it. Track scope with two or more tracks selected has the same exposure and is
deliberately untouched — changing a shipped action from "produces a file" to
"refuses" is a behavioral-contract change, and it was out of that track's surface.
**Intended fix.** Not proposed. Three shapes exist and the choice is a product call,
not a mechanical one: refuse (matching the item path), render each track and land N
bank entries (that is batch capture's meaning, not this action's), or sum the
selected tracks into one file (needs a summing render source `&128` does not offer).
**The constraint the fix MUST handle.** The per-track-output reading of `&128` is
INFERRED from the header's single-file wording, never observed in a DAW. Verify it
first — including what REAPER actually writes when N stems share one literal
`RENDER_PATTERN` — because the answer decides whether this is a defect at all.
**Priority / risk.** Unknown until the DAW check above runs. If the reading is right,
a silently-wrong capture on an ordinary two-track selection; if wrong, nothing.
**Done looks like.** The `&128` multi-track output shape is DAW-confirmed, and track
scope either produces defined correct output for a multi-track selection or refuses
it with a message naming the reason.
## A `SelectedItems` recipe replays against whatever items are selected then
**Context (surfaced by Ψ-W1-T1, capture-range-exactness).** `RunRecaptureFromSource`
rebuilds a `CaptureRequest` from the recorded `CaptureRecipe` and resolves its source
tracks by GUID. `renderOffline` engages `RenderTrackSelection` only when the recipe's
source mode is `SelectedTracks`, which is what makes a ranged item capture and a
track capture replay against their recorded tracks rather than the live selection.
**The wart.** A recipe whose source mode is `SelectedItems` — every pre-fix item-scope
capture, and every post-fix full-extent one — renders `&32`, which prints whatever
items happen to be selected when the replay fires. The recorded recipe therefore does
not fully determine the audio it reproduces, which is what "recapture from source"
promises.
**Intended fix.** Not proposed. The recipe stores tracks and a range; it carries no
item GUIDs, so no guard on the shell side can reconstruct the item selection from
what is recorded. Closing it means widening `CaptureRecipe` (a wire-format change with
a version rung) or re-sourcing full-extent item captures through the tracks render too,
which would drag them onto the isolation path for no gain.
**The constraint the fix MUST handle.** Widening the recipe must keep every already-
persisted recipe readable, and must not make a replay depend on items that no longer
exist — a deleted source item has to degrade to a stated refusal, not a silent
substitution.
**Priority / risk.** Pre-existing; not introduced or worsened by the range-exactness
work. Harmless when the user re-runs a recapture with the same items still selected,
wrong when they do not.
**Done looks like.** A `SelectedItems` recapture either reproduces its recorded audio
from the recipe alone, or refuses with a message naming what the recipe cannot pin
down.