// 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. Modes: // --render-fx --in --out (offline) // --realtime-fx --shm [--parent ] (SHM realtime) // --scan // --open-fx-gui // --fx-gui // SHM realtime loop (--realtime-fx): cloud-FX loop cho VST3 trong master FX // chain — browser gửi mix qua engine → SHM input ring → bridge xử lý (cùng // RealtimeFxChain như daw_vst_bridge master chain) → SHM output ring → // engine → browser. Xem FxRealtimeIPC.h + RealtimeFxLoop.cpp. #include "RenderFxJob.h" #ifdef _WIN32 #include #include #include #include #include #else #include #endif #include #include #include #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; if (er) { HMODULE hMod = nullptr; DWORD_PTR crashAddr = (DWORD_PTR)er->ExceptionAddress; if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)er->ExceptionAddress, &hMod) && hMod) { char modPath[MAX_PATH]; if (GetModuleFileNameA(hMod, modPath, MAX_PATH)) std::cerr << "[CRASH] in " << modPath << "+0x" << std::hex << (crashAddr - (DWORD_PTR)hMod) << 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); std::cerr << "[CRASH] minidump: " << dumpPath << std::endl; } } } } 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); // Suppress Windows loader error dialogs (plugin dependency thiếu -> 0x7E // phải fail âm thầm, không MessageBox chặn user; child kế thừa error mode). SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); 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; std::string realtimeFxJob, realtimeShm; uint32_t realtimeParent = 0; 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]; else if (std::strcmp(argv[i], "--realtime-fx") == 0 && i + 1 < argc) realtimeFxJob = argv[i + 1]; else if (std::strcmp(argv[i], "--shm") == 0 && i + 1 < argc) realtimeShm = argv[i + 1]; else if (std::strcmp(argv[i], "--parent") == 0 && i + 1 < argc) realtimeParent = (uint32_t)std::strtoul(argv[i + 1], nullptr, 10); } if (!realtimeFxJob.empty()) { if (realtimeShm.empty()) { std::cerr << "[RealtimeFxLoop] --realtime-fx requires --shm " << std::endl; return 1; } return run_realtime_fx_loop(realtimeFxJob, realtimeShm, realtimeParent); } if (!renderFxJob.empty()) { if (renderFxIn.empty() || renderFxOut.empty()) { std::cerr << "[RenderFxJob] --render-fx requires --in " " --out " << 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 --in --out \n" " --realtime-fx --shm [--parent ]\n" " --scan \n" " --open-fx-gui \n" " --fx-gui " << std::endl; return 1; }