From 68d3a3dba0593b026f487481bed2219c3c0ebc7b Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Thu, 23 Jul 2026 16:51:33 -0400 Subject: [PATCH] docs(context): reconcile capture spec to two-scope model Update CONTEXT.md capture sections to two FX scopes (item/track, chain-independent, no master, always wet): build-order step 7, offline/realtime render API surface, the capture module sketch, and mark the realtime wet-master-routing open question resolved (track-tap). --- CONTEXT.md | 51 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 85aa51c..465d863 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -51,9 +51,12 @@ Pure (no REAPER types, fully unit-tested): REAPER-facing: - `capture` — the `ICaptureBackend` interface plus `OfflineRenderBackend` and - `RealtimeRecordBackend`. Input: a `CaptureRequest` (source mode, time range, - wet/dry, tail, SR/bit-depth/channels, output path). Output: a finished file + - a populated `Sample` handed to `bank_model`. + `RealtimeRecordBackend`. Input: a `CaptureRequest` (capture scope — item or + track, time range, tail, SR/bit-depth/channels, output path). Output: a finished + file + a populated `Sample` handed to `bank_model`. Capture is always wet; the FX + *scope* (not a wet/dry dial) is the control — the pure `render_settings` module + maps a scope to its render-source bits and its FX-bypass plan (see §Capture FX + scope below). - `insert` — placement via `InsertMedia`; conform-to-project-tempo vs literal, as an explicit flag (never silent stretching). - `bank_panel` — the docked grid: LICE-drawn thumbnails, audition, multi-select, @@ -81,21 +84,31 @@ tier filtering; JSON round-trip. Scratch tier is auto-prunable; archive is kept. Offline render (the crux — prototype this first): - Drive render settings with `GetSetProjectInfo` (`RENDER_BOUNDSFLAG`, `RENDER_STARTPOS`, `RENDER_ENDPOS`, `RENDER_TAILFLAG`, `RENDER_TAILMS`, - `RENDER_SRATE`, `RENDER_CHANNELS`, `RENDER_SETTINGS` for source = master mix / - selected tracks / selected items / time selection, wet vs dry) and + `RENDER_SRATE`, `RENDER_CHANNELS`, `RENDER_SETTINGS`) and `GetSetProjectInfo_String` (`RENDER_FILE`, `RENDER_PATTERN`, `RENDER_FORMAT`). + The `RENDER_SETTINGS` bit choice is driven by capture scope: **item** scope renders + selected media items (single-file), **track** scope renders selected tracks via + master. There is **no master-mix scope** (to capture the master, render a track). + Render is always wet — REAPER has no true pre-FX "dry" render bit; FX scoping is done + by the FX-bypass-around-render mechanism, not a render bit (see §Capture FX scope). - Trigger a no-dialog render via the appropriate render action / `RENDER_SETTINGS` bit. Confirm the exact command id and the "render without opening dialog" flag against current REAPER — do not assume; test that it runs headless. - Determinism is a hard requirement: two identical requests must produce bit-identical files (enables the null test below). -Realtime record: -- Standard "resample track" recipe: create a hidden track, set its record mode to - record-output (latency-compensated) or route the source to it via a send, arm - (`I_RECARM`), `CSurf_OnRecord`, run for the range, `CSurf_OnStop`, then move the - recorded source file into the bank and delete the temp track. Verify - `I_RECMODE` / `I_RECINPUT` values for output-recording. +Realtime record (**track scope** — item realtime is deferred): +- Taps the **selected track**, not the master. Recipe: create a hidden temp track, + route a **post-fader send from each selected source track into it** + (`CreateTrackSend`, default post-fader/full-stereo — captures each track's own + output *before* the parent/master sums it, so the tap is chain-independent by + construction and needs no FX bypass), set the temp's record mode to record-output + (latency-compensated, `B_MAINSEND=0` so it does not sum back), arm (`I_RECARM`), + `CSurf_OnRecord`, run for the range, `OnStopButtonEx`, then move the recorded file + into the bank and delete the temp track (which drops the sends — no source track is + left mutated). Verify `I_RECMODE` values for output-recording. +- Master→track sends are refused by REAPER as feedback loops — this is *why* the tap + is the selected track's post-fader output, not the master. Sources & metadata: - Time selection: `GetSet_LoopTimeRange`. Razor edits: @@ -124,8 +137,13 @@ Persistence & paths: it survives Save / Save As. (bank travels with the .rpp) 5. `bank_panel`: docked grid with thumbnails, audition, selection. 6. `insert`: "insert selected sample at edit cursor" action via `InsertMedia`. -7. Capture action family: master / selected tracks / selected items / razor area, - each with wet-dry and tail options, all registered as bindable actions. +7. Capture action family: two FX scopes — **item** (item/take FX only) and **track** + (item FX + the selected track's own track FX), each chain-independent (the + out-of-scope chain is neutralized to unity for the render). No master scope — to + capture the master, render a track. Each scope captures over a **time-or-razor + range** (razor is a range source, not a mode: razor-when-present, else time + selection). Capture is always wet; the scope is the control. All registered as + bindable actions (tail off; a tail-on variant is a later opt-in). 8. `RealtimeRecordBackend` behind the same interface. 9. Slots: "capture to slot N" / "insert slot N", MIDI-bindable (MPC-style). 10. Provenance + "re-capture from source"; null-test verify action. @@ -157,8 +175,11 @@ Persistence & paths: ## Open questions to resolve during build - Exact no-dialog render command/flag on the current REAPER build. -- Realtime record routing that captures wet master output without altering the - user's monitoring. +- ~~Realtime record routing that captures wet master output without altering the + user's monitoring.~~ **Resolved:** realtime taps the *selected track* (track scope), + not the master — a post-fader track→temp send is chain-independent by construction, + so there is no wet-master-routing problem and monitoring is untouched (see the + Realtime record API surface above). - Audio format for the bank (wav bit depth default; allow float for wavetable fidelity). - Thumbnail cache: recompute vs store peak bins alongside the index.