diff --git a/native_bridge/juce_fx/JuceFxLoop.cpp b/native_bridge/juce_fx/JuceFxLoop.cpp index d62249c..79bd005 100644 --- a/native_bridge/juce_fx/JuceFxLoop.cpp +++ b/native_bridge/juce_fx/JuceFxLoop.cpp @@ -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);