Resolve ReaSampler 9000 samples against the instance's own project, not the focused tab

sampleRoots matches getReaperParent(3) among EnumProjects' open projects; every instrument sample read goes through an ordered root list (one root today) so a later temp-bank root slots in after it.
This commit is contained in:
2026-09-25 14:55:50 -04:00
parent c3e7556261
commit 0976ea13a8
11 changed files with 304 additions and 42 deletions
+156
View File
@@ -0,0 +1,156 @@
// Standalone tests for reasampler::instrument::map::sample_roots — no VST3, no REAPER, no
// test framework. The project identities are addresses of local objects standing in for
// ReaProject*; paths are lowercase forward-slash so normalizeSlashes is the identity on
// every platform.
#include "../src/core/instrument/map/sample_roots.h"
#include <cstdint>
#include <cstdio>
#include <map>
#include <string>
#include <vector>
using namespace reasampler::instrument::map;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
namespace {
int projA = 0, projB = 0, projC = 0, stranger = 0;
// Tab 0 (projA) plays the focused tab — the one the old EnumProjects(-1) lookup answered.
std::vector<ProjectTab> threeTabs() {
return {
{&projA, "c:/projects/a/song_a.rpp"},
{&projB, "c:/projects/b/song_b.rpp"},
{&projC, ""}, // never saved
};
}
std::vector<std::uint8_t> bytesOf(const std::string& s) {
return std::vector<std::uint8_t>(s.begin(), s.end());
}
} // namespace
// --- ownProjectDir ------------------------------------------------------------
static void testBackgroundInstanceResolvesItsOwnProjectNotTheFirstTab() {
CHECK(ownProjectDir(&projB, threeTabs()) == "c:/projects/b");
}
static void testEachOfTwoProjectsResolvesItsOwnDirectory() {
const auto tabs = threeTabs();
CHECK(ownProjectDir(&projA, tabs) == "c:/projects/a");
CHECK(ownProjectDir(&projB, tabs) == "c:/projects/b");
}
static void testNullIdentityResolvesNothing() {
CHECK(ownProjectDir(nullptr, threeTabs()).empty());
}
static void testUnlistedProjectResolvesNothingRatherThanAnotherTab() {
CHECK(ownProjectDir(&stranger, threeTabs()).empty());
}
static void testUnsavedOwnProjectResolvesNothingEvenBesideSavedTabs() {
CHECK(ownProjectDir(&projC, threeTabs()).empty());
}
static void testNoOpenProjectsResolvesNothing() {
CHECK(ownProjectDir(&projA, {}).empty());
}
// --- sampleRootCandidates -----------------------------------------------------
static void testProjectDirIsTheOnlyCandidate() {
const auto roots = sampleRootCandidates("c:/projects/b");
CHECK(roots.size() == 1);
CHECK(!roots.empty() && roots[0] == "c:/projects/b");
}
static void testNoProjectDirMeansNoCandidates() {
CHECK(sampleRootCandidates("").empty());
}
// --- readFromFirstRoot --------------------------------------------------------
static void testReadsUnderTheFirstRootThatHasTheFile() {
const std::map<std::string, std::string> disk = {
{"c:/second/reasampler_bank/x.wav", "SECOND"},
};
std::vector<std::string> asked;
const auto bytes = readFromFirstRoot(
{"c:/first", "c:/second"}, "reasampler_bank/x.wav", [&](const std::string& p) {
asked.push_back(p);
const auto it = disk.find(p);
return it == disk.end() ? std::vector<std::uint8_t>{} : bytesOf(it->second);
});
CHECK(bytes == bytesOf("SECOND"));
CHECK(asked.size() == 2);
CHECK(asked.size() == 2 && asked[0] == "c:/first/reasampler_bank/x.wav");
}
static void testEarlierRootWinsWhenBothHaveTheFile() {
std::vector<std::string> asked;
const auto bytes = readFromFirstRoot(
{"c:/first", "c:/second"}, "reasampler_bank/x.wav", [&](const std::string& p) {
asked.push_back(p);
return bytesOf(p);
});
CHECK(bytes == bytesOf("c:/first/reasampler_bank/x.wav"));
CHECK(asked.size() == 1);
}
static void testNoRootsReadsNothing() {
int calls = 0;
const auto bytes = readFromFirstRoot({}, "reasampler_bank/x.wav",
[&](const std::string&) {
++calls;
return bytesOf("never");
});
CHECK(bytes.empty());
CHECK(calls == 0);
}
static void testEmptyRelativePathReadsNothing() {
int calls = 0;
const auto bytes = readFromFirstRoot({"c:/first"}, "", [&](const std::string&) {
++calls;
return bytesOf("never");
});
CHECK(bytes.empty());
CHECK(calls == 0);
}
static void testNoRootHoldingTheFileYieldsEmpty() {
const auto bytes = readFromFirstRoot({"c:/first", "c:/second"}, "reasampler_bank/x.wav",
[](const std::string&) {
return std::vector<std::uint8_t>{};
});
CHECK(bytes.empty());
}
int main() {
testBackgroundInstanceResolvesItsOwnProjectNotTheFirstTab();
testEachOfTwoProjectsResolvesItsOwnDirectory();
testNullIdentityResolvesNothing();
testUnlistedProjectResolvesNothingRatherThanAnotherTab();
testUnsavedOwnProjectResolvesNothingEvenBesideSavedTabs();
testNoOpenProjectsResolvesNothing();
testProjectDirIsTheOnlyCandidate();
testNoProjectDirMeansNoCandidates();
testReadsUnderTheFirstRootThatHasTheFile();
testEarlierRootWinsWhenBothHaveTheFile();
testNoRootsReadsNothing();
testEmptyRelativePathReadsNothing();
testNoRootHoldingTheFileYieldsEmpty();
if (g_fail == 0) std::printf("sample_roots: all tests passed\n");
return g_fail != 0;
}