// native_bridge/juce_fx/JuceFxEngine.h // JUCE AudioProcessorGraph engine (G1): đọc input ring → graph (rỗng) → // ghi output ring. Tách khỏi loop để G2 chỉ thêm plugin vào graph. #pragma once #include #include #include #include class JuceFxEngine { public: JuceFxEngine(); ~JuceFxEngine(); // Không copy. JuceFxEngine(const JuceFxEngine&) = delete; JuceFxEngine& operator=(const JuceFxEngine&) = delete; // Tạo graph mới (input node -> [VST3 theo chain] -> output node). // sampleRate/blockSize đổi → teardown graph cũ + prepare lại (validate // header G1). blockSize là kích thước mỗi SHM block (256). void prepare(uint32_t sampleRate, uint32_t blockSize); // G2: đặt chain (mảng JSON fx_chain) trước prepare — các slot bypass / // không phải vst3 / load fail chạy passthrough (latency 0). void setChain(const std::string& chainJson); // Audio thread: xử lý n mẫu stereo in-place (n <= blockSize * FXRT_IN_SLOTS). void process(float* inL, float* inR, uint32_t n); // Graph latency samples (G1 chain rỗng = 0; G2 = getLatencySamples thật). uint32_t latencySamples() const; // Latency thật từng slot theo thứ tự chain (0 = skip/bypass/fail). std::vector entryLatencies() const; // ── Editor host (Phase 2 — Reaper-style): editor của CHÍNH instance DSP // trong graph, không instance thứ 2 / không feeder. Audio thread // process() KHÔNG bao giờ chạm editor; mọi thao tác đi qua queue và chỉ // thực thi trong pumpEditorQueue() trên GUI thread (JUCE message thread). enum class EditorAction { Open, // tạo editor (createEditorIfNeeded) cho slot — giữ instance DSP Close, // huỷ editor, GIỮ instance DSP + state (mở lại nhanh) Show, // hiện editor + host window (M2) Hide, // ẩn editor + host window, GIỮ editor+instance (mở lại nhanh) Resize, // đổi kích thước editor (M2: host window) Capture // đọc state processor (getStateInformation) -> captured[slot] }; // Thread-safe (gọi từ bất kỳ thread nào, kể cả ctrl-reader): xếp lệnh. // w/h dùng cho Open/Resize (kích thước editor mong muốn). void requestEditor(uint32_t slot, EditorAction action, int w = 0, int h = 0); // GUI thread (JUCE message thread): drain queue + thực thi lệnh editor. // Trả true nếu xử lý >= 1 lệnh. Không gọi từ audio thread. bool pumpEditorQueue(); // Thread-safe đọc: slot có editor đang mở không. bool isEditorOpen(uint32_t slot) const; // Thread-safe: lấy + xoá state capture gần nhất của slot (rỗng nếu chưa). // Raw bytes (chưa base64) — M3 encode khi đẩy về engine Python. std::string takeCapturedState(uint32_t slot); // ── GUI host support (M2): GuiHost chạy trên GUI thread dùng các API này // để attach editor vào host window + teardown an toàn. Header giữ JUCE-free // (editorHandle trả void* = juce::Component*). // GUI thread: con trỏ editor Component của slot (null nếu chưa mở). Chỉ // gọi trên message thread, cùng lúc pumpEditorQueue(). void* editorHandle(uint32_t slot); // Thread-safe: số slot chain hiện tại (sau prepare; trước = 0). uint32_t slotCount() const; // Thread-safe: có editor nào đang mở không (teardown đồng bộ). bool hasOpenEditor() const; // Thread-safe: enqueue Close cho MỌI slot đang mở — main thread gọi trước // engine.prepare()/shutdown() khi GUI thread đang chạy (editor là JUCE // Component, phải đóng trên message thread; GuiHost chờ + pump). void closeAllEditors(); // Thread-safe đọc: editor slot đang hiển thị (Show/Hide) không. bool isEditorVisible(uint32_t slot) const; // GUI host: có việc editor cần tick nhanh không (queue lệnh chờ / editor // đang mở / retry Open chờ). Gọi từ GUI thread (retryOpens GUI-thread-only). bool guiBusy() const; void shutdown(); private: struct Slot; // G2: một mục fx_chain (type/path/presetB64/bypass) struct Impl; std::unique_ptr impl_; };