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:
2026-07-23 10:01:34 -04:00
parent 4f1231ea81
commit 7b530193b2
7 changed files with 679 additions and 60 deletions
+123
View File
@@ -0,0 +1,123 @@
// render_settings.cpp — pure logic for the M7 capture action family. See header.
// NO REAPER types; unit-tested by tests/test_render_settings.cpp.
#include "render_settings.h"
#include <sstream>
namespace reasampler {
RenderSettingsChoice renderSettingsFor(SourceMode mode, double /*wetDry*/) {
// All M7 actions are wet-only. `wetDry` is accepted so CaptureRequest.wetDry
// remains the seam for future M10 dry work, but it does not affect the mapping.
RenderSettingsChoice c;
switch (mode) {
case SourceMode::MasterMix:
case SourceMode::TimeSelection:
// Master IS the mix — wet-only; &(1|2)==0, no source bits.
c.settings = kRenderMasterMix;
c.supported = true;
return c;
case SourceMode::SelectedTracks:
// Selected tracks via master (&128) — wet (post-FX). Header ~3041.
c.settings = kRenderSelTracksViaMaster;
c.supported = true;
return c;
case SourceMode::SelectedItems:
// Selected media items, rendered to ONE file (single-file bit) so a
// multi-item selection yields a single bank entry, not N wavs.
c.settings = kRenderSelItems | kRenderSingleFile;
c.supported = true;
return c;
case SourceMode::RazorArea:
// Render razor edits to ONE file (same single-file rationale as items).
c.settings = kRenderRazorEdits | kRenderSingleFile;
c.supported = true;
return c;
case SourceMode::Realtime:
// Not an offline-render source — the realtime backend (M8) owns it.
c.settings = kRenderMasterMix;
c.supported = false;
return c;
}
// Unreachable for a valid enum; fail closed (unsupported) rather than render.
c.supported = false;
return c;
}
std::vector<RazorRange> parseRazorEdits(const std::string& razorString) {
std::vector<RazorRange> ranges;
std::istringstream in(razorString);
// The string is space-separated TRIPLES: <start> <end> <envGuidString>.
// A track-audio area's third token is the literal two-char string `""`; an
// envelope-lane area's is a GUID `{…}`. We keep only track-audio triples.
std::string startTok, endTok, guidTok;
while (in >> startTok >> endTok >> guidTok) {
// Envelope-lane areas carry a real GUID; skip them (M7 = track audio).
// A track-audio area's GUID token is the empty quoted string `""`.
if (guidTok != "\"\"") continue;
// Parse the two time tokens. std::stod throws on garbage — guard so one
// malformed triple does not abort the whole parse.
double start = 0.0, end = 0.0;
try {
std::size_t sp = 0, ep = 0;
start = std::stod(startTok, &sp);
end = std::stod(endTok, &ep);
// Reject tokens with trailing garbage (e.g. "1.0x") — a partial parse
// is a malformed area, not a valid range.
if (sp != startTok.size() || ep != endTok.size()) continue;
} catch (...) {
continue;
}
if (end > start) ranges.push_back({start, end}); // drop empty/inverted
}
return ranges;
}
RazorRange razorUnionBounds(const std::vector<RazorRange>& ranges) {
if (ranges.empty()) return {0.0, 0.0};
RazorRange u = ranges.front();
for (const RazorRange& r : ranges) {
if (r.startSeconds < u.startSeconds) u.startSeconds = r.startSeconds;
if (r.endSeconds > u.endSeconds) u.endSeconds = r.endSeconds;
}
return u;
}
const std::vector<CaptureActionDef>& captureActionTable() {
// Built once (function-local static): four wet-only actions. Tail OFF for all
// (exact bounds). Ids are FOREVER-STABLE — never edit a shipped string.
// Dry variants deferred to M10; see kRenderPreFaderStems note in the header.
static const std::vector<CaptureActionDef> table = {
// Master mix — wet only (master IS the mix; no pre-FX concept applies).
{"CEREBELLUM_REASAMPLER_CAPTURE_MASTER",
"ReaSampler: capture master mix", "master_mix",
SourceMode::MasterMix, 1.0},
// Selected tracks — wet (via master, &128).
{"CEREBELLUM_REASAMPLER_CAPTURE_TRACKS_WET",
"ReaSampler: capture selected tracks", "tracks_wet",
SourceMode::SelectedTracks, 1.0},
// Selected items — wet, single file (&32 | single-file).
{"CEREBELLUM_REASAMPLER_CAPTURE_ITEMS_WET",
"ReaSampler: capture selected items", "items_wet",
SourceMode::SelectedItems, 1.0},
// Razor area — wet, single file (&4096 | single-file).
{"CEREBELLUM_REASAMPLER_CAPTURE_RAZOR_WET",
"ReaSampler: capture razor area", "razor_wet",
SourceMode::RazorArea, 1.0},
};
return table;
}
} // namespace reasampler