fix(review): dead silenceFlags store removed; channelModeFor extracted + tested; auto-default + drop comments corrected; v9 constant minted
This commit is contained in:
@@ -26,10 +26,11 @@ int b64Value(unsigned char c) {
|
||||
|
||||
std::vector<std::uint8_t> instrumentDropStateBytes(const std::string& sampleId) {
|
||||
// The ONE fact the drop carries: this capture is the instance's selection. Everything
|
||||
// else stays at the fresh-instance defaults (no zones, mono, generation 0) — the same
|
||||
// ComponentState a browser click would produce. serializeComponentState is the
|
||||
// instrument's own writer (the single source of truth for the byte layout), so this is
|
||||
// NOT a parallel encoder — it IS the instrument's encoder.
|
||||
// else stays at the fresh-instance defaults (no zones, implicit channel mode, generation
|
||||
// 0) — the same ComponentState a browser click would produce. The implicit mode means
|
||||
// the GA auto-default will follow the loaded capture's channel count on first reload.
|
||||
// serializeComponentState is the instrument's own writer (the single source of truth for
|
||||
// the byte layout), so this is NOT a parallel encoder — it IS the instrument's encoder.
|
||||
ComponentState cs;
|
||||
cs.selectionId = sampleId;
|
||||
return serializeComponentState(cs);
|
||||
|
||||
@@ -505,16 +505,15 @@ std::string ReaSamplerProcessor::reloadFromBank() {
|
||||
std::optional<SelectedSample> sel =
|
||||
selectSample(*banksJson, selectedSampleId());
|
||||
if (sel) {
|
||||
// GA auto-default: while the channel mode is IMPLICIT (never user-toggled),
|
||||
// follow the loaded capture's channel count — a stereo capture decodes (and
|
||||
// shows) Stereo, a mono one Mono. An unknown count (0, an older bank entry)
|
||||
// changes nothing; an explicit user choice is never fought. Decode-only: the
|
||||
// output bus is fixed stereo, so no bus work follows a flip.
|
||||
if (sel->channelCount > 0) {
|
||||
const ChannelMode desired = sel->channelCount >= 2 ? ChannelMode::Stereo
|
||||
: ChannelMode::Mono;
|
||||
// GA auto-default: channelModeFor computes the mode from the loaded capture's
|
||||
// REQUESTED channel count (always 2 for extension captures; mono only for
|
||||
// ingest-imported mono files). An unknown count (0) or explicit user choice
|
||||
// returns the current mode unchanged. Decode-only: the output bus is fixed
|
||||
// stereo, so no bus work follows a flip.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(channelModeMutex_);
|
||||
if (!channelModeExplicit_) channelMode_ = desired;
|
||||
channelMode_ = channelModeFor(sel->channelCount, channelMode_,
|
||||
channelModeExplicit_);
|
||||
mode = channelMode_;
|
||||
}
|
||||
std::optional<DecodedZonePcm> pcm =
|
||||
@@ -888,9 +887,6 @@ tresult PLUGIN_API ReaSamplerProcessor::process(ProcessData& data) {
|
||||
// render ADDS into a cleared buffer — RT-safe (no alloc/IO/lock). NEVER reads the mode here.
|
||||
float* ch0 = out.numChannels > 0 ? out.channelBuffers32[0] : nullptr;
|
||||
float* ch1 = out.numChannels > 1 ? out.channelBuffers32[1] : nullptr;
|
||||
// The PLUG-IN owns output silenceFlags (VST3 contract). Claim non-silence on every rendered
|
||||
// block — a stale host-side flag left unwritten could mute a channel downstream (GA).
|
||||
out.silenceFlags = 0;
|
||||
if (ch0 && ch1) {
|
||||
// Stereo: clear both, render L/R. A mono sample plays dual-mono via the engine's stereo
|
||||
// path (both channels equal), so a mono capture in stereo mode is centered, not silent.
|
||||
|
||||
@@ -63,6 +63,12 @@ std::optional<SelectedSample> selectSample(const std::string& banksJson,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ChannelMode channelModeFor(int channelCount, ChannelMode current, bool isExplicit) {
|
||||
if (isExplicit) return current; // user's explicit choice is never fought
|
||||
if (channelCount <= 0) return current; // unknown (0) or pathological -> no change
|
||||
return channelCount >= 2 ? ChannelMode::Stereo : ChannelMode::Mono;
|
||||
}
|
||||
|
||||
std::vector<SampleChoice> listSamples(const std::string& banksJson) {
|
||||
std::vector<SampleChoice> out;
|
||||
if (banksJson.empty()) return out;
|
||||
@@ -753,8 +759,8 @@ ComponentState deserializeComponentState(const std::vector<std::uint8_t>& bytes,
|
||||
// above-cap value (a corrupt blob) falls back to unity rather than silencing/blasting.
|
||||
if (version >= kSelectionZonesModeMarkerVelVoiceGainV8Version) {
|
||||
const double g = bitsToDouble(r.u64());
|
||||
if (!r.ok) return out; // truncated inside the gain double — unity holds (out already
|
||||
// carries mode/marker/velocity/voice fields from above)
|
||||
if (!r.ok) return out; // truncated inside the gain double — out already carries
|
||||
// mode/marker/velocity/voice fields from above; unity holds
|
||||
out.masterGainLinear =
|
||||
(std::isfinite(g) && g >= 0.0 && g <= vst::masterGainMaxLinear() * (1.0 + 1e-9))
|
||||
? g
|
||||
@@ -763,7 +769,7 @@ ComponentState deserializeComponentState(const std::vector<std::uint8_t>& bytes,
|
||||
// v9 (GA): the channel-mode-EXPLICIT flag. A v8-or-older blob skips it — the construction
|
||||
// default (false = implicit) holds, so an already-saved instance's mode is treated as the
|
||||
// un-touched default and the shell may auto-default it from the loaded capture.
|
||||
if (version >= kComponentStateVersion) {
|
||||
if (version >= kSelectionZonesModeMarkerVelVoiceGainExplicitV9Version) {
|
||||
const std::uint8_t explicitByte = r.u8();
|
||||
if (!r.ok) return out; // truncated before the flag -> empty (implicit holds)
|
||||
out.channelModeExplicit = (explicitByte == 1);
|
||||
|
||||
@@ -60,6 +60,16 @@ struct SelectedSample {
|
||||
std::optional<SelectedSample> selectSample(const std::string& banksJson,
|
||||
const std::string& sampleId);
|
||||
|
||||
// GA auto-default rule (pure, tested): given the capture's requested channel count, the
|
||||
// instance's current mode, and whether the user has explicitly toggled the mode, return
|
||||
// the mode to apply. Explicit choice is never overridden. An unknown channelCount (0)
|
||||
// leaves the current mode unchanged. Used by reloadFromBank in the single-capture path.
|
||||
// * isExplicit == true -> current (user's choice stands)
|
||||
// * channelCount == 0 -> current (unknown, skip)
|
||||
// * channelCount >= 2 -> Stereo
|
||||
// * channelCount == 1 -> Mono
|
||||
ChannelMode channelModeFor(int channelCount, ChannelMode current, bool isExplicit);
|
||||
|
||||
// One entry in the capture browser's card list: the stable id + display name plus the S2
|
||||
// intrinsics + bank the browser draws as a card (peak thumbnail + name + root/key badge,
|
||||
// filterable by bank). Peaks are NOT here — they are computed shell-side from the decoded
|
||||
@@ -526,6 +536,10 @@ inline constexpr std::uint32_t kComponentStateVersion = 9;
|
||||
// explicit flag). Retained so deserializeComponentState can lift a v8 blob to implicit mode.
|
||||
inline constexpr std::uint32_t kSelectionZonesModeMarkerVelVoiceGainV8Version = 8;
|
||||
|
||||
// The GA combined-state version (v8 + the channel-mode-EXPLICIT flag). Mirrors the
|
||||
// v8/v7/v6/… series so the v9-branch check in deserializeComponentState is self-describing.
|
||||
inline constexpr std::uint32_t kSelectionZonesModeMarkerVelVoiceGainExplicitV9Version = 9;
|
||||
|
||||
// The pre-FB1 combined-state version (selection + zones + channel mode + consumed marker +
|
||||
// preview velocity + voice system, no master gain). Retained so deserializeComponentState can
|
||||
// lift a v7 blob to unity master gain.
|
||||
|
||||
@@ -146,6 +146,34 @@ static void testSelectChannelCountThreaded() {
|
||||
CHECK(selUn && selUn->channelCount == 0); // unstamped -> unknown, never a guess
|
||||
}
|
||||
|
||||
// --- channelModeFor (GA auto-default rule) -------------------------------------------
|
||||
|
||||
static void testChannelModeForExplicitIsNeverFought() {
|
||||
// An explicit user choice is ALWAYS returned unchanged, regardless of channelCount.
|
||||
CHECK(channelModeFor(2, ChannelMode::Mono, true) == ChannelMode::Mono);
|
||||
CHECK(channelModeFor(1, ChannelMode::Stereo, true) == ChannelMode::Stereo);
|
||||
CHECK(channelModeFor(0, ChannelMode::Stereo, true) == ChannelMode::Stereo);
|
||||
}
|
||||
|
||||
static void testChannelModeForUnknownCountIsNoOp() {
|
||||
// An unknown channel count (0 — an older bank entry) leaves the current mode unchanged.
|
||||
CHECK(channelModeFor(0, ChannelMode::Mono, false) == ChannelMode::Mono);
|
||||
CHECK(channelModeFor(0, ChannelMode::Stereo, false) == ChannelMode::Stereo);
|
||||
}
|
||||
|
||||
static void testChannelModeForStereoCapture() {
|
||||
// A capture with channelCount >= 2 selects Stereo (regardless of current mode).
|
||||
CHECK(channelModeFor(2, ChannelMode::Mono, false) == ChannelMode::Stereo);
|
||||
CHECK(channelModeFor(2, ChannelMode::Stereo, false) == ChannelMode::Stereo);
|
||||
CHECK(channelModeFor(6, ChannelMode::Mono, false) == ChannelMode::Stereo);
|
||||
}
|
||||
|
||||
static void testChannelModeForMonoCapture() {
|
||||
// A capture with channelCount == 1 selects Mono (ingest-imported mono files only).
|
||||
CHECK(channelModeFor(1, ChannelMode::Stereo, false) == ChannelMode::Mono);
|
||||
CHECK(channelModeFor(1, ChannelMode::Mono, false) == ChannelMode::Mono);
|
||||
}
|
||||
|
||||
static void testSelectEmptyBlob() {
|
||||
CHECK(!selectSample("", "a").has_value());
|
||||
}
|
||||
@@ -2100,6 +2128,10 @@ int main() {
|
||||
testSelectLoopThreaded();
|
||||
testSelectNoLoopIsAbsent();
|
||||
testSelectChannelCountThreaded();
|
||||
testChannelModeForExplicitIsNeverFought();
|
||||
testChannelModeForUnknownCountIsNoOp();
|
||||
testChannelModeForStereoCapture();
|
||||
testChannelModeForMonoCapture();
|
||||
testSelectEmptyBlob();
|
||||
testSelectMalformedBlob();
|
||||
testSelectZeroSamples();
|
||||
|
||||
Reference in New Issue
Block a user