fix(scan): fx_vst_bridge --scan timeout per-module 30s + emit UNKNOWN thay vi is_instrument=true

- RenderFxJob: safeScanModule chay tren worker thread + TerminateThread neu
  Module::create treo >30s (Ozone 11 Clarity treo vinh vien, SEH khong bat
  hang) -> scan khong bao gio ket thuc -> list_fx() timeout -> fx_paths rong
  -> filter /available vo hieu -> nut Synth hien ca VST FX
- Module crash/hang -> emit is_fx=false,is_instrument=false (UNKNOWN) thay vi
  is_instrument=true (truoc day xep VST FX crash vao danh sach instrument)
- vst_engine: list_scan_all() tra toan bo (FX+instrument+unknown); list_fx()
  loc is_fx tu cache chung
- plugins.py /available: loai ca fx_paths lan unknown_paths khoi
  vst_instruments -> nut Synth chi hien VSTi that
This commit is contained in:
2026-08-22 01:53:21 +00:00
parent 1a8b210063
commit dd2a320b2a
3 changed files with 88 additions and 25 deletions
+39 -6
View File
@@ -1479,13 +1479,43 @@ bool scanModuleInner(const std::string& path, std::vector<ScanEntry>& out) {
return added > 0;
}
// Per-module scan context for the timeout worker thread.
struct ScanCtx {
const std::string* path;
std::vector<ScanEntry>* out;
bool ok = false;
};
static DWORD WINAPI scanWorker(LPVOID p) {
auto* ctx = static_cast<ScanCtx*>(p);
__try { ctx->ok = scanModuleInner(*ctx->path, *ctx->out); }
__except (EXCEPTION_EXECUTE_HANDLER) {
std::cerr << "[Scan] skip (crashed module): " << *ctx->path << std::endl;
ctx->ok = false;
}
return 0;
}
bool safeScanModule(const std::string& path, std::vector<ScanEntry>& out) {
#ifdef _WIN32
__try { return scanModuleInner(path, out); }
__except (EXCEPTION_EXECUTE_HANDLER) {
std::cerr << "[Scan] skip (crashed module): " << path << std::endl;
// Run the VST3 loader on a dedicated thread so a HANGING module (e.g.
// Ozone 11 Clarity.vst3 — Module::create blocks forever inside the SDK)
// cannot stall the whole --scan. SEH only catches crashes, not hangs, so
// a watchdog timeout + TerminateThread is required. Safe here: --scan is
// a one-shot process; the worker only touches the VST3 SDK loader and the
// process exits right after the scan completes.
ScanCtx ctx{&path, &out};
HANDLE hThread = CreateThread(nullptr, 0, scanWorker, &ctx, 0, nullptr);
if (!hThread) { std::cerr << "[Scan] skip (thread fail): " << path << std::endl; return false; }
const DWORD timeoutMs = 30000; // 30s/module — generous for big VST3s
DWORD wait = WaitForSingleObject(hThread, timeoutMs);
if (wait == WAIT_TIMEOUT) {
TerminateThread(hThread, 0);
CloseHandle(hThread);
std::cerr << "[Scan] skip (timeout 30s): " << path << std::endl;
return false;
}
CloseHandle(hThread);
return ctx.ok;
#else
return scanModuleInner(path, out);
#endif
@@ -1541,9 +1571,12 @@ int run_scan_dir(const std::string& dirPath) {
for (const auto& p : vst3Paths) {
std::vector<ScanEntry> entries;
if (!safeScanModule(p, entries)) {
// Module unreadable (or crashed): report as opaque instrument-like
// entry so the UI still lists it (VST2 default behavior).
emit(p, p.substr(p.find_last_of("/\\") + 1), false, true, "VST3");
// Module unreadable/crashed/hung (e.g. Ozone 11 Clarity): emit
// entry UNKNOWN (is_fx=false, is_instrument=false) — trước đây
// emit is_instrument=true làm VST FX lọt vào danh sách instrument
// (nút Synth). Backend loại entry unknown khỏi vst_instruments:
// không xác định được loại → không xếp bừa vào instrument.
emit(p, p.substr(p.find_last_of("/\\") + 1), false, false, "VST3");
continue;
}
for (const auto& e : entries) emit(e.path, e.name, e.isFx, e.isInstrument, "VST3");