pS-usage: captures held by live ReaSampler 9000 instances are un-prunable — instances publish usage_<guid> ext-state records (ComponentState v11), prune unions live holds into referenced

This commit is contained in:
2026-07-28 12:56:36 -04:00
parent f988ded543
commit 5886ae1456
19 changed files with 1282 additions and 10 deletions
+75
View File
@@ -2272,6 +2272,79 @@ static void testComponentStateV9LiftsToEmptyRefs() {
CHECK(back.map.zones.empty());
}
static void testInstanceGuidRoundTripV11() {
// v11 (pS-usage): the minted publish identity round-trips with the envelope
// neighbours (refs table before it, selection/zones after it) intact — the guid
// read consumed exactly its own bytes. An empty guid (never published) is legal
// and round-trips empty.
ComponentState s;
s.selectionId = "kick";
s.instanceGuid = "0123456789abcdef0123456789abcdef";
s.sampleRefs.push_back(refEntry("kick", "reasampler_bank/kick.wav", 36));
s.map.zones.push_back(zone("kick", 0, 127));
const ComponentState back =
deserializeComponentState(serializeComponentState(s), 44100.0);
CHECK(back.instanceGuid == "0123456789abcdef0123456789abcdef");
CHECK(back.sampleRefs.size() == 1);
CHECK(back.selectionId == "kick");
CHECK(back.map.zones.size() == 1);
ComponentState fresh;
fresh.selectionId = "s";
const ComponentState freshBack =
deserializeComponentState(serializeComponentState(fresh), 44100.0);
CHECK(freshBack.instanceGuid.empty());
CHECK(freshBack.selectionId == "s");
}
static void testComponentStateV10LiftsToEmptyGuid() {
// OLD-BLOB FALLBACK: a genuine v10 blob (refs table but no instance guid) restores
// with an EMPTY guid — the processor mints one on first publish — and every other
// field intact. Hand-built (serializeComponentState now emits v11, so it cannot
// make a v10 blob).
std::vector<std::uint8_t> v10;
v10.push_back(10); v10.push_back(0); v10.push_back(0); v10.push_back(0); // version 10
v10.push_back(0); // mode = mono
for (int i = 0; i < 8; ++i) v10.push_back(0); // marker = 0
v10.push_back(88); // preview velocity
v10.push_back(7); // voice count
v10.push_back(0); // voice mode = poly
v10.push_back(0); // trigger = retrigger
for (int i = 0; i < 8; ++i) v10.push_back(0); // gain double bytes...
v10[17 + 6] = 0xF0; v10[17 + 7] = 0x3F; // ...= 1.0 (LE IEEE-754)
v10.push_back(1); // explicit flag = true
// The v10 refs table: ONE entry {id "a", path "b/a.wav", root 36, no loop, ch 0, no name}.
v10.push_back(1); v10.push_back(0); v10.push_back(0); v10.push_back(0); // ref count 1
v10.push_back(1); v10.push_back(0); v10.push_back(0); v10.push_back(0); // id len 1
v10.push_back('a');
const std::string relPath = "b/a.wav";
v10.push_back(static_cast<std::uint8_t>(relPath.size()));
v10.push_back(0); v10.push_back(0); v10.push_back(0);
v10.insert(v10.end(), relPath.begin(), relPath.end());
v10.push_back(36); v10.push_back(0); v10.push_back(0); v10.push_back(0); // root 36
v10.push_back(0); // hasLoop = false
for (int i = 0; i < 16; ++i) v10.push_back(0); // loop start+end
v10.push_back(0); v10.push_back(0); v10.push_back(0); v10.push_back(0); // channels 0
v10.push_back(0); v10.push_back(0); v10.push_back(0); v10.push_back(0); // name len 0
// NO guid field (the v11 addition) — the selection id follows directly.
const std::string id = "saved";
v10.push_back(static_cast<std::uint8_t>(id.size()));
v10.push_back(0); v10.push_back(0); v10.push_back(0);
v10.insert(v10.end(), id.begin(), id.end());
v10.push_back(0); v10.push_back(0); v10.push_back(0); v10.push_back(0); // zone count 0
const ComponentState back = deserializeComponentState(v10, 44100.0);
CHECK(back.instanceGuid.empty()); // pre-v11 blob -> empty guid (minted on publish)
CHECK(back.sampleRefs.size() == 1);
CHECK(back.sampleRefs.size() == 1 && back.sampleRefs[0].sampleId == "a");
CHECK(back.sampleRefs.size() == 1 &&
back.sampleRefs[0].ref.relativePath == "b/a.wav");
CHECK(back.selectionId == "saved");
CHECK(back.channelModeExplicit);
CHECK(back.previewVelocity == 88);
CHECK(back.voiceCount == 7);
CHECK(back.map.zones.empty());
}
static void testReferencedSampleIdsDedup() {
// Selection first, then map order, duplicates collapsed; an empty selection contributes
// nothing (no phantom "" id in the refs table).
@@ -2524,6 +2597,8 @@ int main() {
testResolveFromRefsMissingRefDrops();
testResolveFromRefsMatchesBankResolve();
testComponentStateV9LiftsToEmptyRefs();
testInstanceGuidRoundTripV11();
testComponentStateV10LiftsToEmptyGuid();
testReferencedSampleIdsDedup();
testRefreshRefsFromBankUpsertAndOwnership();
testRetainRefsFiltersToPlayedSet();