fix(vst): pin output bus to stereo (mode is decode-only) killing the hard-right pan; auto-default channel mode from the loaded capture (state v9)

This commit is contained in:
2026-07-28 06:19:28 -04:00
parent 104a25f390
commit 3e6629a867
7 changed files with 212 additions and 74 deletions
+30
View File
@@ -851,6 +851,35 @@ static void testMonoSamplePlaysDualMonoInStereo() {
}
}
static void testDualMonoStereoSampleRendersCentered() {
// GA Bug 1 (pure-layer proof): a STEREO sample whose two channels are IDENTICAL (a
// dual-mono capture) must render EXACTLY equal L and R — bitwise, every frame — under
// BOTH pitch engines. Any asymmetry here (a silent L, a channel offset, divergent
// shifter state) would pan the output; hard-panned output from a dual-mono capture
// therefore cannot originate in the engine.
auto renderBoth = [](PitchEngine engine, int note) {
SampleData s = sineSample(600, 12.0, 60);
s.framesR = s.frames; // dual-mono: identical channels
s.play.pitchEngine = engine;
Keymap km = Keymap::singleSampleChromatic(std::move(s));
VoiceEngine eng(1, km, /*preserveCap=*/0, /*preserveWindowFrames=*/128);
eng.noteOn(note, 127);
std::vector<AudioSample> left(256, 0.f), right(256, 0.f);
eng.render(left.data(), right.data(), 256);
bool sound = false, equal = true;
for (std::size_t i = 0; i < left.size(); ++i) {
if (left[i] != 0.0f) sound = true;
if (left[i] != right[i]) equal = false; // EXACT: dual-mono must be centered
}
CHECK(sound); // the render actually produced signal (a 0==0 pass would be vacuous)
CHECK(equal);
};
renderBoth(PitchEngine::Varispeed, 60);
renderBoth(PitchEngine::Varispeed, 67); // off-root: repitch rides both channels equally
renderBoth(PitchEngine::Preserve, 60); // both shifters run (no unity demotion in MIDI)
renderBoth(PitchEngine::Preserve, 67); // off-root Preserve: per-channel shift, same state
}
static void testMonoRenderUnchangedByStereoData() {
// Regression: the mono render path (renderFrame) reads channel 0 ONLY and is byte-identical
// whether or not a second channel is present. A stereo sample rendered mono == its L channel.
@@ -1975,6 +2004,7 @@ int main() {
testChannelCount();
testStereoRenderKeepsChannelsDistinct();
testMonoSamplePlaysDualMonoInStereo();
testDualMonoStereoSampleRendersCentered();
testMonoRenderUnchangedByStereoData();
testStereoRenderAdvancesLikeMonoRepitch();
testStereoRenderSumsVoicesPerChannel();