Files
SonicForgeStudio/native_bridge/juce_fx/JuceFxGuiHost.h
T
admin df4a22b9b4 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.
2026-09-04 10:40:46 +07:00

72 lines
3.2 KiB
C++

// native_bridge/juce_fx/JuceFxGuiHost.h
// M2: GUI thread + window host (Reaper-style) — host editor của ĐÚNG instance
// DSP trong graph ngay trong fxrt_bridge (juce_fx_bridge.exe), không instance
// 2 / feeder (nguyên nhân CPU 100% .24).
//
// GUI thread riêng (BELOW_NORMAL) chạy juce::MessageManager + tick 30ms:
// engine.pumpEditorQueue() → reconcile window theo editor state.
// PluginWindow (DocumentWindow native) bọc juce::AudioProcessorEditor mà
// ENGINE sở hữu — window KHÔNG sở hữu content (setContentNonOwned).
// Idle (không Open, không play) ≈ 0 CPU như Reaper.
#pragma once
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <thread>
class JuceFxEngine;
class JuceFxGuiHost {
public:
explicit JuceFxGuiHost(JuceFxEngine& engine);
~JuceFxGuiHost(); // .cpp (Impl incomplete ở header)
JuceFxGuiHost(const JuceFxGuiHost&) = delete;
JuceFxGuiHost& operator=(const JuceFxGuiHost&) = delete;
// Start GUI thread (thread này thành JUCE message thread). Gọi TRƯỚC mọi
// engine.prepare() — plugin instance phải tạo trên GUI thread (plugin JUCE
// MM gắn thread tạo instance; editor tạo thread khác → treo — M1 chỉ chạy
// vì main thread vừa tạo instance vừa là message thread).
void start();
// Chạy task đồng bộ trên GUI thread (chờ xong). Main/audio thread dùng để
// bọc engine.prepare()/shutdown() — instance + editor sống trên cùng thread
// như plugin yêu cầu. KHÔNG gọi từ GUI thread; không started → chạy inline.
template <typename F>
void runOnGui(F&& f) {
enqueueGuiTask([f = std::forward<F>(f)]() mutable { f(); });
}
// Đóng editor + dừng GUI thread + join (tối đa 2s, detach nếu GUI thread
// kẹt trong plugin editor). Gọi TRƯỚC engine.shutdown().
void stop();
// Main thread: enqueue Close cho mọi editor đang mở + chờ GUI thread pump
// xong (poll hasOpenEditor). Bắt buộc trước engine.prepare()/shutdown()
// khi GUI thread đang chạy (editor là JUCE Component — xoá trên message
// thread). Không started → return ngay.
void waitEditorsClosed(uint32_t timeoutMs);
// Thread-safe: số host window đang tồn tại (selftest).
uint32_t windowCount() const { return winCount_.load(); }
// GUI thread: X button của PluginWindow slot → engine Close (queue) — tick
// sau pump + reconcile xoá window.
void onWindowClose(uint32_t slot);
// GUI thread tick 30ms (GuiTickTimer trong .cpp gọi): pump + reconcile.
void tick();
private:
// runOnGui: xếp task cho GUI thread + chờ hoàn thành (count-based).
void enqueueGuiTask(std::function<void()> task);
// GUI thread: tạo/xoá/ẩn-hiện PluginWindow theo engine editor state.
void reconcile();
// GUI thread entry.
void guiMain();
struct Impl;
std::unique_ptr<Impl> impl_; // windows_ (PluginWindow chỉ thấy trong .cpp)
JuceFxEngine& engine_;
std::thread thread_;
std::atomic<bool> started_{false};
std::atomic<bool> quit_{false};
std::atomic<bool> guiExited_{false};
std::atomic<uint32_t> winCount_{0};
};