169 lines
7.4 KiB
C++
169 lines
7.4 KiB
C++
// 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 <job.json> --in <input.wav> --out <output.wav> (offline)
|
|
// --realtime-fx <job.json> --shm <name> [--parent <pid>] (SHM realtime)
|
|
// --scan <dir>
|
|
// --open-fx-gui <job.json>
|
|
// --fx-gui <job.json>
|
|
// 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 <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;
|
|
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 <name>"
|
|
<< 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 <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"
|
|
" --realtime-fx <job.json> --shm <name> [--parent <pid>]\n"
|
|
" --scan <dir>\n"
|
|
" --open-fx-gui <job.json>\n"
|
|
" --fx-gui <job.json>" << std::endl;
|
|
return 1;
|
|
}
|