From cede51ae0ce834b3442a78c91e196f9bef343666 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Fri, 4 Sep 2026 17:34:54 +0700 Subject: [PATCH] M5.4: VST3 host conformance (Reaper-style) for FX GUI attach - queryInterface now answers FUnknown::iid + own iid (IComponentHandler / IPlugFrame) with addRef+this per SDK QUERY_INTERFACE contract; plugins that QI the host handler no longer get kNoInterface unconditionally. - setBusArrangements negotiation before activateBus: offer mono/stereo for <=2ch audio buses, keep wider arrangements; result logged, not fatal. Matches REAPER load path so Ozone-class editors see a valid arrangement on first attach. Local float regression: Ozone 11 editor opens OK. --- native_bridge/src/RenderFxJob.cpp | 56 +++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/native_bridge/src/RenderFxJob.cpp b/native_bridge/src/RenderFxJob.cpp index b9cf880..6f6e29c 100644 --- a/native_bridge/src/RenderFxJob.cpp +++ b/native_bridge/src/RenderFxJob.cpp @@ -25,6 +25,7 @@ #include "public.sdk/source/vst/vstpresetfile.h" #include "public.sdk/source/common/memorystream.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/vstspeaker.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstprocesscontext.h" @@ -355,7 +356,18 @@ using Steinberg::Vst::kSample32; class HostComponentHandlerFx : public IComponentHandler { public: - tresult queryInterface(const char*, void** v) override { *v = nullptr; return kNoInterface; } + tresult queryInterface(const char* iid, void** v) override { + // FUnknown contract: answer for our own iid (+FUnknown::iid) with + // addRef + this; kNoInterface otherwise (SDK QUERY_INTERFACE pattern). + if (Steinberg::FUnknownPrivate::iidEqual(iid, Steinberg::FUnknown::iid) || + Steinberg::FUnknownPrivate::iidEqual(iid, IComponentHandler::iid)) { + addRef(); + *v = this; + return kResultOk; + } + *v = nullptr; + return kNoInterface; + } Steinberg::uint32 addRef() override { return 1; } Steinberg::uint32 release() override { return 1; } tresult beginEdit(Steinberg::Vst::ParamID) override { return kResultOk; } @@ -377,7 +389,15 @@ using Steinberg::kPlatformTypeHWND; // Minimal IPlugFrame so the plugin can resize its editor view. class HostPlugFrameFx : public Steinberg::IPlugFrame { public: - tresult queryInterface(const char*, void** v) override { + tresult queryInterface(const char* iid, void** v) override { + // FUnknown contract: answer for our own iid (+FUnknown::iid) with + // addRef + this; kNoInterface otherwise (SDK QUERY_INTERFACE pattern). + if (Steinberg::FUnknownPrivate::iidEqual(iid, Steinberg::FUnknown::iid) || + Steinberg::FUnknownPrivate::iidEqual(iid, Steinberg::IPlugFrame::iid)) { + addRef(); + *v = this; + return kResultOk; + } *v = nullptr; return kNoInterface; } @@ -515,8 +535,38 @@ bool vst3FxLoadInner(Vst3FxState* s, const std::string& path, double sampleRate, std::cerr << "[RenderFx] STEP connection points OK" << std::endl; int32 numAudioInputs = component->getBusCount(kAudio, kInput); - for (int32 i = 0; i < numAudioInputs; ++i) component->activateBus(kAudio, kInput, i, true); int32 numAudioOutputs = component->getBusCount(kAudio, kOutput); + // Reaper-style negotiation BEFORE activation: offer mono/stereo for the + // <=2ch audio busses so plugins (Ozone 11 family) never see an empty or + // unset arrangement on their first GUI attach. Wider busses keep their + // current arrangement. Failure to negotiate is not fatal (log only). + { + FUnknownPtr proc(component); + if (proc) { + std::vector inArr, outArr; + for (int32 i = 0; i < numAudioInputs; ++i) { + Steinberg::Vst::SpeakerArrangement arr = 0; + proc->getBusArrangement(kInput, i, arr); + int32 ch = Steinberg::Vst::SpeakerArr::getChannelCount(arr); + inArr.push_back(ch == 1 ? Steinberg::Vst::SpeakerArr::kMono + : (ch == 2 ? Steinberg::Vst::SpeakerArr::kStereo : arr)); + } + for (int32 i = 0; i < numAudioOutputs; ++i) { + Steinberg::Vst::SpeakerArrangement arr = 0; + proc->getBusArrangement(kOutput, i, arr); + int32 ch = Steinberg::Vst::SpeakerArr::getChannelCount(arr); + outArr.push_back(ch == 1 ? Steinberg::Vst::SpeakerArr::kMono + : (ch == 2 ? Steinberg::Vst::SpeakerArr::kStereo : arr)); + } + tresult res = proc->setBusArrangements(inArr.data(), (int32)inArr.size(), + outArr.data(), (int32)outArr.size()); + std::cerr << "[RenderFx] STEP bus arrangements " + << (res == kResultOk || res == kResultTrue ? "OK" : "skip") + << " (ain=" << numAudioInputs << " aout=" << numAudioOutputs << ")" + << std::endl; + } + } + for (int32 i = 0; i < numAudioInputs; ++i) component->activateBus(kAudio, kInput, i, true); for (int32 i = 0; i < numAudioOutputs; ++i) component->activateBus(kAudio, kOutput, i, true); int32 numEventInputs = component->getBusCount(kEvent, kInput); for (int32 i = 0; i < numEventInputs; ++i) component->activateBus(kEvent, kInput, i, true);