#pragma once // capture_name — the REAPER-free composition of one capture's label and file-stem base // from its source-track name(s), a local-calendar discriminator, and an optional batch // ordinal. The shell reads the names and the clock; the SHAPE of a capture's name is // decided here so it is testable without a DAW. #include #include #include namespace reasampler::capture { // The capture's own moment, already broken down into LOCAL calendar fields by the shell. // Passing fields rather than an epoch is what keeps the format deterministic under test: // an epoch would render differently per machine timezone. month < 1 or day < 1 means // "no stamp" and suppresses the discriminator entirely. struct CaptureStamp { int month = 0; // 1-12 int day = 0; // 1-31 int hour = 0; // 0-23 int minute = 0; // 0-59 }; // Longest source-name prefix kept in either the label or the stem. Real track names sit // far under it; the bound exists so a pathological name cannot push the rendered file // path toward the platform's limit, and so a label and its file still read as the same // name. inline constexpr std::size_t kMaxSourceNameBytes = 64; struct CaptureNameInputs { // Source-track names in source order — the first non-empty one names the capture, // the rest only contribute the "+N" multi-source marker. std::vector sourceNames; CaptureStamp stamp; // Batch unit ordinal; <= 0 for a single capture. int ordinal = 0; // The scope literal ("item"/"track"/"realtime"), used ONLY when no source name // resolved at all — otherwise the source name wins. std::string fallback = "capture"; }; struct CaptureName { // Sample::displayName. Legible, carries the source name verbatim, and is explicitly // NOT unique (core/model/CLAUDE.md §resample_name) — the stamp serves the eye. std::string label; // deriveBankPaths' baseName. Still passes through sanitizeStem, and stem uniqueness // is still entirely makeUniqueTag's job. std::string stemBase; }; // "MM-DD HHMM" (e.g. "08-01 1432"); empty when the stamp carries no calendar date. // Year is deliberately omitted: the card and the browse list are narrow, and Sample // carries the full createdTimestamp for anything needing the exact moment. std::string formatCaptureStamp(const CaptureStamp& stamp); CaptureName composeCaptureName(const CaptureNameInputs& in); // The single source of truth for the word itself — kCaptureTrackPrefixBare and // kCaptureTrackPrefix below both expand from this one token, so editing it can never // desync captureTrackName's "no readable source name" bare-word fallback from the // separator-terminated prefix it is derived from. #define REASAMPLER_CAPTURE_TRACK_WORD "Capture" // The bare word behind kCaptureTrackPrefix, needed by captureTrackName's // no-readable-source-name fallback. inline constexpr const char* kCaptureTrackPrefixBare = REASAMPLER_CAPTURE_TRACK_WORD; // Prefixed onto a source track's name to name the track a render-in-place created. // A display convention, not a persisted key — unlike a lane prefix or an action-id // suffix, changing it later strands nothing. inline constexpr const char* kCaptureTrackPrefix = REASAMPLER_CAPTURE_TRACK_WORD " "; // The new track's name for a render of `sourceName`. IDEMPOTENT — a fixed point on // its own output, so a second render over a result track yields "Capture MONEY" // again rather than "Capture Capture MONEY". A counter suffix is deliberately not // offered: REAPER does not uniquify track names either, and what distinguishes two // renders of one source is their position, not their name. std::string captureTrackName(const std::string& sourceName); } // namespace reasampler::capture