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:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user