fix(bridge): noteId match noteOn (0) for noteOff; create VST GUI window on main thread, attach on worker
This commit is contained in:
+95
-98
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
@@ -346,123 +347,119 @@ int main(int argc, char* argv[]) {
|
||||
playheadSamples = c.arg1;
|
||||
}
|
||||
} else if (c.type == 4) { // OPEN_GUI (A7): arg1 = parent HWND (0 → bridge tự tạo native window), arg2 = plugin id
|
||||
// Chay tren CUNG ChannelWorker da load instrument: thread rieng
|
||||
// cho openGUI lai tao COM apartment moi, con plugin thi song o
|
||||
// apartment cu da chet (load thread exit) -> Nexus attached()
|
||||
// hang (gui_probe: bridge_like treo, same_thread OK).
|
||||
uint32_t guiCh = c.channel;
|
||||
if (guiCh >= 16) guiCh = 0;
|
||||
// Bo poll 10s tren real-time loop (writeIndex stall > 3s -> Rust
|
||||
// tuong bridge chet va restart -> 2 bridge cung map SHM -> race).
|
||||
// LOAD (type=2) post truoc OPEN_GUI tren CUNG ChannelWorker (FIFO)
|
||||
// -> job openGUI chay sau khi LOAD xong -> kiem tra inst trong job.
|
||||
if (!workers[guiCh]) workers[guiCh] = std::make_unique<ChannelWorker>();
|
||||
workers[guiCh]->post([&instruments, guiCh, arg1 = c.arg1, arg2 = std::string(c.arg2)]() {
|
||||
void* hwnd = (void*)(uintptr_t)c.arg1;
|
||||
#ifdef _WIN32
|
||||
// Window PHAI thuoc MAIN thread (audio loop pump nay dispatch
|
||||
// messages cua no moi vong lap). Window tren worker + worker
|
||||
// khong pump trong luc job chay -> view->attached() treo
|
||||
// (gui_probe: two_workers_close TIMEOUT; same_thread /
|
||||
// two_instances_close — window tren main — OK). Tao/cap nhat
|
||||
// window ngay tai day tren main thread.
|
||||
HWND nativeHwnd = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
auto it = g_guiWindows.find(guiCh);
|
||||
if (it != g_guiWindows.end()) nativeHwnd = (HWND)it->second;
|
||||
}
|
||||
if (hwnd == 0) {
|
||||
if (nativeHwnd && IsWindow(nativeHwnd)) {
|
||||
hwnd = (void*)nativeHwnd;
|
||||
SetWindowTextA(nativeHwnd, std::string(c.arg2).c_str());
|
||||
ShowWindow(nativeHwnd, SW_SHOW);
|
||||
SetForegroundWindow(nativeHwnd);
|
||||
// Reuse: cap nhat USERDATA (channel+1) — inst CU da bi
|
||||
// thay the boi assign() -> WM_DESTROY sau nay lookup
|
||||
// inst MOI, khong dung con tro dangling.
|
||||
SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));
|
||||
} else {
|
||||
nativeHwnd = (HWND)create_native_vst_window(std::string(c.arg2).c_str());
|
||||
if (!nativeHwnd) {
|
||||
std::cerr << "[NativeBridge] GUI create window FAILED plugin=" << c.arg2 << std::endl;
|
||||
continue;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
g_guiWindows[guiCh] = nativeHwnd; // keep window alive
|
||||
g_hwndToCh[nativeHwnd] = guiCh; // WM_DESTROY cleanup
|
||||
}
|
||||
SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));
|
||||
hwnd = (void*)nativeHwnd;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
workers[guiCh]->post([&instruments, guiCh, hwnd, arg2 = std::string(c.arg2)]() {
|
||||
if (!instruments.get(guiCh)) {
|
||||
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << arg1
|
||||
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << " ch=" << guiCh << " (no instrument loaded)" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cerr << "[dbg] openGUI thread start hwnd=" << arg1
|
||||
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
||||
void* hwnd = (void*)(uintptr_t)arg1;
|
||||
std::cerr << "[dbg] openGUI thread start hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
||||
#ifdef _WIN32
|
||||
// Option B: chi 1 editor VST mo tai 1 thoi diem toan
|
||||
// bridge. Instance thu 2 cua CUNG plugin (Nexus) attach
|
||||
// view o apartment/worker khac -> treo (bridge.log: ch=2
|
||||
// ket o attached() khi ch=1 con mo) / crash (gui_probe
|
||||
// bridge_two). Dong editor cua channel khac TRUOC khi
|
||||
// attach: WM_CLOSE -> worker channel do destroy window ->
|
||||
// WM_DESTROY -> closeGUI() -> view->removed() tren dung
|
||||
// thread so huu (gui_probe two_instances_close: OK).
|
||||
// Option B: chi 1 editor VST mo tai 1 thoi diem toan
|
||||
// bridge. Instance thu 2 cua CUNG plugin (Nexus) attach
|
||||
// view o apartment/worker khac -> treo. Dong editor cua
|
||||
// channel khac TRUOC khi attach: WM_CLOSE -> main pump
|
||||
// (audio loop) destroy window -> WM_DESTROY -> closeGUI()
|
||||
// + xoa registry. Chay tren worker job de khong stall
|
||||
// writeIndex cua real-time loop.
|
||||
{
|
||||
std::vector<uint32_t> others;
|
||||
{
|
||||
std::vector<uint32_t> others;
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
for (const auto& kv : g_guiWindows)
|
||||
if (kv.first != guiCh) others.push_back(kv.first);
|
||||
}
|
||||
for (uint32_t y : others) {
|
||||
HWND yHwnd = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
for (const auto& kv : g_guiWindows)
|
||||
if (kv.first != guiCh) others.push_back(kv.first);
|
||||
auto it = g_guiWindows.find(y);
|
||||
if (it != g_guiWindows.end()) yHwnd = (HWND)it->second;
|
||||
}
|
||||
for (uint32_t y : others) {
|
||||
HWND yHwnd = nullptr;
|
||||
if (!yHwnd || !IsWindow(yHwnd)) continue;
|
||||
PostMessage(yHwnd, WM_CLOSE, 0, 0);
|
||||
std::cerr << "[dbg] openGUI: closing editor ch=" << y
|
||||
<< " before attach ch=" << guiCh << std::endl;
|
||||
bool closed = false;
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
auto it = g_guiWindows.find(y);
|
||||
if (it != g_guiWindows.end()) yHwnd = (HWND)it->second;
|
||||
if (g_guiWindows.find(y) == g_guiWindows.end()) { closed = true; break; }
|
||||
}
|
||||
if (!yHwnd || !IsWindow(yHwnd)) continue;
|
||||
PostMessage(yHwnd, WM_CLOSE, 0, 0);
|
||||
std::cerr << "[dbg] openGUI: closing editor ch=" << y
|
||||
<< " before attach ch=" << guiCh << std::endl;
|
||||
// Cho worker channel y xu ly WM_CLOSE -> destroy ->
|
||||
// WM_DESTROY (xoa registry). Timeout 5s tranh
|
||||
// worker ket (load lau / plugin treo).
|
||||
bool closed = false;
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
if (g_guiWindows.find(y) == g_guiWindows.end()) { closed = true; break; }
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
if (!closed)
|
||||
std::cerr << "[dbg] openGUI: editor ch=" << y
|
||||
<< " not closed in 5s, proceeding" << std::endl;
|
||||
}
|
||||
}
|
||||
if (arg1 == 0) {
|
||||
HWND existingHwnd = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
auto it = g_guiWindows.find(guiCh);
|
||||
if (it != g_guiWindows.end()) existingHwnd = (HWND)it->second;
|
||||
}
|
||||
if (existingHwnd && IsWindow(existingHwnd)) {
|
||||
hwnd = existingHwnd;
|
||||
SetWindowTextA((HWND)hwnd, arg2.c_str());
|
||||
ShowWindow((HWND)hwnd, SW_SHOW);
|
||||
SetForegroundWindow((HWND)hwnd);
|
||||
// Reuse: cap nhat USERDATA (channel+1) — inst CU
|
||||
// da bi thay the boi assign() -> WM_DESTROY sau
|
||||
// nay lookup inst MOI, khong dung con tro dangling.
|
||||
SetWindowLongPtrA((HWND)hwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));
|
||||
} else {
|
||||
hwnd = create_native_vst_window(arg2.c_str());
|
||||
if (!hwnd) {
|
||||
std::cerr << "[NativeBridge] GUI create window FAILED plugin=" << arg2 << std::endl;
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||
g_guiWindows[guiCh] = hwnd; // keep window alive
|
||||
g_hwndToCh[(HWND)hwnd] = guiCh; // WM_DESTROY cleanup
|
||||
}
|
||||
SetWindowLongPtrA((HWND)hwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));
|
||||
Sleep(10);
|
||||
}
|
||||
if (!closed)
|
||||
std::cerr << "[dbg] openGUI: editor ch=" << y
|
||||
<< " not closed in 5s, proceeding" << std::endl;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)0;
|
||||
(void)0;
|
||||
#endif
|
||||
if (auto* inst = instruments.get(guiCh)) {
|
||||
// Reopen sau khi dong: openGUI() rebuild lai plugin
|
||||
// instance (reload = terminate + loadPlugin) trong
|
||||
// khi audio loop co the dang processAudioBlock tren
|
||||
// CUNG object -> UAF/hang. Chan bang flag reloading_
|
||||
// (set/clear duoi engine mutex; renderAll giu mutex
|
||||
// khi process nen check luon nhat quan).
|
||||
bool guard = inst->needsReload();
|
||||
if (guard) instruments.setReloading(guiCh, true);
|
||||
bool ok = inst->openGUI(hwnd);
|
||||
if (guard) instruments.setReloading(guiCh, false);
|
||||
if (ok)
|
||||
std::cout << "[NativeBridge] GUI attached hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
||||
else
|
||||
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << std::endl;
|
||||
}
|
||||
// Editor windows song tren thread nay — ChannelWorker
|
||||
// pump message queue khi idle (xem class comment).
|
||||
});
|
||||
if (auto* inst = instruments.get(guiCh)) {
|
||||
// Reopen sau khi dong: reload() (terminate + loadPlugin)
|
||||
// PHAI chay tren worker thread nay — COM STA apartment
|
||||
// cua channel song o day. view->attached() cung chay o
|
||||
// day; window thuoc main thread nen main pump (audio
|
||||
// loop) dispatch messages cua no — khong can pump worker.
|
||||
// Chan UAF bang flag reloading_ (set/clear duoi engine
|
||||
// mutex; renderAll giu mutex khi process).
|
||||
bool guard = inst->needsReload();
|
||||
if (guard) instruments.setReloading(guiCh, true);
|
||||
bool ok = inst->reloadForGUI() && inst->attachView(hwnd);
|
||||
if (guard) instruments.setReloading(guiCh, false);
|
||||
if (ok)
|
||||
std::cout << "[NativeBridge] GUI attached hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
||||
else
|
||||
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd
|
||||
<< " plugin=" << arg2 << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
shmIPC->controlQueueCount = 0;
|
||||
|
||||
Reference in New Issue
Block a user