M5: capture editor state -> preset_b64 khi đóng GUI native (JuceFxLoop ghi <job>.preset.json + /fx-gui/close poll)

This commit is contained in:
2026-09-04 11:49:42 +07:00
parent 14fd794e6d
commit 014b9ac460
3 changed files with 88 additions and 1 deletions
+49
View File
@@ -21,6 +21,7 @@
#include <algorithm>
#include <chrono>
#include <cstring>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <iterator>
@@ -79,6 +80,23 @@ std::string extractChainJson(const std::string& s) {
return s.substr(p, i - p + 1);
}
// M5: base64 encode (RFC 4648) cho editor state capture -> file preset.
static std::string base64Encode(const std::string& in) {
static const char* tbl =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string out;
out.reserve(((in.size() + 2) / 3) * 4);
for (size_t i = 0; i < in.size(); i += 3) {
unsigned a = (unsigned char)in[i];
unsigned b = i + 1 < in.size() ? (unsigned char)in[i + 1] : 0;
unsigned c = i + 2 < in.size() ? (unsigned char)in[i + 2] : 0;
out += tbl[a >> 2];
out += tbl[((a & 3) << 4) | (b >> 4)];
out += i + 1 < in.size() ? tbl[((b & 15) << 2) | (c >> 6)] : '=';
out += i + 2 < in.size() ? tbl[c & 63] : '=';
}
return out;
}
} // namespace
int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
@@ -232,6 +250,37 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
engine.requestEditor(slot, act, w, h);
std::cerr << "[JuceFxLoop] ctrl " << key << " slot=" << slot
<< " w=" << w << " h=" << h << std::endl;
// M5: capture state editor (GUI đóng → preset về project như
// WM_FXGUI_CAPTURE cũ). PumpEditorQueue (GUI tick 30ms) thực thi
// Capture → captured[slot] (raw). Ctrl thread đợi tối đa 3s rồi
// base64 + ghi file JSON cạnh job: <jobPath>.preset.json
// {slot, preset_b64}. Python /fx-gui/close poll file này (xoá file
// cũ trước — chỉ đọc file MỚI cho capture lần này).
if (act == JuceFxEngine::EditorAction::Capture) {
const std::string capFile = jobPath + ".preset.json";
std::remove(capFile.c_str());
std::string raw;
for (int i = 0; i < 300 && raw.empty(); ++i) {
sleepMs(10);
if (!ipc->h.running || ipc->h.state != FXRT_STATE_READY)
break;
raw = engine.takeCapturedState(slot);
}
if (!raw.empty()) {
const std::string b64 = base64Encode(raw);
std::ofstream f(capFile, std::ios::binary);
f << "{\"slot\":" << slot << ",\"preset_b64\":\""
<< b64 << "\"}" << std::endl;
std::cerr << "[JuceFxLoop] capture slot=" << slot
<< " -> " << capFile << " (" << raw.size()
<< " bytes, " << b64.size() << " b64)"
<< std::endl;
} else {
std::cerr << "[JuceFxLoop] capture slot=" << slot
<< " rỗng — plugin không có state" << std::endl;
}
}
}
sleepMs(2);
}