// sample_roots — where the instrument looks for a bank-relative sample: the directory of // the project it LIVES IN, found by identity among the open projects, never the focused // tab's. Pure: the shell enumerates REAPER's projects and reads files; this decides which // project, which roots, and in what order. #pragma once #include #include #include #include namespace reasampler::instrument::map { // One enumerated project: its identity (an opaque ReaProject*, compared, never // dereferenced) and its .rpp path, empty for a never-saved project. struct ProjectTab { const void* project = nullptr; std::string rppPath; }; // The directory of the project whose identity is `own`; empty when `own` is not among // `open` or was never saved. Deliberately no fallback to any other project for a // NON-NULL `own`: another project's bank folder can hold a same-named file, and playing // it is the wrong-project bug this exists to close. // // `activeFallback` is used ONLY when `own` is null (REAPER may not expose // getReaperParent(3) yet during plugin creation/setState) — then it stands in for `own` // unchanged, so a still-null fallback (or one absent from `open`) resolves nothing exactly // as a null `own` always did. std::string ownProjectDir(const void* own, const std::vector& open, const void* activeFallback = nullptr); // The roots a bank-relative path is tried against, in order. Only the project directory // today — the ordered list is the seam a later root (tried after it) extends. Empty when // there is no project directory, which every caller treats as nothing to play. std::vector sampleRootCandidates(const std::string& projectDir); using FileReader = std::function(const std::string& path)>; // The bytes of `relativePath` resolved under the first root whose read yields any (a read // that comes back empty falls through to the next root); empty when none does. std::vector readFromFirstRoot(const std::vector& roots, const std::string& relativePath, const FileReader& read); } // namespace reasampler::instrument::map