df4a22b9b4
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.
106 lines
4.2 KiB
C++
106 lines
4.2 KiB
C++
// native_bridge/juce_fx/gui_host_selftest.cpp
|
|
// M2 selftest (KHÔNG ship): GUI thread + host window trên instance DSP thật.
|
|
// juce_fx_gui_selftest.exe "<path.vst3>"
|
|
// Main thread = engine + GuiHost điều khiển; GUI thread riêng (BELOW_NORMAL)
|
|
// chạy MessageManager + tick 30ms (pump + reconcile). Main thread KHÔNG gọi
|
|
// JUCE GUI — mọi thao tác qua requestEditor queue (thread-safe) + poll.
|
|
// Plugin không có editor (createEditorIfNeeded null) → [FAIL] open — thử khác.
|
|
#include "JuceFxEngine.h"
|
|
#include "JuceFxGuiHost.h"
|
|
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
static int failures = 0;
|
|
|
|
static void check(bool ok, const char* what) {
|
|
std::printf("%s %s\n", ok ? "[PASS]" : "[FAIL]", what);
|
|
if (!ok) ++failures;
|
|
}
|
|
|
|
static void sleepMs(int ms) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
|
}
|
|
|
|
// Poll predicate tối đa timeoutMs (bước 10ms).
|
|
template <typename F>
|
|
static bool waitFor(F f, int timeoutMs) {
|
|
const auto t0 = std::chrono::steady_clock::now();
|
|
while (!f()) {
|
|
const auto el = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::steady_clock::now() - t0)
|
|
.count();
|
|
if (el > timeoutMs) return false;
|
|
sleepMs(10);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 2) {
|
|
std::fprintf(stderr, "usage: juce_fx_gui_selftest <path.vst3>\n");
|
|
return 2;
|
|
}
|
|
// Escape backslash + quote cho JSON path.
|
|
std::string path(argv[1]), jpath;
|
|
jpath.reserve(path.size() + 8);
|
|
for (char c : path) {
|
|
if (c == '\\' || c == '"') jpath += '\\';
|
|
jpath += c;
|
|
}
|
|
|
|
JuceFxEngine engine;
|
|
const std::string chain =
|
|
"[{\"type\":\"vst3\",\"path\":\"" + jpath + "\",\"bypass\":false}]";
|
|
engine.setChain(chain);
|
|
|
|
JuceFxGuiHost host(engine);
|
|
host.start();
|
|
// M2b: 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). Prepare chạy trên GUI thread;
|
|
// audio thread chỉ gọi engine.process (JUCE cho phép cross-thread).
|
|
host.runOnGui([&] { engine.prepare(48000, 256); });
|
|
|
|
int rc = 1;
|
|
// Open editor 640x480 — GUI thread tạo editor + PluginWindow (host native).
|
|
engine.requestEditor(0, JuceFxEngine::EditorAction::Open, 640, 480);
|
|
check(waitFor([&] { return engine.isEditorOpen(0) && host.windowCount() == 1; }, 5000),
|
|
"open: isEditorOpen(0) && windowCount==1 (editor + host window)");
|
|
|
|
sleepMs(3000); // chừa thời gian render — chụp màn hình bên ngoài
|
|
|
|
// Hide: editor + instance GIỮ (mở lại nhanh), window ẩn.
|
|
engine.requestEditor(0, JuceFxEngine::EditorAction::Hide);
|
|
check(waitFor([&] { return !engine.isEditorVisible(0) && engine.isEditorOpen(0); }, 3000),
|
|
"hide: !isEditorVisible(0) nhưng editor vẫn open");
|
|
check(host.windowCount() == 1, "hide: windowCount vẫn 1 (giữ editor+instance)");
|
|
|
|
// Show lại.
|
|
engine.requestEditor(0, JuceFxEngine::EditorAction::Show);
|
|
check(waitFor([&] { return engine.isEditorVisible(0); }, 3000),
|
|
"show: isEditorVisible(0) true lại");
|
|
|
|
// Resize 700x500 — setSize editor; window resizeToFit co theo.
|
|
engine.requestEditor(0, JuceFxEngine::EditorAction::Resize, 700, 500);
|
|
sleepMs(1500);
|
|
check(host.windowCount() == 1 && engine.isEditorOpen(0),
|
|
"resize: editor vẫn mở sau Resize 700x500");
|
|
|
|
sleepMs(2000); // chừa render sau resize — chụp màn hình bên ngoài
|
|
|
|
// Close: window bị reconcile xoá → windowCount 0.
|
|
engine.requestEditor(0, JuceFxEngine::EditorAction::Close);
|
|
check(waitFor([&] { return !engine.isEditorOpen(0) && host.windowCount() == 0; }, 3000),
|
|
"close: !isEditorOpen(0) && windowCount==0 (window xoá)");
|
|
|
|
host.runOnGui([&] { engine.shutdown(); }); // graph clear trên GUI thread
|
|
host.stop(); // đóng editor + dừng GUI thread + join
|
|
|
|
rc = (failures == 0) ? 0 : 1;
|
|
std::printf(failures == 0 ? "GUI SELFTEST OK\n" : "GUI SELFTEST FAILED\n");
|
|
return rc;
|
|
}
|