fix(bridge): noteId match noteOn (0) for noteOff; create VST GUI window on main thread, attach on worker
This commit is contained in:
@@ -41,6 +41,11 @@ public:
|
|||||||
// inside processAudioBlock on the same instance).
|
// inside processAudioBlock on the same instance).
|
||||||
virtual bool needsReload() const { return false; }
|
virtual bool needsReload() const { return false; }
|
||||||
virtual void setReloading(bool /*on*/) {}
|
virtual void setReloading(bool /*on*/) {}
|
||||||
|
// VST3: split openGUI so instance teardown/reload runs on the owning
|
||||||
|
// apartment thread while view->attached() may run on a temporary thread
|
||||||
|
// with the channel worker pumping messages (see bridge main.cpp).
|
||||||
|
virtual bool reloadForGUI() { return true; }
|
||||||
|
virtual bool attachView(void* /*parentWindowHandle*/) { return false; }
|
||||||
|
|
||||||
// Real-time Audio PCM Float32 rendering loop
|
// Real-time Audio PCM Float32 rendering loop
|
||||||
virtual void processAudioBlock(float* outputL, float* outputR, uint32_t numSamples) = 0;
|
virtual void processAudioBlock(float* outputL, float* outputR, uint32_t numSamples) = 0;
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public:
|
|||||||
void programChange(uint32_t channel, uint32_t program) override;
|
void programChange(uint32_t channel, uint32_t program) override;
|
||||||
void pitchBend(uint32_t channel, uint32_t bend14) override;
|
void pitchBend(uint32_t channel, uint32_t bend14) override;
|
||||||
bool openGUI(void* parentWindowHandle) override;
|
bool openGUI(void* parentWindowHandle) override;
|
||||||
|
bool reloadForGUI() override;
|
||||||
|
bool attachView(void* parentWindowHandle) override;
|
||||||
void closeGUI() override;
|
void closeGUI() override;
|
||||||
bool needsReload() const override { return hasAttachedOnce_ && !guiAttached_; }
|
bool needsReload() const override { return hasAttachedOnce_ && !guiAttached_; }
|
||||||
void setReloading(bool on) override { reloading_ = on; }
|
void setReloading(bool on) override { reloading_ = on; }
|
||||||
|
|||||||
@@ -424,7 +424,10 @@ void Vst3Instrument::noteOff(uint32_t channel, uint32_t pitch, uint32_t sampleOf
|
|||||||
e.noteOff.channel = 0; // Force to MIDI Channel 1 (0) for VSTi compatibility
|
e.noteOff.channel = 0; // Force to MIDI Channel 1 (0) for VSTi compatibility
|
||||||
e.noteOff.pitch = (int16)pitch;
|
e.noteOff.pitch = (int16)pitch;
|
||||||
e.noteOff.velocity = 0.f;
|
e.noteOff.velocity = 0.f;
|
||||||
e.noteOff.noteId = -1;
|
// noteId phai MATCH noteOn (0): Nexus/DUNE3 track notes bang id, -1
|
||||||
|
// khong khop voi note-on -> plugin giu note vo han (am thanh khong dung
|
||||||
|
// cho den khi kill bridge).
|
||||||
|
e.noteOff.noteId = 0;
|
||||||
e.noteOff.tuning = 0.f;
|
e.noteOff.tuning = 0.f;
|
||||||
s->eventList.addEvent(e);
|
s->eventList.addEvent(e);
|
||||||
#endif
|
#endif
|
||||||
@@ -485,21 +488,44 @@ bool Vst3Instrument::openGUI(void* parentWindowHandle) {
|
|||||||
#ifndef HAVE_VST3SDK
|
#ifndef HAVE_VST3SDK
|
||||||
(void)parentWindowHandle;
|
(void)parentWindowHandle;
|
||||||
return false;
|
return false;
|
||||||
|
#else
|
||||||
|
if (!reloadForGUI()) return false;
|
||||||
|
return attachView(parentWindowHandle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vst3Instrument::reloadForGUI() {
|
||||||
|
#ifndef HAVE_VST3SDK
|
||||||
|
return false;
|
||||||
#else
|
#else
|
||||||
auto* s = static_cast<Vst3HostState*>(state_);
|
auto* s = static_cast<Vst3HostState*>(state_);
|
||||||
std::cerr << "[dbg] openGUI hwnd=" << (void*)(uintptr_t)parentWindowHandle
|
if (!s || !s->controller) return false;
|
||||||
<< " controller=" << (s ? (s->controller ? 1 : 0) : -1) << std::endl;
|
|
||||||
if (!s || !s->controller || !parentWindowHandle) return false;
|
|
||||||
if (s->view && guiAttached_) return true;
|
if (s->view && guiAttached_) return true;
|
||||||
if (hasAttachedOnce_ && !guiAttached_) {
|
if (hasAttachedOnce_ && !guiAttached_) {
|
||||||
// Reopen GUI sau khi dong: mot so plugin (Nexus...) hang o
|
// Reopen GUI sau khi dong: mot so plugin (Nexus...) hang o
|
||||||
// view->attached() LAN 2 tren cung component instance. Tao lai inst
|
// view->attached() LAN 2 tren cung component instance. Tao lai inst
|
||||||
// moi tren CUNG worker thread (COM STA con song) roi attach lai.
|
// moi tren CUNG worker thread (COM STA con song) roi attach lai.
|
||||||
|
// PHAN NAY PHAI chay tren worker thread — apartment cua channel song
|
||||||
|
// o day; chay tren thread tam thi apartment moi chet ngay sau do
|
||||||
|
// (reopen lan sau -> treo nhu bridge_like).
|
||||||
std::cerr << "[dbg] openGUI: re-attach - reloading fresh plugin instance" << std::endl;
|
std::cerr << "[dbg] openGUI: re-attach - reloading fresh plugin instance" << std::endl;
|
||||||
if (!reload()) return false;
|
if (!reload()) return false;
|
||||||
s = static_cast<Vst3HostState*>(state_);
|
s = static_cast<Vst3HostState*>(state_);
|
||||||
if (!s || !s->controller) return false;
|
if (!s || !s->controller) return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vst3Instrument::attachView(void* parentWindowHandle) {
|
||||||
|
#ifndef HAVE_VST3SDK
|
||||||
|
(void)parentWindowHandle;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
auto* s = static_cast<Vst3HostState*>(state_);
|
||||||
|
std::cerr << "[dbg] openGUI hwnd=" << (void*)(uintptr_t)parentWindowHandle
|
||||||
|
<< " controller=" << (s ? (s->controller ? 1 : 0) : -1) << std::endl;
|
||||||
|
if (!s || !s->controller || !parentWindowHandle) return false;
|
||||||
IPlugView* rawView = nullptr;
|
IPlugView* rawView = nullptr;
|
||||||
tresult qi = s->controller->queryInterface(IPlugView::iid, (void**)&rawView);
|
tresult qi = s->controller->queryInterface(IPlugView::iid, (void**)&rawView);
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public:
|
|||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
|
printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
|
||||||
"variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker\n");
|
"variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker | two_workers_close\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
std::string path = argv[1];
|
std::string path = argv[1];
|
||||||
@@ -315,6 +315,99 @@ int main(int argc, char* argv[]) {
|
|||||||
aDone.load() ? 1 : 0, aOk.load() ? 1 : 0,
|
aDone.load() ? 1 : 0, aOk.load() ? 1 : 0,
|
||||||
bDone.load() ? 1 : 0, bOk.load() ? 1 : 0);
|
bDone.load() ? 1 : 0, bOk.load() ? 1 : 0);
|
||||||
return (aDone.load() && aOk.load() && bDone.load() && bOk.load()) ? 0 : 3;
|
return (aDone.load() && aOk.load() && bDone.load() && bOk.load()) ? 0 : 3;
|
||||||
|
} else if (variant == "two_workers_close") {
|
||||||
|
// Round-4 exact bridge pattern: per-channel workers (own STA
|
||||||
|
// apartments); attach via temp thread + WORKER nested pump (the bridge
|
||||||
|
// main.cpp job pumps the worker queue while attachView runs on a
|
||||||
|
// temporary thread); close editor inst1 before attaching inst2 on its
|
||||||
|
// own worker.
|
||||||
|
ProbeWorker wa, wb;
|
||||||
|
wa.start();
|
||||||
|
wb.start();
|
||||||
|
std::atomic<bool> aDone{false}, aOk{false}, bDone{false}, bOk{false};
|
||||||
|
wa.post([&]() {
|
||||||
|
printf("[two_workers_close] workerA load inst1\n");
|
||||||
|
fflush(stdout);
|
||||||
|
bool r = inst.loadPlugin(path, 44100.0);
|
||||||
|
printf("[two_workers_close] workerA load inst1=%d\n", r ? 1 : 0);
|
||||||
|
fflush(stdout);
|
||||||
|
if (r) {
|
||||||
|
HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeA", WS_OVERLAPPEDWINDOW,
|
||||||
|
0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
||||||
|
printf("[two_workers_close] workerA hwnd=%p\n", (void*)h);
|
||||||
|
fflush(stdout);
|
||||||
|
bool r2 = false;
|
||||||
|
std::atomic<bool> d{false};
|
||||||
|
std::thread t([&]() {
|
||||||
|
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
|
r2 = inst.attachView(h);
|
||||||
|
CoUninitialize();
|
||||||
|
d = true;
|
||||||
|
});
|
||||||
|
while (!d.load()) {
|
||||||
|
MSG msg;
|
||||||
|
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
t.join();
|
||||||
|
printf("[two_workers_close] workerA attach inst1=%d\n", r2 ? 1 : 0);
|
||||||
|
fflush(stdout);
|
||||||
|
aOk = r2;
|
||||||
|
inst.closeGUI(); // view->removed() on worker A
|
||||||
|
printf("[two_workers_close] workerA closeGUI inst1\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
aDone = true;
|
||||||
|
});
|
||||||
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
|
auto elapsed = [&]() {
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::steady_clock::now() - t0).count();
|
||||||
|
};
|
||||||
|
while (!aDone.load() && elapsed() < secs) Sleep(50);
|
||||||
|
if (!aDone.load()) { printf("RESULT: TIMEOUT inst1 (aDone=0)\n"); return 3; }
|
||||||
|
wb.post([&]() {
|
||||||
|
printf("[two_workers_close] workerB load inst2\n");
|
||||||
|
fflush(stdout);
|
||||||
|
bool r = inst2.loadPlugin(path, 44100.0);
|
||||||
|
printf("[two_workers_close] workerB load inst2=%d\n", r ? 1 : 0);
|
||||||
|
fflush(stdout);
|
||||||
|
if (r) {
|
||||||
|
HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeB", WS_OVERLAPPEDWINDOW,
|
||||||
|
0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
||||||
|
printf("[two_workers_close] workerB hwnd=%p\n", (void*)h);
|
||||||
|
fflush(stdout);
|
||||||
|
bool r2 = false;
|
||||||
|
std::atomic<bool> d{false};
|
||||||
|
std::thread t([&]() {
|
||||||
|
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
|
r2 = inst2.attachView(h);
|
||||||
|
CoUninitialize();
|
||||||
|
d = true;
|
||||||
|
});
|
||||||
|
while (!d.load()) {
|
||||||
|
MSG msg;
|
||||||
|
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
t.join();
|
||||||
|
printf("[two_workers_close] workerB attach inst2=%d\n", r2 ? 1 : 0);
|
||||||
|
fflush(stdout);
|
||||||
|
bOk = r2;
|
||||||
|
}
|
||||||
|
bDone = true;
|
||||||
|
});
|
||||||
|
while ((!aDone.load() || !bDone.load()) && elapsed() < secs) Sleep(50);
|
||||||
|
printf("RESULT: aDone=%d aOk=%d bDone=%d bOk=%d\n",
|
||||||
|
aDone.load() ? 1 : 0, aOk.load() ? 1 : 0,
|
||||||
|
bDone.load() ? 1 : 0, bOk.load() ? 1 : 0);
|
||||||
|
return (aDone.load() && aOk.load() && bDone.load() && bOk.load()) ? 0 : 3;
|
||||||
} else if (variant == "two_instances" || variant == "two_instances_close" || variant == "close_reopen") {
|
} else if (variant == "two_instances" || variant == "two_instances_close" || variant == "close_reopen") {
|
||||||
// Bridge round-3 probes: does a SECOND instance of the same plugin hang
|
// Bridge round-3 probes: does a SECOND instance of the same plugin hang
|
||||||
// in view->attached() while the first instance's view is attached?
|
// in view->attached() while the first instance's view is attached?
|
||||||
|
|||||||
+57
-60
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -346,35 +347,65 @@ int main(int argc, char* argv[]) {
|
|||||||
playheadSamples = c.arg1;
|
playheadSamples = c.arg1;
|
||||||
}
|
}
|
||||||
} else if (c.type == 4) { // OPEN_GUI (A7): arg1 = parent HWND (0 → bridge tự tạo native window), arg2 = plugin id
|
} 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;
|
uint32_t guiCh = c.channel;
|
||||||
if (guiCh >= 16) guiCh = 0;
|
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>();
|
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)) {
|
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;
|
<< " plugin=" << arg2 << " ch=" << guiCh << " (no instrument loaded)" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::cerr << "[dbg] openGUI thread start hwnd=" << arg1
|
std::cerr << "[dbg] openGUI thread start hwnd=" << hwnd
|
||||||
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
<< " plugin=" << arg2 << " ch=" << guiCh << std::endl;
|
||||||
void* hwnd = (void*)(uintptr_t)arg1;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Option B: chi 1 editor VST mo tai 1 thoi diem toan
|
// Option B: chi 1 editor VST mo tai 1 thoi diem toan
|
||||||
// bridge. Instance thu 2 cua CUNG plugin (Nexus) attach
|
// bridge. Instance thu 2 cua CUNG plugin (Nexus) attach
|
||||||
// view o apartment/worker khac -> treo (bridge.log: ch=2
|
// view o apartment/worker khac -> treo. Dong editor cua
|
||||||
// ket o attached() khi ch=1 con mo) / crash (gui_probe
|
// channel khac TRUOC khi attach: WM_CLOSE -> main pump
|
||||||
// bridge_two). Dong editor cua channel khac TRUOC khi
|
// (audio loop) destroy window -> WM_DESTROY -> closeGUI()
|
||||||
// attach: WM_CLOSE -> worker channel do destroy window ->
|
// + xoa registry. Chay tren worker job de khong stall
|
||||||
// WM_DESTROY -> closeGUI() -> view->removed() tren dung
|
// writeIndex cua real-time loop.
|
||||||
// thread so huu (gui_probe two_instances_close: OK).
|
|
||||||
{
|
{
|
||||||
std::vector<uint32_t> others;
|
std::vector<uint32_t> others;
|
||||||
{
|
{
|
||||||
@@ -393,9 +424,6 @@ int main(int argc, char* argv[]) {
|
|||||||
PostMessage(yHwnd, WM_CLOSE, 0, 0);
|
PostMessage(yHwnd, WM_CLOSE, 0, 0);
|
||||||
std::cerr << "[dbg] openGUI: closing editor ch=" << y
|
std::cerr << "[dbg] openGUI: closing editor ch=" << y
|
||||||
<< " before attach ch=" << guiCh << std::endl;
|
<< " 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;
|
bool closed = false;
|
||||||
for (int i = 0; i < 500; ++i) {
|
for (int i = 0; i < 500; ++i) {
|
||||||
{
|
{
|
||||||
@@ -409,49 +437,20 @@ int main(int argc, char* argv[]) {
|
|||||||
<< " not closed in 5s, proceeding" << std::endl;
|
<< " 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
(void)0;
|
(void)0;
|
||||||
#endif
|
#endif
|
||||||
if (auto* inst = instruments.get(guiCh)) {
|
if (auto* inst = instruments.get(guiCh)) {
|
||||||
// Reopen sau khi dong: openGUI() rebuild lai plugin
|
// Reopen sau khi dong: reload() (terminate + loadPlugin)
|
||||||
// instance (reload = terminate + loadPlugin) trong
|
// PHAI chay tren worker thread nay — COM STA apartment
|
||||||
// khi audio loop co the dang processAudioBlock tren
|
// cua channel song o day. view->attached() cung chay o
|
||||||
// CUNG object -> UAF/hang. Chan bang flag reloading_
|
// day; window thuoc main thread nen main pump (audio
|
||||||
// (set/clear duoi engine mutex; renderAll giu mutex
|
// loop) dispatch messages cua no — khong can pump worker.
|
||||||
// khi process nen check luon nhat quan).
|
// Chan UAF bang flag reloading_ (set/clear duoi engine
|
||||||
|
// mutex; renderAll giu mutex khi process).
|
||||||
bool guard = inst->needsReload();
|
bool guard = inst->needsReload();
|
||||||
if (guard) instruments.setReloading(guiCh, true);
|
if (guard) instruments.setReloading(guiCh, true);
|
||||||
bool ok = inst->openGUI(hwnd);
|
bool ok = inst->reloadForGUI() && inst->attachView(hwnd);
|
||||||
if (guard) instruments.setReloading(guiCh, false);
|
if (guard) instruments.setReloading(guiCh, false);
|
||||||
if (ok)
|
if (ok)
|
||||||
std::cout << "[NativeBridge] GUI attached hwnd=" << hwnd
|
std::cout << "[NativeBridge] GUI attached hwnd=" << hwnd
|
||||||
@@ -460,8 +459,6 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd
|
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd
|
||||||
<< " plugin=" << arg2 << std::endl;
|
<< " plugin=" << arg2 << std::endl;
|
||||||
}
|
}
|
||||||
// Editor windows song tren thread nay — ChannelWorker
|
|
||||||
// pump message queue khi idle (xem class comment).
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user