M2: GUI thread + host window trong juce_fx_bridge (Reaper-style — editor cua CHINH instance DSP)
JuceFxGuiHost: GUI thread rieng chay juce MessageManager runDispatchLoopUntil(10) + tick 30ms inline cadence (JUCE Timer KHONG fire tren GUI thread rieng — da xac minh selftest), PluginWindow (DocumentWindow native 'FX slot N') attach editor, reconcile dong editor+ window, teardown thu tu. runOnGui(): plugin instance phai tao tren GUI thread — JUCE plugin gan MM vao thread tao instance, editor tao thread khac -> treo (M1 chi chay vi main thread vua tao instance vua la message thread); prepare/shutdown bao qua runOnGui. JuceFxLoop: guiHost.start() TRUOC engine.prepare; re-prepare sau waitEditorsClosed(1500); teardown runOnGui(shutdown) truoc stop(). Idle ~0 CPU (khong feeder, khong instance 2). juce_fx_gui_selftest (khong ship) PASS tren purecomp + fast-compressor: open/hide/show/ resize/close, teardown sach; window native chup duoc (PrintWindow). Regression M1 selftest OK.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
// Validate header mỗi iteration: sampleRate/blockSize đổi giữa chừng →
|
||||
// teardown + prepareToPlay lại + report latency mới qua FxLatReport.
|
||||
#include "JuceFxEngine.h"
|
||||
#include "JuceFxGuiHost.h"
|
||||
#include "FxRealtimeIPC.h"
|
||||
#include "FxShm.h"
|
||||
|
||||
@@ -132,7 +133,16 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
engine.setChain(chainJson);
|
||||
if (ipc->h.sampleRate) curSr = ipc->h.sampleRate;
|
||||
if (ipc->h.blockSize) curBlock = ipc->h.blockSize;
|
||||
engine.prepare(curSr, curBlock);
|
||||
// M2: GUI thread + host window (Reaper-style — editor của CHÍNH instance
|
||||
// DSP trong graph, không instance 2 / feeder). GUI thread cũng là thread
|
||||
// tạo plugin instance: JUCE plugin gắn MM vào thread tạo instance — editor
|
||||
// tạo trên thread khác sẽ treo (selftest: createEditorIfNeeded hang khi
|
||||
// instance tạo trên main). Vì vậy start() TRƯỚC prepare, prepare chạy qua
|
||||
// runOnGui; main thread sau này chỉ engine.process (JUCE cho phép
|
||||
// cross-thread process như host chuẩn).
|
||||
JuceFxGuiHost guiHost(engine);
|
||||
guiHost.start();
|
||||
guiHost.runOnGui([&] { engine.prepare(curSr, curBlock); });
|
||||
std::cerr << "[JuceFxLoop] prepared sr=" << curSr << " block=" << curBlock
|
||||
<< " latency=" << engine.latencySamples() << std::endl;
|
||||
|
||||
@@ -160,6 +170,8 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
ipc->h.state = FXRT_STATE_READY;
|
||||
std::cerr << "[JuceFxLoop] ready" << std::endl;
|
||||
|
||||
// (guiHost đã start TRƯỚC engine.prepare ở trên — instance phải tạo trên
|
||||
// GUI thread.) Editor chỉ mở khi có lệnh (M3 ctrl ring); idle ≈ 0 CPU.
|
||||
std::thread hb([&]() {
|
||||
while (ipc->h.running && ipc->h.state == FXRT_STATE_READY) {
|
||||
ipc->h.heartbeat++;
|
||||
@@ -192,7 +204,11 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
ipc->h.blockSize != curBlock)) {
|
||||
curSr = ipc->h.sampleRate;
|
||||
curBlock = ipc->h.blockSize;
|
||||
engine.prepare(curSr, curBlock);
|
||||
// M2: editor là JUCE Component — đóng trên GUI thread TRƯỚC khi
|
||||
// re-prepare; prepare() chạy trên GUI thread (thread tạo instance —
|
||||
// xem startup, M2b).
|
||||
guiHost.waitEditorsClosed(1500);
|
||||
guiHost.runOnGui([&] { engine.prepare(curSr, curBlock); });
|
||||
reportLatencies();
|
||||
std::cerr << "[JuceFxLoop] re-prepared sr=" << curSr
|
||||
<< " block=" << curBlock
|
||||
@@ -270,7 +286,8 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
}
|
||||
ipc->h.state = FXRT_STATE_STARTING;
|
||||
hb.join();
|
||||
engine.shutdown();
|
||||
guiHost.runOnGui([&] { engine.shutdown(); }); // graph clear trên GUI thread
|
||||
guiHost.stop(); // M2: đóng editor (GUI thread) + dừng GUI thread + join
|
||||
fxshm::closeShm(v);
|
||||
#ifdef _WIN32
|
||||
timeEndPeriod(1);
|
||||
|
||||
Reference in New Issue
Block a user