#pragma once // batch_capture — the REAPER-free logic behind batch capture (one action fires N // captures: one bank sample per selected item / per razor area). // // PURE MODULE: NO REAPER types, NO SWELL, NO vendor/ includes. Standard library // only. The batch shell reads the DAW state (selected items -> exact bounds; // each track's P_RAZOREDITS -> areas) and hands the raw ranges here: // // 1. planCaptureUnits: an ordered list of (start,end) ranges -> an ordered // list of CaptureUnit, each with a 1-based ordinal and validated bounds. // Empty/inverted ranges are dropped (mirrors the offline backend's own // end>start guard); ordinals count only the kept units, so three valid // items yield 1,2,3 regardless of dropped neighbors. // 2. BatchOutcome: order-preserving aggregation of per-unit results into a // summary (succeeded/failed counts + ordered failures) for one console // line with no partial-corruption ambiguity. // // Range is the only thing that varies per unit here. FX scope (item vs track) is // a per-action constant the shell already owns; item-batch uses item scope, // razor-batch uses track scope, passed through unchanged from the single-capture // path. #include #include #include namespace reasampler::capture { // One capture in a batch: an exact source range plus its 1-based ordinal within // the kept set. The ordinal disambiguates per-unit file stems (the offline // backend's unique tag is 1-second-granular, so a fast batch could otherwise // collide N files onto one name) and labels a failure in the summary. struct CaptureUnit { int ordinal = 0; // 1-based, counts kept units only double startSeconds = 0.0; // exact — no rounding double endSeconds = 0.0; }; // A source range handed in by the shell (a selected item's [pos, pos+len] or one // razor area's [start, end]). Named BatchRange (not SourceRange) to avoid // collision with bank_model's SourceRange, which carries PPQ fields this planner // doesn't need. struct BatchRange { double startSeconds = 0.0; double endSeconds = 0.0; }; // Validates + orders a batch's source ranges into capture units. Preserves input // order; drops every range with end <= start; assigns 1-based ordinals over the // kept units. An empty input yields an empty plan — the shell reports "nothing // to batch" and writes nothing. std::vector planCaptureUnits(const std::vector& ranges); // The per-unit verdict the shell records after each render attempt, in unit order. struct BatchUnitResult { int ordinal = 0; // the CaptureUnit's ordinal this result is for bool ok = false; // true iff the render + bank-add succeeded std::string detail; // failure reason (empty on success) — for the summary }; // Order-preserving aggregation of a batch's per-unit results. Built incrementally // by the shell (record() after each unit) so a mid-batch failure doesn't abort // the remaining units — each unit is independent. class BatchOutcome { public: // Records one unit's verdict. Order of calls IS the reported order. void record(int ordinal, bool ok, std::string detail = {}); std::size_t total() const { return results_.size(); } std::size_t succeeded() const; std::size_t failed() const; const std::vector& results() const { return results_; } // The ordered subset of results that failed (ok == false). For the summary line. std::vector failures() const; // A single human summary line for the console (explicit-action response — allowed // by the console policy; a batch-completion summary with failure counts qualifies, // per-unit success spam does not). `noun` is the unit word ("item" / "razor area"). // Examples: // all-success, 3 items : "ReaSampler batch capture: 3 items captured." // partial, 3 of 5 : "ReaSampler batch capture: 3 of 5 items captured " // "(2 failed: #2, #4)." // empty plan : "ReaSampler batch capture: nothing to capture." std::string summaryLine(const std::string& noun) const; private: std::vector results_; }; } // namespace reasampler::capture