fix(processor): close S-VIEW-4 previewVelocity persistence loop

Add previewVelocity_ member to ReaSamplerProcessor; populate it in
getState and restore it in setState so the field round-trips in the
plugin, not just in the pure unit test. Clamp 1..127 at the
deserialize read boundary in sample_map.cpp.
This commit is contained in:
2026-07-27 13:34:39 -04:00
parent b0b24c3e31
commit 5db620be78
3 changed files with 15 additions and 1 deletions
+4
View File
@@ -197,6 +197,9 @@ tresult PLUGIN_API ReaSamplerProcessor::setState(IBStream* state) {
channelMode_ = cs.channelMode; channelMode_ = cs.channelMode;
} }
applyOutputArrangement(cs.channelMode); applyOutputArrangement(cs.channelMode);
// S-VIEW-4: restore the per-instance preview velocity. No mutex — setState is a load-time
// call serialized by the host; there is no concurrent writer before Wave 2.
previewVelocity_ = cs.previewVelocity;
// Rebuild from the restored state (off-thread — setState is a load-time call). // Rebuild from the restored state (off-thread — setState is a load-time call).
reloadFromBank(); reloadFromBank();
return kResultOk; return kResultOk;
@@ -217,6 +220,7 @@ tresult PLUGIN_API ReaSamplerProcessor::getState(IBStream* state) {
std::lock_guard<std::mutex> lock(assignMarkerMutex_); std::lock_guard<std::mutex> lock(assignMarkerMutex_);
state_out.lastConsumedAssignGeneration = lastConsumedAssignGeneration_; // S8 reader marker state_out.lastConsumedAssignGeneration = lastConsumedAssignGeneration_; // S8 reader marker
} }
state_out.previewVelocity = previewVelocity_; // S-VIEW-4: persist the preview strike velocity
const std::vector<std::uint8_t> bytes = serializeComponentState(state_out); const std::vector<std::uint8_t> bytes = serializeComponentState(state_out);
if (!bytes.empty()) { if (!bytes.empty()) {
const tresult wr = state->write(const_cast<std::uint8_t*>(bytes.data()), const tresult wr = state->write(const_cast<std::uint8_t*>(bytes.data()),
+6
View File
@@ -250,6 +250,12 @@ private:
// NOT read on the audio thread. // NOT read on the audio thread.
std::int64_t lastSeenBankGeneration_ = -1; std::int64_t lastSeenBankGeneration_ = -1;
// S-VIEW-4 preview-trigger velocity (MIDI 1..127). Persisted in component state (v6) so the
// user's chosen strike velocity survives a project save/reload. No concurrent writer before
// Wave 2 (no editor knob yet) — setState and getState are the sole accessors, both on the
// load/save thread (host-serialized). Default kPreviewVelocityDefault (64).
std::uint8_t previewVelocity_ = kPreviewVelocityDefault;
// Latched from setupProcessing so setActive/reload can size against it. Read // Latched from setupProcessing so setActive/reload can size against it. Read
// off-thread only. 0.0 is explicitly invalid — setupProcessing sets the real host rate // off-thread only. 0.0 is explicitly invalid — setupProcessing sets the real host rate
// before any audio, and reloadFromBank guards on it before use. // before any audio, and reloadFromBank guards on it before use.
+5 -1
View File
@@ -628,7 +628,11 @@ ComponentState deserializeComponentState(const std::vector<std::uint8_t>& bytes,
if (!r.ok) return out; // truncated before/inside the marker -> empty (marker 0 holds) if (!r.ok) return out; // truncated before/inside the marker -> empty (marker 0 holds)
const std::uint8_t previewVel = r.u8(); const std::uint8_t previewVel = r.u8();
if (!r.ok) return out; // truncated before the velocity byte -> empty (mid default holds) if (!r.ok) return out; // truncated before the velocity byte -> empty (mid default holds)
out.previewVelocity = previewVel; // Clamp to the documented MIDI 1..127 range: a 0 byte (or any out-of-spec value from a
// corrupt blob) falls back to the mid default rather than silencing the preview trigger.
out.previewVelocity = (previewVel >= 1 && previewVel <= 127)
? previewVel
: kPreviewVelocityDefault;
const std::uint32_t idLen = r.u32(); const std::uint32_t idLen = r.u32();
out.selectionId = r.str(idLen); out.selectionId = r.str(idLen);
if (!r.ok) { out.selectionId.clear(); return out; } // truncated id -> empty if (!r.ok) { out.selectionId.clear(); return out; } // truncated id -> empty