# G1.4: bo mute-when-editor-open — xoa editorOpen machinery (channelQuiet chi con reloading) import io, sys p = 'native_bridge/src/main.cpp' raw = open(p, 'rb').read() EM = b'\xe2\x80\x94' NL = b'\r\n' def rep(old, new, tag): global raw o = old.replace(b'\n', NL) if o not in raw: print(f'MISSING [{tag}]') sys.exit(1) raw = raw.replace(o, new.replace(b'\n', NL)) print(f'OK [{tag}]') rep(b"""// Editor-open tracking: while a channel's VST editor (native window) is // attached, EVERY channel assigned the same plugin DLL path must stay quiet """ + EM + b""" // the plugin's window proc runs on the editor channel's worker while the // audio loop calls process() on other instances of the same DLL (2 threads // in one DLL -> Nexus USER32 crash). g_editorPathCount = refcount per // lowercase plugin path; g_editorOpenPath/g_editorOpen per channel. // Lock order: g_editorMutex is taken while holding the engine mutex // (channelQuiet) or standalone in non-rt jobs """ + EM + b""" never the reverse. static std::mutex g_editorMutex; static std::map g_editorPathCount; static std::string g_editorOpenPath[16]; static bool g_editorOpen[16] = { false }; """, b"", "decl-block") rep(b"""// Editor closed / instrument replaced: drop the channel's editor-open state // (refcounted per lowercase plugin path). Called from non-rt jobs only """ + EM + b""" // never from the audio loop. static void clear_editor_open(uint32_t ch) { std::lock_guard lk(g_editorMutex); if (!g_editorOpen[ch]) return; g_editorOpen[ch] = false; std::string pth = g_editorOpenPath[ch]; g_editorOpenPath[ch].clear(); if (!pth.empty()) { auto it = g_editorPathCount.find(pth); if (it != g_editorPathCount.end() && --it->second <= 0) g_editorPathCount.erase(it); } } """, b"", "clear_editor_open") rep(b""" // Editor-open predicate: channelQuiet() checks whether ANY editor is // attached for the channel's plugin DLL path (refcounted in // g_editorPathCount, updated by the open/close GUI jobs). instruments.setEditorOpenPredicate([](const std::string& lp) { std::lock_guard lk(g_editorMutex); auto it = g_editorPathCount.find(lp); return it != g_editorPathCount.end() && it->second > 0; }); """, b"", "set-predicate") rep(b""" // Editor attached: mark the plugin path editor-open so the // audio loop stops processing EVERY channel assigned this // DLL while the editor lives (its window proc runs on this // worker; process() elsewhere on the same DLL -> Nexus crash). { std::lock_guard lk(g_editorMutex); if (!gp.empty()) { g_editorOpen[guiCh] = true; g_editorOpenPath[guiCh] = gp; ++g_editorPathCount[gp]; } } disable_ime_contexts((HWND)hwnd); """, b""" disable_ime_contexts((HWND)hwnd); """, "attach-mark") rep(b""" // Editor detached + fresh instance loaded: drop editor-open // state (channelQuiet falls back to reloading flags only). clear_editor_open(ch); """, b"", "close-ok-path") rep(b""" // Instrument already gone (unloaded) """ + EM + b""" nothing to rebuild. g_engine->setReloading(ch, false); clear_editor_open(ch); """, b""" // Instrument already gone (unloaded) """ + EM + b""" nothing to rebuild. g_engine->setReloading(ch, false); """, "close-else-path") rep(b""" ShowWindow(hToHide, SW_HIDE); // Editor detached: drop editor-open state before the // instrument is replaced (assign). clear_editor_open(ch); """, b""" ShowWindow(hToHide, SW_HIDE); """, "load-close-path") open(p, 'wb').write(raw) print('DONE main.cpp')