7b530193b2
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.
116 lines
5.2 KiB
C++
116 lines
5.2 KiB
C++
#pragma once
|
|
// capture — the REAPER-facing capture shell (CLAUDE.md §load-bearing split).
|
|
//
|
|
// This header declares the capture *seam* the later milestones fill:
|
|
// * CaptureRequest — everything a capture needs, source-mode-agnostic.
|
|
// * ICaptureBackend — the one interface behind which OfflineRenderBackend
|
|
// (M3, here) and RealtimeRecordBackend (M8) both sit.
|
|
// * OfflineRenderBackend — the deterministic default; M3 implements ONLY the
|
|
// time-selection master-mix case.
|
|
//
|
|
// It includes bank_model (pure) to hand back a populated Sample, but NO REAPER
|
|
// headers — the .cpp is the REAPER-facing translation unit. Keeping this header
|
|
// REAPER-free lets callers (main.cpp, future actions.cpp) depend on the seam
|
|
// without dragging the SDK into every include site.
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "bank_model.h"
|
|
|
|
namespace reasampler {
|
|
|
|
// Audio bit-depth for the rendered wav. 32-bit float is the M3 default —
|
|
// rationale lives in capture.cpp next to the sink-config bytes.
|
|
enum class WavBitDepth {
|
|
Int16,
|
|
Int24,
|
|
Float32,
|
|
};
|
|
|
|
// One capture, independent of source mode. Populated by the caller (the action
|
|
// handler in M3; the action family in M7) and consumed by a backend.
|
|
//
|
|
// M3 fills only the fields the master-mix/time-selection path needs; the rest
|
|
// are declared now so M7/M8 do not reshape the struct (they are the seam).
|
|
struct CaptureRequest {
|
|
SourceMode sourceMode = SourceMode::MasterMix;
|
|
|
|
// Sample-accurate render bounds in project seconds. For the M3 spike these
|
|
// come straight from the time selection (GetSet_LoopTimeRange) — NO rounding.
|
|
double startSeconds = 0.0;
|
|
double endSeconds = 0.0;
|
|
|
|
// 1.0 = fully wet, 0.0 = fully dry. All M7 actions set this to 1.0 (wet).
|
|
// The field is kept as the seam for future true-dry work (M10 null test):
|
|
// true pre-FX dry offline is NOT available via RENDER_SETTINGS — it requires
|
|
// FX-bypass-around-render or the M8 realtime pre-FX path, and will be
|
|
// designed alongside the M10 null test. Also recorded on the Sample.
|
|
double wetDry = 1.0;
|
|
|
|
// Track GUID(s) the capture came from, when the source mode is track-scoped
|
|
// (SelectedTracks). Empty for master/items/razor. The action layer (M7)
|
|
// resolves the selection to canonical GUID strings and passes them here; the
|
|
// backend copies them onto the Sample (it does NOT itself read the selection —
|
|
// it stays source-agnostic, driven entirely by the request).
|
|
std::vector<std::string> trackGuids;
|
|
|
|
// Render tail. Default OFF for the spike (exact bounds, no added silence —
|
|
// precision invariant). M7 makes this bindable.
|
|
bool renderTail = false;
|
|
double tailMs = 0.0;
|
|
|
|
// Output format. 0 sampleRate => follow project rate (deterministic: the
|
|
// project rate is fixed for a given project).
|
|
int sampleRate = 0;
|
|
int channelCount = 2;
|
|
WavBitDepth bitDepth = WavBitDepth::Float32;
|
|
|
|
// Human base name for the file stem; sanitized by capture_paths. The unique
|
|
// tag (disambiguator) is supplied separately by the backend caller so the
|
|
// pure naming logic stays testable.
|
|
std::string baseName = "capture";
|
|
std::string uniqueTag; // e.g. a timestamp/counter; may be empty
|
|
};
|
|
|
|
// Outcome of a capture attempt. `Ok` carries the populated Sample; every failure
|
|
// is an explicit code (never a thrown exception across the REAPER boundary) so
|
|
// the action handler can log a precise reason.
|
|
enum class CaptureStatus {
|
|
Ok,
|
|
NoProject, // no active project to render / resolve a bank folder
|
|
EmptyRange, // start >= end: nothing to render
|
|
UnsupportedMode, // backend does not implement this source mode (M3 scope)
|
|
UnsupportedFormat, // requested bit depth has no known REAPER blob (M3: Float32 only)
|
|
RenderFailed, // the render action ran but produced no output file
|
|
};
|
|
|
|
struct CaptureResult {
|
|
CaptureStatus status = CaptureStatus::RenderFailed;
|
|
Sample sample; // valid only when status == Ok
|
|
std::string message; // human-readable detail for the console log
|
|
};
|
|
|
|
// The capture seam. One method: run a request, return a populated Sample (or a
|
|
// failure code). Backends are non-destructive — they must restore any global
|
|
// state they touch before returning (OfflineRenderBackend snapshots/restores the
|
|
// RENDER_* project settings).
|
|
class ICaptureBackend {
|
|
public:
|
|
virtual ~ICaptureBackend() = default;
|
|
virtual CaptureResult capture(const CaptureRequest& request) = 0;
|
|
};
|
|
|
|
// Deterministic offline-render backend. M7 implements the full offline source
|
|
// family — master mix / time selection, selected tracks, selected items, razor
|
|
// area — all wet-only (render_settings.h) with optional tail. The source
|
|
// selection + range are resolved by the caller (the action layer) and handed in
|
|
// via the CaptureRequest; the backend drives RENDER_* and never reads the DAW
|
|
// selection itself. SourceMode::Realtime returns UnsupportedMode (that is M8).
|
|
class OfflineRenderBackend : public ICaptureBackend {
|
|
public:
|
|
CaptureResult capture(const CaptureRequest& request) override;
|
|
};
|
|
|
|
} // namespace reasampler
|