fix(bridge): VST2 host dung ABI SDK — crash outs>2, NaN input, editOpen(NULL), opcodes; docs A14 MIDI-out + phan tich Ozone -Inf

- aeffect.h: bo sung opcodes 4-22, 53, 55 + audioMaster 15/41-44 + ERect (dung chuan VST2.4 SDK, DUNE load OK khong crash)
- Vst2Instrument: cap du pointer arrays theo numInputs/numOutputs (fix AV 0xC0000005 Qin outs=32), input fill 0.0f (het NaN poison effect), bo probe effEditOpen(NULL) (crash WaveObserver), GetDirectory + SizeWindow hook, canDo sendVstEvents/sendVstTimeInfo
- TASKS.md: A14 MIDI-out tu plugin (Scaler2/Instacomposer/MelodicFlow xuat MIDI khong phai audio)
- PLAN_MASTERBUS_FX_RACK_VST.md: phan tich Ozone -Inf — chain day du, nguyen nhan = audio DAW khong chay qua bridge master ring
This commit is contained in:
2026-08-19 20:57:16 +07:00
parent 927597a822
commit 6efef85e47
9 changed files with 714 additions and 73 deletions
+58 -4
View File
@@ -23,6 +23,9 @@
#include <iostream>
#include <thread>
#include <chrono>
#ifdef _WIN32
#include <malloc.h> // _heapchk (TEMP diag)
#endif
// -----------------------------------------------------------------
// 1. SOUNDFONT ENGINE (.SF2 / .SF3) VIA FLUIDSYNTH C API
@@ -108,7 +111,7 @@ void FluidSynthInstrument::programChange(uint32_t channel, uint32_t program) {
void FluidSynthInstrument::pitchBend(uint32_t channel, uint32_t bend14) {
if (!synth) return;
// fluid_synth_pitch_bend takes the raw 14-bit value (center 8192).
// fluid_synth_pitch_bend takes the raw 14-bit value (center 2097152).
fluid_synth_pitch_bend(FS_SYNTH, channel, bend14);
}
@@ -435,7 +438,10 @@ void InstrumentEngineManager::allNotesOff() {
// 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);
// 64B-aligned pointer can sit up to 63B into the allocation; TEMP diag:
// huge tail so we can measure how far DUNE writes past n.
const size_t kTail = 2097152 + 64;
if (v.size() < n + kTail) v.assign(n + kTail, 0.0f);
uintptr_t a = reinterpret_cast<uintptr_t>(v.data());
return reinterpret_cast<float*>((a + 63) & ~(uintptr_t)63);
}
@@ -457,6 +463,16 @@ static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR
return true;
} __except (g_sehCode = GetExceptionCode(),
g_sehAddr = GetExceptionInformation()->ExceptionRecord->ExceptionAddress,
std::cerr << "[NativeBridge][G3.3][diag] SEH code=0x" << std::hex
<< g_sehCode << " rip=0x" << (uintptr_t)g_sehAddr
<< " fault=0x" << std::hex
<< (GetExceptionInformation()->ExceptionRecord->NumberParameters > 1
? GetExceptionInformation()->ExceptionRecord->ExceptionInformation[1]
: 0)
<< " isWrite=" << (GetExceptionInformation()->ExceptionRecord->NumberParameters > 0
? GetExceptionInformation()->ExceptionRecord->ExceptionInformation[0]
: 0)
<< std::dec << std::endl,
EXCEPTION_EXECUTE_HANDLER) {
*outCode = g_sehCode;
return false;
@@ -546,8 +562,17 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t
if (channelQuiet(ch)) continue; // editor open / crashed: do not process
float* spL = alignF(scratchL_, numSamples);
float* spR = alignF(scratchR_, numSamples);
std::memset(spL, 0, numSamples * sizeof(float));
std::memset(spR, 0, numSamples * sizeof(float));
std::cerr << "[NativeBridge][diag] renderAll ch=" << ch << " n=" << numSamples
<< " spL=0x" << std::hex << (uintptr_t)spL
<< " spR=0x" << (uintptr_t)spR
<< " capL=" << std::dec << scratchL_.size()
<< " capR=" << scratchR_.size() << std::endl;
// TEMP diag: fill canary with a NaN pattern so writes of 0.0 (silence)
// are still detected.
for (uint32_t i = 0; i < numSamples + 2097152; ++i) {
spL[i] = 0x7FC00000u; // quiet NaN
spR[i] = 0x7FC00000u;
}
#ifdef _WIN32
DWORD sehCode = 0;
#else
@@ -592,6 +617,28 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t
}
continue;
}
// TEMP diag: plugin output-overrun canary + heap health.
{
static uint64_t diagBlock = 0; ++diagBlock;
int farL = -1, farR = -1;
for (uint32_t i = 0; i < 2097152; ++i) {
uint32_t bL, bR;
std::memcpy(&bL, &spL[numSamples + i], 4);
std::memcpy(&bR, &spR[numSamples + i], 4);
if (bL != 0x7FC00000u) farL = (int)i;
if (bR != 0x7FC00000u) farR = (int)i;
}
if (farL >= 0 || farR >= 0)
std::cerr << "[NativeBridge][diag] block " << diagBlock
<< " OUTPUT OVERRUN past n: L=" << farL
<< " R=" << farR << " (ch=" << ch << ")" << std::endl;
#ifdef _WIN32
int h = _heapchk();
if (h != _HEAPOK)
std::cerr << "[NativeBridge][diag] block " << diagBlock
<< " heap NOT OK code=" << h << std::endl;
#endif
}
// Auto-normalize: makeup calibrated (constant per-channel gain, no steps).
auto mit = makeup_.find(ch);
const float g = (mit != makeup_.end()) ? mit->second : 1.0f;
@@ -620,3 +667,10 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t
outputR[i] *= limiterGain_;
}
}
void InstrumentEngineManager::guiIdleAll() {
std::lock_guard<std::mutex> lock(mu_);
for (auto& kv : channels_) {
kv.second->guiIdle();
}
}