#pragma once // batch_capture — the REAPER-free logic behind M11 batch capture (one action fires // N captures: one bank sample per selected item / per razor area). // // PURE MODULE (CLAUDE.md §load-bearing split): NO REAPER types, NO SWELL, NO // vendor/ includes. Standard library only. The batch shell (main.cpp) reads the DAW // state (selected items -> their exact bounds; every track's P_RAZOREDITS -> areas) // and hands the raw ranges here so the genuinely-pure, easy-to-get-wrong pieces are // unit-tested outside the DAW: // // 1. planCaptureUnits: an ordered list of (start,end) source ranges -> an ordered // list of CaptureUnit, each carrying its 1-based ordinal and validated bounds. // Empty/inverted ranges are DROPPED (mirrors the offline backend's own // end>start guard) so a zero-length item/area never produces a stray render. // Order is preserved: unit ordinals count only the KEPT units, so a batch of // three valid items yields ordinals 1,2,3 regardless of dropped neighbors. // 2. BatchOutcome: order-preserving aggregation of per-unit results into a summary // (succeeded / failed counts + the ordered list of failures) so the shell can // report a mixed result with one console line and no partial-corruption // ambiguity. The AGGREGATION is pure; the render loop that feeds it is shell. // // Range is the ONLY thing that varies per unit here. FX scope (item vs track) is a // per-ACTION constant the shell already owns (fxBypassPlanFor); it is not a // per-unit field. Item-batch uses item scope; razor-batch uses track scope — the // shell passes the scope straight through to each render, unchanged from the // single-capture path. #include #include #include namespace reasampler { // 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]). Kept as a distinct type from CaptureUnit so the input // (raw, possibly-invalid) and the output (validated, ordinal-assigned) do not share // a shape by accident. Named BatchRange (not SourceRange) to avoid collision with // bank_model's SourceRange, which carries PPQ fields this planner does not 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 (empty/inverted) so no stray render is // planned; assigns 1-based ordinals over the KEPT units. An empty input (no selected // item / no razor area) yields an empty plan — the shell reports "nothing to batch" // and writes nothing (the same no-op posture the single-capture path takes). 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 is captured without // aborting the remaining units (no partial corruption: each unit is independent, and // the selection is restored on every exit path by the shell's RAII guard). 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