feat(capture): bindable capture family — master/tracks/items/razor (M7)
Four wet capture actions route to OfflineRenderBackend (snapshot/restore RENDER_*, 32-bit float), produce a Sample, add to the bank, persist. Pure render_settings maps source mode to RENDER_SETTINGS and parses P_RAZOREDITS (tested). No arrange insertion. True dry deferred to M10.
This commit is contained in:
+33
-18
@@ -4,10 +4,19 @@
|
||||
// reaper_plugin_functions.h WITHOUT REAPERAPI_IMPLEMENT — main.cpp is the one TU
|
||||
// that defines the API pointers; here they are extern (CLAUDE.md §contract).
|
||||
//
|
||||
// Scope (M3): ONE source mode — the time-selection master mix. Drives the
|
||||
// RENDER_* project settings via GetSetProjectInfo / _String, snapshots and
|
||||
// restores every setting it changes (non-destructive), triggers a render, then
|
||||
// populates a Sample. It NEVER inserts into the arrange (load-bearing principle).
|
||||
// Scope (M7): the full offline source family — master mix / time selection,
|
||||
// selected tracks, selected items, razor area — all wet-only with optional tail.
|
||||
// Drives the RENDER_* project settings via GetSetProjectInfo / _String
|
||||
// (the source-selection bits come from render_settings.cpp, the pure mapping),
|
||||
// snapshots and restores every setting it changes (non-destructive), triggers a
|
||||
// render, then populates a Sample. It NEVER inserts into the arrange
|
||||
// (load-bearing principle) — RENDER_ADDTOPROJ&1 is cleared on every path.
|
||||
//
|
||||
// The backend is SOURCE-AGNOSTIC: it does NOT read the DAW selection. The action
|
||||
// layer (main.cpp) resolves each source mode to a concrete time range (+ track
|
||||
// GUIDs for track captures) and hands it in via the CaptureRequest. This keeps
|
||||
// the render-driving here and the selection-reading testable/visible up in the
|
||||
// actions layer.
|
||||
//
|
||||
// RENDER PROGRESS WINDOW (Item 2 finding — not suppressible via stock API):
|
||||
// Triggering kActionRenderUsingMostRecentSettings (42230) causes REAPER to show
|
||||
@@ -30,6 +39,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "capture_paths.h"
|
||||
#include "render_settings.h"
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_EnumProjects
|
||||
@@ -61,11 +71,6 @@ constexpr int kActionRenderUsingMostRecentSettings = 42230;
|
||||
// ourselves for exact, unrounded bounds). Verified: SDK header line ~3042.
|
||||
constexpr double kBoundsCustom = 0.0;
|
||||
|
||||
// RENDER_SETTINGS master-mix bit pattern. Per the SDK header (line ~3041):
|
||||
// (&(1|2))==0 => master mix, &8=use render matrix. We want plain master mix:
|
||||
// no stems (bits 1|2 clear), no render matrix. Value 0 = master mix, no matrix.
|
||||
constexpr double kRenderSettingsMasterMix = 0.0;
|
||||
|
||||
// RENDER_TAILFLAG bit &1 = apply tail for custom time bounds. We clear it for
|
||||
// the spike (exact bounds, no added silence — precision invariant).
|
||||
constexpr double kTailFlagNone = 0.0;
|
||||
@@ -226,13 +231,16 @@ std::string makeUniqueTag() {
|
||||
CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
CaptureResult result;
|
||||
|
||||
// M3 implements only the master-mix / time-selection case. Both mean "render
|
||||
// the master mix over the requested bounds".
|
||||
if (request.sourceMode != SourceMode::MasterMix &&
|
||||
request.sourceMode != SourceMode::TimeSelection) {
|
||||
// Resolve the RENDER_SETTINGS source/processing bits for this mode + wet/dry
|
||||
// (pure mapping, unit-tested in render_settings). An unsupported mode (only
|
||||
// SourceMode::Realtime — that is the M8 realtime backend) is refused here so
|
||||
// the offline path never silently renders the wrong thing.
|
||||
const RenderSettingsChoice choice =
|
||||
renderSettingsFor(request.sourceMode, request.wetDry);
|
||||
if (!choice.supported) {
|
||||
result.status = CaptureStatus::UnsupportedMode;
|
||||
result.message = "OfflineRenderBackend (M3) supports only master-mix / "
|
||||
"time-selection capture.";
|
||||
result.message = "OfflineRenderBackend does not render this source mode "
|
||||
"(realtime capture is the M8 backend).";
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -350,8 +358,11 @@ CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
GetSetProjectInfo(proj, "RENDER_TAILMS", 0.0, true);
|
||||
}
|
||||
|
||||
// Master mix, no stems, no render matrix.
|
||||
GetSetProjectInfo(proj, "RENDER_SETTINGS", kRenderSettingsMasterMix, true);
|
||||
// Source-selection bits for this mode, from the pure render_settings mapping
|
||||
// (verified against SDK header ~3041). All M7 actions are wet-only:
|
||||
// master mix = 0; tracks = &128; items = &32|single-file; razor = &4096|single-file.
|
||||
GetSetProjectInfo(proj, "RENDER_SETTINGS",
|
||||
static_cast<double>(choice.settings), true);
|
||||
|
||||
// Resolve the effective sample rate. When the request carries 0 ("follow
|
||||
// project"), read PROJECT_SRATE explicitly so RENDER_SRATE is set to the
|
||||
@@ -448,6 +459,10 @@ CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
// Seconds are the authoritative source for the render. Do NOT add DAW-
|
||||
// unverifiable PPQ resolution here — it requires a live REAPER to validate.
|
||||
s.wetDry = request.wetDry;
|
||||
// Track GUIDs for track-scoped captures (empty for master/items/razor). The
|
||||
// caller resolved the selection to canonical GUID strings; we record them so a
|
||||
// "re-capture from source" (M10) knows which tracks the sample came from.
|
||||
s.trackGuids = request.trackGuids;
|
||||
s.channelCount = request.channelCount;
|
||||
// Store the resolved sample rate only when it is known (> 0). If the project
|
||||
// never pinned a rate (PROJECT_SRATE read 0), we did not force RENDER_SRATE
|
||||
@@ -465,7 +480,7 @@ CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
|
||||
result.status = CaptureStatus::Ok;
|
||||
result.sample = s;
|
||||
result.message = "Captured master mix [" +
|
||||
result.message = "Captured [" +
|
||||
std::to_string(request.startSeconds) + "s, " +
|
||||
std::to_string(request.endSeconds) + "s] -> " +
|
||||
paths.relativePath;
|
||||
|
||||
Reference in New Issue
Block a user