Add VST2 hosting to native bridge; fix CC7 data2 routing verified via SHM test
VST2 (Steinberg 2.4 ABI) host via Vst2Instrument: aeffect.h, dispatcher, process events/replacing, kEffectStartProcess, headless GUI skip. - create_instrument returns Vst2Instrument for InstrumentType::VST2 - 64B-aligned scratch buffers for SIMD plugins; SEH crash capture module+stack - plugins.py: VST2 resolves .dll/.so; vst_engine.py reports VST2 type - app.jsx: derive instrumentType from scan (VST3 vs VST2), not hardcoded - Install exes refreshed from Release build SF2 FluidSynth audio verified end-to-end through SHM ring (energy 1.33): silence was a test-harness bug (CC7 value written to velocity field, bridge reads data2) — app sends data2 correctly. All temporary diagnostics removed.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <fluidsynth.h>
|
||||
#include <sfizz.hpp>
|
||||
#include "Vst3Instrument.h"
|
||||
#include "Vst2Instrument.h"
|
||||
#include "SandboxVst3Host.h"
|
||||
|
||||
// void* members keep fluid types out of the public header; cast here.
|
||||
@@ -173,8 +174,8 @@ std::unique_ptr<INativeInstrument> InstrumentEngineManager::create_instrument(In
|
||||
if (std::getenv("SF_SANDBOX_VST3"))
|
||||
return std::make_unique<SandboxVst3Host>();
|
||||
return std::make_unique<Vst3Instrument>();
|
||||
// ponytail: VST2 host (VST2.4 SDK, Steinberg discontinued) not implemented.
|
||||
case InstrumentType::VST2:
|
||||
return std::make_unique<Vst2Instrument>();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -196,6 +197,12 @@ bool InstrumentEngineManager::assign(uint32_t channel, InstrumentType type,
|
||||
else if (auto* s = dynamic_cast<SandboxVst3Host*>(inst.get()))
|
||||
loaded = s->loadPlugin(path, sampleRate, channel);
|
||||
}
|
||||
else if (type == InstrumentType::VST2) {
|
||||
if (auto* v2 = dynamic_cast<Vst2Instrument*>(inst.get())) {
|
||||
v2->setChannel(channel);
|
||||
loaded = v2->loadPlugin(path, sampleRate);
|
||||
}
|
||||
}
|
||||
if (type == InstrumentType::VST3) {
|
||||
if (auto* v = dynamic_cast<Vst3Instrument*>(inst.get()))
|
||||
v->setChannel(channel);
|
||||
@@ -394,16 +401,31 @@ void InstrumentEngineManager::allNotesOff() {
|
||||
}
|
||||
}
|
||||
|
||||
// Some commercial plugins (DUNE 3) use aligned SIMD loads/stores on their
|
||||
// audio buffers; plain std::vector is only 16B-aligned. Over-allocate and
|
||||
// align the pointer handed to plugins to 64B.
|
||||
static float* alignF(std::vector<float>& v, size_t n) {
|
||||
if (v.size() < n + 16) v.assign(n + 16, 0.0f);
|
||||
uintptr_t a = reinterpret_cast<uintptr_t>(v.data());
|
||||
return reinterpret_cast<float*>((a + 63) & ~(uintptr_t)63);
|
||||
}
|
||||
|
||||
// G3.3: SEH cannot live inside a function that needs C++ unwinding (C2712),
|
||||
// so per-channel process() runs in this helper. A plugin access violation
|
||||
// (0xC0000005) is caught here — the channel is muted instead of killing the
|
||||
// whole bridge process. /EHa (set in CMakeLists) allows mixing with the
|
||||
// outer C++ try/catch net in main.cpp.
|
||||
static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR, uint32_t n) {
|
||||
static DWORD g_sehCode = 0;
|
||||
static void* g_sehAddr = nullptr;
|
||||
static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR, uint32_t n, DWORD* outCode) {
|
||||
*outCode = 0;
|
||||
__try {
|
||||
inst->processAudioBlock(outL, outR, n);
|
||||
return true;
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
} __except (g_sehCode = GetExceptionCode(),
|
||||
g_sehAddr = GetExceptionInformation()->ExceptionRecord->ExceptionAddress,
|
||||
EXCEPTION_EXECUTE_HANDLER) {
|
||||
*outCode = g_sehCode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -418,27 +440,49 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t
|
||||
std::memset(outputL, 0, numSamples * sizeof(float));
|
||||
std::memset(outputR, 0, numSamples * sizeof(float));
|
||||
if (channels_.empty()) return;
|
||||
if (scratchL_.size() < numSamples) {
|
||||
scratchL_.resize(numSamples);
|
||||
scratchR_.resize(numSamples);
|
||||
}
|
||||
for (auto& [ch, inst] : channels_) {
|
||||
if (channelQuiet(ch)) continue; // editor open / crashed: do not process
|
||||
std::memset(scratchL_.data(), 0, numSamples * sizeof(float));
|
||||
std::memset(scratchR_.data(), 0, numSamples * sizeof(float));
|
||||
if (!SafeProcessChannel(inst.get(), scratchL_.data(), scratchR_.data(), numSamples)) {
|
||||
float* spL = alignF(scratchL_, numSamples);
|
||||
float* spR = alignF(scratchR_, numSamples);
|
||||
std::memset(spL, 0, numSamples * sizeof(float));
|
||||
std::memset(spR, 0, numSamples * sizeof(float));
|
||||
DWORD sehCode = 0;
|
||||
if (!SafeProcessChannel(inst.get(), spL, spR, numSamples, &sehCode)) {
|
||||
// G3.3: plugin fault — mute this channel only, keep the bridge up.
|
||||
if (!crashedCh_[ch]) {
|
||||
crashedCh_[ch] = true;
|
||||
std::cerr << "[NativeBridge] G3.3: channel " << ch
|
||||
<< " plugin crashed in processAudioBlock (SEH) — muted until reload"
|
||||
<< std::endl;
|
||||
{
|
||||
HMODULE m = nullptr;
|
||||
char mod[MAX_PATH] = "?";
|
||||
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCSTR)g_sehAddr, &m))
|
||||
GetModuleFileNameA(m, mod, MAX_PATH);
|
||||
std::cerr << "[NativeBridge] G3.3: channel " << ch
|
||||
<< " plugin crashed in processAudioBlock (SEH code=0x" << std::hex << sehCode
|
||||
<< " addr=0x" << (uintptr_t)g_sehAddr
|
||||
<< std::dec << " mod=" << mod
|
||||
<< ") — muted until reload"
|
||||
<< std::endl;
|
||||
void* bt[16];
|
||||
USHORT nf = RtlCaptureStackBackTrace(0, 16, bt, nullptr);
|
||||
std::cerr << "[NativeBridge] stack:";
|
||||
for (USHORT fi = 0; fi < nf; ++fi) {
|
||||
HMODULE bm = nullptr;
|
||||
char bmod[MAX_PATH] = "?";
|
||||
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCSTR)bt[fi], &bm))
|
||||
GetModuleFileNameA(bm, bmod, MAX_PATH);
|
||||
std::cerr << " " << std::hex << (uintptr_t)bt[fi] << std::dec
|
||||
<< "(" << bmod << ")";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (uint32_t i = 0; i < numSamples; ++i) {
|
||||
outputL[i] += scratchL_[i];
|
||||
outputR[i] += scratchR_[i];
|
||||
outputL[i] += spL[i];
|
||||
outputR[i] += spR[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user