#pragma once // The shared capture seam: CaptureRequest/CaptureResult (types both backends // speak), OfflineRenderBackend, and the makeUniqueTag/stampCaptureSample helpers. // Realtime's async begin/tick/abort surface lives in capture_realtime_shell.h. // // REAPER-free on purpose (bank_model only) so callers can depend on the seam // without dragging the SDK into every include site; the .cpp is the REAPER TU. #include #include #include "core/model/bank_model.h" #include "core/capture/render_settings.h" // TailMode — the three-state tail contract // Forward-declared, never dereferenced here — only the REAPER-facing .cpp touches these. class MediaTrack; class ReaProject; namespace reasampler::capture { using model::Sample; // 32-bit float is the default; rationale lives in capture.cpp next to the sink-config bytes. enum class WavBitDepth { Int16, Int24, Float32, }; // One capture, independent of source mode. struct CaptureRequest { SourceMode sourceMode = SourceMode::MasterMix; // Sample-accurate render bounds in project seconds — NO rounding. double startSeconds = 0.0; double endSeconds = 0.0; // 1.0 = fully wet, 0.0 = fully dry. Every current capture action sets 1.0; // true pre-FX dry isn't available via RENDER_SETTINGS (needs FX-bypass-around-render // or the realtime pre-FX path) so this stays a seam for that future work. double wetDry = 1.0; // Track GUID(s) when source mode is track-scoped (SelectedTracks); empty otherwise. // The backend only copies these onto the Sample — it never reads selection itself. std::vector trackGuids; // Render tail (docs/product/capture-tail.md §The three tail states). None = exact // bounds, no added silence — the only mode valid for null-test/verify captures. // tailMs applies only to Manual (clamped to 8s by the pure mapping); Auto uses // the 8s cap + -72 dB trim internally, None ignores it. TailMode tailMode = TailMode::None; double tailMs = 0.0; // 0 sampleRate => follow project rate. int sampleRate = 0; int channelCount = 2; WavBitDepth bitDepth = WavBitDepth::Float32; // Sanitized by capture_paths. uniqueTag (disambiguator) is supplied by the // backend caller so the pure naming logic stays testable. std::string baseName = "capture"; std::string uniqueTag; }; // Every failure is an explicit code, never a thrown exception across the REAPER boundary. 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 UnsupportedFormat, // requested bit depth has no known REAPER blob (Float32 only) RenderFailed, // the render action ran but produced no output file TransportBusy, // realtime backend: transport already playing/recording — refused MultiTrackRange, // a ranged item capture whose items span >1 track — would render N files BoundsMismatch, // the rendered file's frame count is not the requested window's }; struct CaptureResult { CaptureStatus status = CaptureStatus::RenderFailed; Sample sample; // valid only when status == Ok std::string message; // human-readable detail for the console log }; // Deterministic offline-render backend: master mix / time selection / selected // tracks / selected items / razor area, all wet-only, optional tail. Source // selection + range are resolved by the caller and handed in via CaptureRequest — // the backend drives RENDER_* and never reads the DAW selection itself. // SourceMode::Realtime returns UnsupportedMode. Non-destructive: restores every // RENDER_* setting it touches on every path. Plain concrete class — see the // no-shared-interface note in capture_realtime_shell.h before adding one back. class OfflineRenderBackend { public: CaptureResult capture(const CaptureRequest& request); }; // Mints the filesystem-safe disambiguating tag for one capture's file stem + // Sample id: "-", a per-session monotonic // counter. Wall-clock seconds alone collide when batch capture drives short // renders back-to-back, silently overwriting the first file. `prefix` is the // backend's family marker ("" offline, "rt-" realtime). std::string makeUniqueTag(const std::string& prefix); // Stamps the metadata shared by both backends onto `s`: trackGuids + channelCount // (echoed from the request), resolved sampleRate (request rate, else PROJECT_SRATE // from `rateProj`), captureTempo, the capture-start time signature // (TimeMap_GetTimeSigAtTime against `timeSigProj` — offline passes nullptr for the // active project, realtime pins the record's own project), the WAV-aware // contentHash of `absolutePath` (left empty when unreadable), and createdTimestamp. // Per-backend bits (id, paths, bounds, tier, realtime's length override) stay // with each caller. void stampCaptureSample(Sample& s, const CaptureRequest& req, ReaProject* rateProj, ReaProject* timeSigProj, const std::string& absolutePath); } // namespace reasampler::capture