refactor(bridge): split fx bridge, stabilize VST3 host chain, drop old install engines

- remove diagnostic canaries from audio hot path (crackle fix) and
  stuck C5 note raw-MIDI test (Vst2Instrument::init noteOff)
- split native bridge into daw_vst_bridge (VSTi) + fx_vst_bridge (VST FX)
- VST3: processData prepare before setActive, zero input buses, accept
  both .vstpreset and bridge-state blob in RenderJob presets
- native_render/vst_engine: bridge-exe discovery + render_fx_chain + /fx-gui
- delete old install/ engine binaries (install via sidecar+resources now)
- add BUILD_LAYOUT.md, build_selfcheck, tools/ diagnostics
This commit is contained in:
2026-08-20 09:42:42 +07:00
parent 5df96aef1e
commit 7c22ae1f02
42 changed files with 2363 additions and 210 deletions
+148
View File
@@ -0,0 +1,148 @@
// native_bridge/src/main_fx.cpp
// Entry point of fx_vst_bridge.exe — VST FX host bridge (task: split FX out
// of daw_vst_bridge). Windows only. One-shot modes only:
// --render-fx <job.json> --in <input.wav> --out <output.wav>
// --scan <dir>
// --open-fx-gui <job.json>
// --fx-gui <job.json>
// No SHM realtime loop: the realtime master FX chain (control type 6) stays
// in daw_vst_bridge because it processes the live instrument mix in-process.
#include "RenderFxJob.h"
#ifdef _WIN32
#include <windows.h>
#include <imm.h>
#include <dbghelp.h>
#include <cstdio>
#include <cstdlib>
#else
#include <cstdlib>
#endif
#include <cstring>
#include <iostream>
#include <string>
#ifdef _WIN32
// Minidump on crash -> %TEMP%\sf_dumps (same convention as daw_vst_bridge).
static LONG WINAPI FxCrashDumpHandler(EXCEPTION_POINTERS* ep) {
static LONG once = 0;
if (InterlockedCompareExchange(&once, 1, 0)) return EXCEPTION_CONTINUE_SEARCH;
PEXCEPTION_RECORD er = ep ? ep->ExceptionRecord : nullptr;
std::cerr << "[CRASH] fx_vst_bridge exception code=0x" << std::hex
<< (er ? er->ExceptionCode : 0) << " addr=0x"
<< (er ? (void*)er->ExceptionAddress : nullptr) << std::dec << std::endl;
char dumpDir[MAX_PATH];
if (GetTempPathA(MAX_PATH, dumpDir)) {
std::strncat(dumpDir, "sf_dumps", MAX_PATH - std::strlen(dumpDir) - 1);
CreateDirectoryA(dumpDir, nullptr);
char dumpPath[MAX_PATH];
std::snprintf(dumpPath, sizeof(dumpPath), "%s/fx_bridge_crash_%lu.dmp",
dumpDir, GetCurrentProcessId());
HMODULE hDbg = LoadLibraryA("dbghelp.dll");
if (hDbg) {
typedef BOOL(WINAPI* MiniDumpWriteDumpFn)(
HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION,
PMINIDUMP_CALLBACK_INFORMATION);
auto pMiniDumpWriteDump =
(MiniDumpWriteDumpFn)GetProcAddress(hDbg, "MiniDumpWriteDump");
if (pMiniDumpWriteDump) {
HANDLE hFile = CreateFileA(dumpPath, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
MINIDUMP_EXCEPTION_INFORMATION mei = {};
mei.ThreadId = GetCurrentThreadId();
mei.ExceptionPointers = ep;
mei.ClientPointers = TRUE;
pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hFile, MiniDumpNormal, &mei, nullptr,
nullptr);
CloseHandle(hFile);
}
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
static LONG WINAPI FxVectoredCrashHandler(PEXCEPTION_POINTERS ep) {
PEXCEPTION_RECORD er = ep ? ep->ExceptionRecord : nullptr;
DWORD code = er ? er->ExceptionCode : 0;
if (code != 0xC0000374 && code != 0xC0000005 && code != 0xC0000409)
return EXCEPTION_CONTINUE_SEARCH;
return FxCrashDumpHandler(ep);
}
// Same daemon init as daw_vst_bridge main(): no console flash, no power
// throttling, per-monitor DPI (plugin renderers must not be OS-scaled),
// no default IME on the main thread (IME recursion fix). Redirected stdio
// (bridge.log pipe / SF_FXGUI_PORT reader) survives FreeConsole.
static void win_daemon_init() {
if (!std::getenv("SF_KEEP_CONSOLE")) FreeConsole();
#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x00000004
#endif
#ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED
#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x00000001
#endif
PROCESS_POWER_THROTTLING_STATE powerState = {};
powerState.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
powerState.ControlMask = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION |
PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
powerState.StateMask = 0;
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling,
&powerState, sizeof(powerState));
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
ImmAssociateContextEx(nullptr, nullptr, IACE_DEFAULT);
}
#endif // _WIN32
int main(int argc, char* argv[]) {
#ifdef _WIN32
SetUnhandledExceptionFilter(FxCrashDumpHandler);
AddVectoredExceptionHandler(1, FxVectoredCrashHandler);
win_daemon_init();
#endif
// stderr, not stdout: stdout is reserved for machine-readable output in
// one-shot modes (--scan JSON, --fx-gui "SF_FXGUI_PORT=...").
std::cerr << "[FxBridge] Starting DAW FX Host Bridge..." << std::endl;
std::string renderFxJob, renderFxIn, renderFxOut;
std::string scanDir;
std::string openFxGuiJob;
std::string fxGuiServerJob;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--render-fx") == 0 && i + 1 < argc)
renderFxJob = argv[i + 1];
else if (std::strcmp(argv[i], "--in") == 0 && i + 1 < argc)
renderFxIn = argv[i + 1];
else if (std::strcmp(argv[i], "--out") == 0 && i + 1 < argc)
renderFxOut = argv[i + 1];
else if (std::strcmp(argv[i], "--scan") == 0 && i + 1 < argc)
scanDir = argv[i + 1];
else if (std::strcmp(argv[i], "--open-fx-gui") == 0 && i + 1 < argc)
openFxGuiJob = argv[i + 1];
else if (std::strcmp(argv[i], "--fx-gui") == 0 && i + 1 < argc)
fxGuiServerJob = argv[i + 1];
}
if (!renderFxJob.empty()) {
if (renderFxIn.empty() || renderFxOut.empty()) {
std::cerr << "[RenderFxJob] --render-fx requires --in <input.wav>"
" --out <output.wav>" << std::endl;
return 1;
}
return run_render_fx_job(renderFxJob, renderFxIn, renderFxOut);
}
if (!scanDir.empty()) return run_scan_dir(scanDir);
if (!openFxGuiJob.empty()) return run_open_fx_gui(openFxGuiJob);
if (!fxGuiServerJob.empty()) return run_fx_gui_server(fxGuiServerJob);
std::cerr << "fx_vst_bridge: no mode. Usage:\n"
" --render-fx <job.json> --in <input.wav> --out <output.wav>\n"
" --scan <dir>\n"
" --open-fx-gui <job.json>\n"
" --fx-gui <job.json>" << std::endl;
return 1;
}