M3: ctrl ring reader (engine -> bridge) dieu khien editor qua SHM

JuceFxLoop them thread ctrlReader poll 2ms: doc FxCtrlCmd ring (key 'ed.open'/
'ed.close'/'ed.show'/'ed.hide'/'ed.resize'/'ed.capture', slot = chain index), dispatch
qua engine.requestEditor (queue thread-safe; GUI tick 30ms pump). w/h ma hoa
trong value: w*4096+h (chinh xac tuyet doi w,h<=4095 — float 24-bit mantissa);
value<=0 -> editor preferred size. SET_PARAM cu (key khac) consume + bo qua an
toan (JuceFxEngine chua co param API — params di trong chain JSON). ctrl.join()
truoc shutdown. E2E test (session SHM that + bridge that, chain purecomp):
ed.open -> window native hien, audio pump khi GUI mo (process+editor song song),
hide/show/resize/capture 4049 bytes, close -> window bien mat, teardown sach
rc=0 — PASS 11/11. Tien de M4 (engine send_editor_cmd + /fx-gui).
This commit is contained in:
2026-09-04 10:53:39 +07:00
parent df4a22b9b4
commit e6d01d90f1
+59
View File
@@ -179,6 +179,64 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
}
});
// M3: ctrl ring reader (engine → bridge, poll 2ms). Key "ed.*" = editor
// command (Reaper-style: engine điều khiển editor của CHÍNH instance DSP
// trong graph — không instance 2). Dispatch qua engine.requestEditor
// (thread-safe queue); GUI tick 30ms pumpEditorQueue thực thi. Key khác
// (SET_PARAM cũ: param name) — JuceFxEngine chưa có param API (params đi
// trong chain JSON) → consume + bỏ qua an toàn (ring không đầy, không chặn
// producer). w/h mã hoá trong value: w*4096 + h — chính xác tuyệt đối với
// w,h <= 4095 (float 24-bit mantissa); value <= 0 → editor preferred size
// (engine chỉ setSize khi w,h > 0).
std::thread ctrl([&]() {
while (ipc->h.running && ipc->h.state == FXRT_STATE_READY) {
while (ipc->h.ctrlRead < ipc->h.ctrlWrite) {
const uint32_t cs = ipc->h.ctrlRead & (FXRT_CTRL_SLOTS - 1);
const FxCtrlCmd& cmd = ipc->ctrl[cs];
const uint32_t slot = cmd.slot;
char key[17];
std::memcpy(key, cmd.key, 16);
key[16] = '\0';
const float value = cmd.value;
MemoryBarrier();
ipc->h.ctrlRead++; // consume trước dispatch — không block ring
if (std::strncmp(key, "ed.", 3) != 0)
continue; // SET_PARAM cũ — no-op (xem comment trên)
JuceFxEngine::EditorAction act;
bool known = true;
if (std::strcmp(key, "ed.open") == 0)
act = JuceFxEngine::EditorAction::Open;
else if (std::strcmp(key, "ed.close") == 0)
act = JuceFxEngine::EditorAction::Close;
else if (std::strcmp(key, "ed.show") == 0)
act = JuceFxEngine::EditorAction::Show;
else if (std::strcmp(key, "ed.hide") == 0)
act = JuceFxEngine::EditorAction::Hide;
else if (std::strcmp(key, "ed.resize") == 0)
act = JuceFxEngine::EditorAction::Resize;
else if (std::strcmp(key, "ed.capture") == 0)
act = JuceFxEngine::EditorAction::Capture;
else
known = false;
if (!known) {
std::cerr << "[JuceFxLoop] ctrl ed.* lạ: " << key
<< " slot=" << slot << std::endl;
continue;
}
int w = 0, h = 0;
if (value > 0.0f) {
const int v = (int)(value + 0.5f);
w = v / 4096;
h = v % 4096;
}
engine.requestEditor(slot, act, w, h);
std::cerr << "[JuceFxLoop] ctrl " << key << " slot=" << slot
<< " w=" << w << " h=" << h << std::endl;
}
sleepMs(2);
}
});
const uint32_t n = curBlock;
const uint32_t inMask = ipc->h.inSlots - 1;
const uint32_t outMask = ipc->h.outSlots - 1;
@@ -286,6 +344,7 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
}
ipc->h.state = FXRT_STATE_STARTING;
hb.join();
ctrl.join(); // dừng trước shutdown — không còn lệnh editor mới
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);