M7-M16: VST FX GUI native embed hoàn thiện — fix click-2-lần chuyển slot, anchor lần mở đầu, taskbar icon
- app.jsx: masterGuiOpenRef sync truoc setState — guard chuyen slot doc ref, het race closure (click 1 lan chuyen slot) - JuceFxGuiHost.cpp: M14 applyAnchor truoc setVisible (het flash giua man hinh); M15 clear WS_EX_APPWINDOW + set TOOLWINDOW (het taskbar icon — JUCE set APPWINDOW 0x40000 thang TOOLWINDOW); M9 owner DAW + M9d skip applyOwner khi an - JuceFxLoop.cpp: M16 bo clearAnchor khi ed.open (retry ed.open ~700ms xoa rect fresh -> window moi khong anchor duoc lan dau mo panel); chi clear khi Close - JuceFxEngine/app.jsx/plugins.py/fx_realtime.py: editor session realtime, anchor rect routing, retry ed.open - Rebuild engine PyInstaller + juce_fx_bridge cmake; binaries dong bo 5 noi (bridge 7464a7ef)
This commit is contained in:
+89
-5
@@ -142,6 +142,10 @@ class FxRealtimeSession:
|
||||
# chủ đích → KHÔNG restart (cờ _closing / running=0 — user gọi lại).
|
||||
self._closing = False
|
||||
self._restarts = 0
|
||||
# M8: bridge JUCE doc job 1 lan luc spawn (khong poll seq) -> set_chain
|
||||
# phai reload (terminate -> watchdog respawn). Co chong dem crash.
|
||||
self._juce_bridge = False
|
||||
self._reload_planned = False
|
||||
self._watch = threading.Thread(target=self._watch_loop, daemon=True)
|
||||
self._spawn()
|
||||
self._watch.start()
|
||||
@@ -197,7 +201,9 @@ class FxRealtimeSession:
|
||||
fd, self._job_path = tempfile.mkstemp(suffix=".json", prefix="fxrt_")
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
json.dump(job, f)
|
||||
if os.path.basename(exe).lower().startswith("juce_fx_bridge"):
|
||||
self._juce_bridge = os.path.basename(exe).lower().startswith("juce_fx_bridge")
|
||||
_log.info("[fxrt] spawn exe=%s juce=%s seq=%d shm=%s", os.path.basename(exe), self._juce_bridge, self._chain_seq, self.name)
|
||||
if self._juce_bridge:
|
||||
cmd = [exe, "--juce-fx", self._job_path, "--shm", self.name,
|
||||
"--parent", str(os.getpid())]
|
||||
else:
|
||||
@@ -209,14 +215,22 @@ class FxRealtimeSession:
|
||||
# stall (procMax=bridge/VST cham, outDepth=Python pump cham).
|
||||
_blog = os.environ.get("FXRT_BRIDGE_LOG")
|
||||
if not _blog:
|
||||
# M13: per-session log (fxrt_bridge_<shm-suffix>.log) — trước đây 1
|
||||
# file chung cho MỌI session → log 2 process lẫn nhau. Env override
|
||||
# giữ nguyên để ép 1 file khi cần.
|
||||
_ad = os.environ.get("APPDATA")
|
||||
_blog = os.path.join(_ad, "SonicForgeDAW", "logs", "fxrt_bridge.log") if _ad else ""
|
||||
if _ad:
|
||||
_blog = os.path.join(_ad, "SonicForgeDAW", "logs",
|
||||
"fxrt_bridge_" + self.name + ".log")
|
||||
else:
|
||||
_blog = ""
|
||||
self.proc = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=open(_blog, "ab", buffering=0) if _blog else subprocess.DEVNULL,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
_log.info("[fxrt] spawned pid=%d log=%s", self.proc.pid, _blog)
|
||||
|
||||
def set_chain(self, fx_chain):
|
||||
"""Cập nhật chain trên session ĐANG CHẠY (không restart session):
|
||||
@@ -238,8 +252,56 @@ class FxRealtimeSession:
|
||||
}, f)
|
||||
except Exception:
|
||||
return False
|
||||
# M8: juce_fx_bridge doc job CHI 1 LAN luc spawn (JuceFxLoop.cpp khong
|
||||
# poll seq nhu RealtimeFxLoop cu) -> chi ghi job KHONG bao gio ap len
|
||||
# bridge: chain bridge giu nguyen trong khi engine chain da doi -> GUI
|
||||
# mo slot moi C++ bao "slot ngoai chain (M) - bo qua". Reload chu dong:
|
||||
# terminate -> watchdog respawn doc chain hien tai (khong tinh crash,
|
||||
# khong dung gioi han FXRT_MAX_RESTARTS). Legacy fx_vst_bridge poll seq
|
||||
# giu nguyen (async swap muot - khong reload).
|
||||
_log.info("[fxrt] set_chain seq=%d len=%d juce=%s proc_alive=%s reload_planned=%s",
|
||||
self._chain_seq, len(self.chain), self._juce_bridge,
|
||||
self.proc is not None and self.proc.poll() is None,
|
||||
self._reload_planned)
|
||||
if self._juce_bridge:
|
||||
self._request_chain_reload()
|
||||
_log.info("[fxrt] set_chain requested reload proc_pid=%s",
|
||||
self.proc.pid if self.proc else None)
|
||||
return True
|
||||
|
||||
def _request_chain_reload(self):
|
||||
"""M8: yeu cau watchdog respawn sach voi chain hien tai (chi juce
|
||||
bridge). Khong block: terminate proc -> vong watchdog (200ms) thay chet
|
||||
+ co _reload_planned -> respawn doc job moi, khong tinh crash."""
|
||||
with self._lock:
|
||||
if self._closing or self.proc is None or self.proc.poll() is not None:
|
||||
return
|
||||
self._reload_planned = True
|
||||
try:
|
||||
self.proc.terminate()
|
||||
except Exception:
|
||||
self._reload_planned = False
|
||||
|
||||
def wait_chain_ready(self, timeout=20.0):
|
||||
"""M8: cho chain moi thuc su ap tren bridge sau set_chain (juce reload
|
||||
async): het co reload + proc respawn READY. Khong co reload -> tra ngay
|
||||
True. False khi session dong/chet/qua han."""
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
with self._lock:
|
||||
if self._closing:
|
||||
return False
|
||||
p = self.proc
|
||||
reloading = self._reload_planned
|
||||
alive = p is not None and p.poll() is None
|
||||
ready = alive and self.h.state == FXRT_STATE_READY
|
||||
if not reloading and ready:
|
||||
return True
|
||||
if not alive and not reloading:
|
||||
return False
|
||||
time.sleep(0.1)
|
||||
return False
|
||||
|
||||
def wait_ready(self, timeout=30.0):
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
@@ -471,6 +533,24 @@ class FxRealtimeSession:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def send_editor_rect(self, slot, x, y, w, h):
|
||||
"""M7: anchor rect cho GUI native (window nằm ĐÚNG rect panel embed).
|
||||
Gửi 4 key SHM ctrl ed.rx/ry/rw/rh — MỖI key 1 số nguyên float (chính
|
||||
xác tuyệt đối |v| <= 2^24; x/y âm khi panel nằm màn hình trái OK vì C++
|
||||
parse int trực tiếp — KHÔNG encode w*4096+h như ed.resize, giới hạn
|
||||
4095). JuceFxGuiHost lưu anchor (thread-safe) + reconcile GUI thread
|
||||
30ms SetWindowPos khi rect ĐỔI. Trả False nếu session không SHM."""
|
||||
with self._lock:
|
||||
if getattr(self, "shm", None) is None:
|
||||
return False
|
||||
try:
|
||||
for f, v in (("x", int(x)), ("y", int(y)),
|
||||
("w", int(w)), ("h", int(h))):
|
||||
self._ctrl_enqueue_locked(int(slot), "ed.r" + f, float(v))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def chain_slot_for_path(self, plugin_path):
|
||||
"""Slot index (theo chain bridge ĐANG CHẠY — đã lọc active=false, giữ
|
||||
thứ tự) của plugin path; None nếu plugin không trong chain. Index khớp
|
||||
@@ -539,11 +619,15 @@ class FxRealtimeSession:
|
||||
if getattr(self.h, "running", 1) == 0:
|
||||
_log.warning("[fxrt] bridge thoát rc=%s sau running=0 (user kill — không restart)", rc)
|
||||
return
|
||||
if self._restarts >= FXRT_MAX_RESTARTS:
|
||||
if self._reload_planned:
|
||||
self._reload_planned = False
|
||||
_log.info("[fxrt] bridge rc=%s — reload chain chủ động (set_chain juce) → respawn đọc chain mới", rc)
|
||||
elif self._restarts >= FXRT_MAX_RESTARTS:
|
||||
_log.error("[fxrt] bridge crash rc=%s — restart tối đa %d lần đã đạt, bỏ", rc, FXRT_MAX_RESTARTS)
|
||||
return
|
||||
self._restarts += 1
|
||||
_log.warning("[fxrt] bridge chết rc=%s — auto restart #%d", rc, self._restarts)
|
||||
else:
|
||||
self._restarts += 1
|
||||
_log.warning("[fxrt] bridge chết rc=%s — auto restart #%d", rc, self._restarts)
|
||||
try:
|
||||
self._spawn()
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user