195 lines
13 KiB
Python
195 lines
13 KiB
Python
import subprocess
|
|
|
|
def load(p):
|
|
return open(p, encoding='utf-8', newline='').read()
|
|
|
|
def save(p, s):
|
|
open(p, 'w', encoding='utf-8', newline='').write(s)
|
|
|
|
def rep(s, needle, repl, tag):
|
|
n = s.count(needle)
|
|
if n != 1:
|
|
raise SystemExit("FAIL %s: needle count=%d" % (tag, n))
|
|
return s.replace(needle, repl)
|
|
|
|
cr = '\r\n'
|
|
|
|
# ---- main.cpp ----
|
|
mp = load('native_bridge/src/main.cpp')
|
|
|
|
mp = rep(mp,
|
|
"int main(int argc, char* argv[]) {" + cr + " std::cout << \"[NativeBridge] Starting DAW Host Bridge Engine...\" << std::endl;" + cr + "#ifdef _WIN32" + cr + " CoInitializeEx(nullptr, COINIT_MULTITHREADED);" + cr + cr + " // Disable Power Throttling (Efficiency Mode) for this background process",
|
|
"int main(int argc, char* argv[]) {" + cr + " std::cout << \"[NativeBridge] Starting DAW Host Bridge Engine...\" << std::endl;" + cr + "#ifdef _WIN32" + cr + " // Disable Power Throttling (Efficiency Mode) for this background process",
|
|
"coinit")
|
|
|
|
mp = rep(mp,
|
|
" SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &powerState, sizeof(powerState));" + cr + cr + " // Elevate main audio processing thread to time-critical priority" + cr + " SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);",
|
|
" SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &powerState, sizeof(powerState));",
|
|
"timecritical")
|
|
|
|
mp = rep(mp,
|
|
" instruments.setEditorOpenPredicate([](const std::string& /*lp*/) {" + cr + " return false;" + cr + " });",
|
|
" instruments.setEditorOpenPredicate([](const std::string& lp) {" + cr + " std::lock_guard<std::mutex> lk(g_editorMutex);" + cr + " auto it = g_editorPathCount.find(lp);" + cr + " return it != g_editorPathCount.end() && it->second > 0;" + cr + " });",
|
|
"predicate")
|
|
|
|
blob = subprocess.check_output(['git', 'show', '0cec3d3:native_bridge/src/main.cpp']).decode('utf-8')
|
|
if '\r\n' not in blob and '\n' in blob:
|
|
blob = blob.replace('\n', '\r\n')
|
|
|
|
def fn_start(s):
|
|
i = s.find('static void post_close_gui(uint32_t ch, HWND hwnd) {' + cr + ' ChannelWorker* w = nullptr;')
|
|
if i < 0:
|
|
i = s.find('static void post_close_gui(uint32_t ch, HWND hwnd) {\n ChannelWorker* w = nullptr;')
|
|
if i < 0:
|
|
raise SystemExit("fn_start not found")
|
|
return i
|
|
|
|
cur_start = fn_start(mp)
|
|
cur_end = mp.find('int main(int argc, char* argv[]) {', cur_start)
|
|
old_start = fn_start(blob)
|
|
old_end = blob.find('int main(int argc, char* argv[]) {', old_start)
|
|
mp = rep(mp, mp[cur_start:cur_end], blob[old_start:old_end], "post_close_gui")
|
|
|
|
needle5 = (
|
|
" workers[guiCh]->post([&instruments, guiCh, arg1, arg2 = pluginId]() {" + cr +
|
|
" if (!instruments.get(guiCh)) {" + cr +
|
|
" std::cerr << \"[NativeBridge] GUI attach FAILED hwnd=\" << (void*)arg1" + cr +
|
|
" << \" plugin=\" << arg2 << \" ch=\" << guiCh << \" (no instrument loaded)\" << std::endl;" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" if (auto* inst0 = instruments.get(guiCh)) {" + cr +
|
|
" if (inst0->hasAttachedView()) {" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" }" + cr +
|
|
" void* hwnd = (void*)arg1;" + cr +
|
|
"#ifdef _WIN32" + cr +
|
|
" HWND nativeHwnd = nullptr;" + cr +
|
|
" {" + cr +
|
|
" std::lock_guard<std::mutex> lock(g_guiMutex);" + cr +
|
|
" auto it = g_guiWindows.find(guiCh);" + cr +
|
|
" if (it != g_guiWindows.end()) nativeHwnd = (HWND)it->second;" + cr +
|
|
" }" + cr +
|
|
" if (hwnd == 0) {" + cr +
|
|
" if (nativeHwnd && IsWindow(nativeHwnd)) {" + cr +
|
|
" hwnd = (void*)nativeHwnd;" + cr +
|
|
" SetWindowTextA(nativeHwnd, arg2.c_str());" + cr +
|
|
" ShowWindow(nativeHwnd, SW_SHOW);" + cr +
|
|
" SetForegroundWindow(nativeHwnd);" + cr +
|
|
" // Reuse: cap nhat USERDATA (channel+1) - inst CU da bi thay" + cr +
|
|
" // the boi assign() -> WM_DESTROY sau nay lookup inst MOI." + cr +
|
|
" SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));" + cr +
|
|
" } else {" + cr +
|
|
" nativeHwnd = (HWND)create_native_vst_window(arg2.c_str());" + cr +
|
|
" if (!nativeHwnd) {" + cr +
|
|
" std::cerr << \"[NativeBridge] GUI create window FAILED plugin=\" << arg2 << std::endl;" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" {" + cr +
|
|
" std::lock_guard<std::mutex> lock(g_guiMutex);" + cr +
|
|
" g_guiWindows[guiCh] = nativeHwnd; // keep window alive" + cr +
|
|
" g_hwndToCh[nativeHwnd] = guiCh; // WM_DESTROY cleanup" + cr +
|
|
" }" + cr +
|
|
" SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));" + cr +
|
|
" hwnd = (void*)nativeHwnd;" + cr +
|
|
" }" + cr +
|
|
" }" + cr +
|
|
"#endif" + cr +
|
|
" if (auto* inst0 = instruments.get(guiCh)) {" + cr +
|
|
" if (inst0->hasAttachedView()) {" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" }" + cr +
|
|
"#ifdef _WIN32" + cr +
|
|
" // Option B: chi 1 editor VST mo tai 1 thoi diem toan"
|
|
)
|
|
repl5 = (
|
|
" void* hwnd = (void*)arg1;" + cr +
|
|
"#ifdef _WIN32" + cr +
|
|
" // Window PHAI thuoc MAIN thread (audio loop pump nay dispatch" + cr +
|
|
" // messages cua no moi vong lap). Tao/cap nhat window ngay tai day." + cr +
|
|
" HWND nativeHwnd = nullptr;" + cr +
|
|
" {" + cr +
|
|
" std::lock_guard<std::mutex> lock(g_guiMutex);" + cr +
|
|
" auto it = g_guiWindows.find(guiCh);" + cr +
|
|
" if (it != g_guiWindows.end()) nativeHwnd = (HWND)it->second;" + cr +
|
|
" }" + cr +
|
|
" if (hwnd == 0) {" + cr +
|
|
" if (nativeHwnd && IsWindow(nativeHwnd)) {" + cr +
|
|
" hwnd = (void*)nativeHwnd;" + cr +
|
|
" SetWindowTextA(nativeHwnd, pluginId.c_str());" + cr +
|
|
" ShowWindow(nativeHwnd, SW_SHOW);" + cr +
|
|
" SetForegroundWindow(nativeHwnd);" + cr +
|
|
" // Reuse: cap nhat USERDATA (channel+1) - inst CU da bi thay" + cr +
|
|
" // the boi assign() -> WM_DESTROY sau nay lookup inst MOI." + cr +
|
|
" SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));" + cr +
|
|
" } else {" + cr +
|
|
" nativeHwnd = (HWND)create_native_vst_window(pluginId.c_str());" + cr +
|
|
" if (!nativeHwnd) {" + cr +
|
|
" std::cerr << \"[NativeBridge] GUI create window FAILED plugin=\" << pluginId << std::endl;" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" {" + cr +
|
|
" std::lock_guard<std::mutex> lock(g_guiMutex);" + cr +
|
|
" g_guiWindows[guiCh] = nativeHwnd; // keep window alive" + cr +
|
|
" g_hwndToCh[nativeHwnd] = guiCh; // WM_DESTROY cleanup" + cr +
|
|
" }" + cr +
|
|
" SetWindowLongPtrA(nativeHwnd, GWLP_USERDATA, (LONG_PTR)(guiCh + 1));" + cr +
|
|
" hwnd = (void*)nativeHwnd;" + cr +
|
|
" }" + cr +
|
|
" }" + cr +
|
|
"#endif" + cr +
|
|
" workers[guiCh]->post([&instruments, guiCh, hwnd, arg2 = pluginId]() {" + cr +
|
|
" if (!instruments.get(guiCh)) {" + cr +
|
|
" std::cerr << \"[NativeBridge] GUI attach FAILED hwnd=\" << hwnd" + cr +
|
|
" << \" plugin=\" << arg2 << \" ch=\" << guiCh << \" (no instrument loaded)\" << std::endl;" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" if (auto* inst0 = instruments.get(guiCh)) {" + cr +
|
|
" if (inst0->hasAttachedView()) {" + cr +
|
|
" return;" + cr +
|
|
" }" + cr +
|
|
" }" + cr +
|
|
"#ifdef _WIN32" + cr +
|
|
" // Option B: chi 1 editor VST mo tai 1 thoi diem toan"
|
|
)
|
|
mp = rep(mp, needle5, repl5, "handleOpenGui-window")
|
|
|
|
mp = rep(mp,
|
|
" try {" + cr + " instruments.setBypassed(true);" + cr + " sleep_ms(10);" + cr + " ok = inst->reloadForGUI();" + cr + " instruments.setBypassed(false);" + cr + " // IME recursion fix: strip the editor's IMC before",
|
|
" try {" + cr + " ok = inst->reloadForGUI();" + cr + " // IME recursion fix: strip the editor's IMC before",
|
|
"bypass-open1")
|
|
|
|
mp = rep(mp,
|
|
" } catch (...) {" + cr + " instruments.setBypassed(false);" + cr + " std::cerr << \"[NativeBridge] GUI attach EXCEPTION ch=\" << guiCh",
|
|
" } catch (...) {" + cr + " std::cerr << \"[NativeBridge] GUI attach EXCEPTION ch=\" << guiCh",
|
|
"bypass-open2")
|
|
|
|
mp = rep(mp,
|
|
' << " (channel active with editor open)" << std::endl;',
|
|
' << " (channel muted while editor open)" << std::endl;',
|
|
"logmsg")
|
|
|
|
mp = rep(mp,
|
|
" if (ch != UINT32_MAX && g_engine) {" + cr + " auto* inst = g_engine->get(ch);" + cr + " if (inst) {" + cr + " inst->resizeView(w, h);" + cr + " }" + cr + " }" + cr + " return 0;",
|
|
" if (ch != UINT32_MAX && g_workers) {" + cr + " auto wit = g_workers->find(ch);" + cr + " if (wit != g_workers->end()) {" + cr + " // onSize() must run on the channel worker (the view was attached" + cr + " // in its COM STA apartment), not on the main pump thread." + cr + " wit->second->post([ch, w, h]() {" + cr + " if (!g_engine) return;" + cr + " auto* i = g_engine->get(ch);" + cr + " if (i) i->resizeView(w, h);" + cr + " });" + cr + " }" + cr + " }" + cr + " return 0;",
|
|
"wmsize")
|
|
|
|
save('native_bridge/src/main.cpp', mp)
|
|
|
|
# ---- Vst3Instrument.cpp ----
|
|
vp = load('native_bridge/src/Vst3Instrument.cpp')
|
|
|
|
vp = rep(vp,
|
|
"bool Vst3Instrument::reloadForGUI() {" + cr + "#ifndef HAVE_VST3SDK" + cr + " return false;" + cr + "#else" + cr + " auto* s = static_cast<Vst3HostState*>(state_);" + cr + " if (!s || !s->controller) return false;" + cr + " return true;" + cr + "#endif" + cr + "}",
|
|
"bool Vst3Instrument::reloadForGUI() {" + cr + "#ifndef HAVE_VST3SDK" + cr + " return false;" + cr + "#else" + cr + " auto* s = static_cast<Vst3HostState*>(state_);" + cr + " if (!s || !s->controller) return false;" + cr + " if (s->view && guiAttached_) return true;" + cr + " if (hasAttachedOnce_ && !guiAttached_) {" + cr + " // Reopen GUI sau khi dong: mot so plugin (Nexus...) hang o" + cr + " // view->attached() LAN 2 tren cung component instance. Tao lai inst" + cr + " // moi tren CUNG worker thread (COM STA con song) roi attach lai." + cr + " // PHAN NAY PHAI chay tren worker thread — apartment cua channel song" + cr + " // o day; chay tren thread tam thi apartment moi chet ngay sau do" + cr + " // (reopen lan sau -> treo nhu bridge_like)." + cr + " if (!reload()) return false;" + cr + " s = static_cast<Vst3HostState*>(state_);" + cr + " if (!s || !s->controller) return false;" + cr + " }" + cr + " return true;" + cr + "#endif" + cr + "}",
|
|
"reloadForGUI")
|
|
|
|
vp = rep(vp,
|
|
"void Vst3Instrument::closeGUI() {" + cr + "#ifndef HAVE_VST3SDK" + cr + " return;" + cr + "#else" + cr + " auto* s = static_cast<Vst3HostState*>(state_);" + cr + " if (s && s->view) {" + cr + " if (guiAttached_) {" + cr + " try {" + cr + " s->view->removed();" + cr + " } catch (...) {" + cr + " std::cerr << \"[Vst3Instrument] Exception inside view->removed()\" << std::endl;" + cr + " }" + cr + " }" + cr + " s->view = nullptr;" + cr + " }" + cr + " guiAttached_ = false;" + cr + "#endif" + cr + "}",
|
|
"void Vst3Instrument::closeGUI() {" + cr + "#ifndef HAVE_VST3SDK" + cr + " return;" + cr + "#else" + cr + " auto* s = static_cast<Vst3HostState*>(state_);" + cr + " // Do NOT call s->view->removed(): Nexus's removed() enters an internal" + cr + " // modal wait for a message only this thread's message pump can dispatch" + cr + " // — but this thread is inside removed(), so it deadlocks forever" + cr + " // (observed: closeGUI job never returns; wdisp flood from the editor)." + cr + " // Nulling the view and letting the next terminate() (reload / destructor)" + cr + " // tear the editor windows down on this same thread is the deterministic" + cr + " // path out. Callers (post_close_gui / load job) additionally destroy the" + cr + " // editor's child windows right here on this thread so the plugin stops" + cr + " // pumping; the leaked editor object is inert once its windows are gone." + cr + " if (s) s->view = nullptr;" + cr + " guiAttached_ = false;" + cr + "#endif" + cr + "}",
|
|
"closeGUI")
|
|
|
|
save('native_bridge/src/Vst3Instrument.cpp', vp)
|
|
print("ALL EDITS OK")
|