fix(fx-rt): het crackle master FX - limiter attack smooth + crossfade khi bat/tat chain
- NativeInstrumentEngine: kAttack 0.9->0.1 (het zipper khi sum nhieu track) - app.jsx: crossfade masterFxDryGain/masterFxGain thay hard-rewire graph luc activation/deactivation master chain FX; worklet bao 'started' -> ramp dung luc; PDC tre sau khi dry silent (tranh gap 80ms) - sf-fx-realtime.js: fade-in 512 mau luc FILL xong / resume sau underrun - index.html + addModule: cache-buster v=202608241200 - rebuild app.precompiled.js, copy bundle vao dist/ + src-tauri/resources/ - keo dai fix cac round truoc: FILL 16, FXRT_BURST_MS, REBUFFER, pacing, VST GUI click/focus, solo crackle - them harness + plans/patch scripts
This commit is contained in:
@@ -239,21 +239,18 @@ bool captureHwndToJpeg(HWND hwnd, std::vector<unsigned char>& out, int quality)
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
// --- real-input fallback ------------------------------------------------------
|
||||
// Some editors (JUCE, iZotope) reject synthetic PostMessage input entirely and
|
||||
// only react to real hardware input while their window is visible. Probe: if 2
|
||||
// consecutive clicks do not change the frame, switch this plugin to real-input
|
||||
// mode — briefly move the window topmost to the work-area corner, inject real
|
||||
// mouse events at the mapped screen position, then restore it off-screen.
|
||||
std::atomic<bool> g_realInput{false}; // plugin needs real hardware input
|
||||
// probe: hit (frame changed) resets g_miss; 2 consecutive misses switch
|
||||
// to real input. A one-off "hit" (e.g. initial settle repaint) must NOT
|
||||
// lock us onto PostMessage, so there is no permanent "detected" state.
|
||||
// --- real-input --------------------------------------------------------------
|
||||
// JUCE/iZotope editors reject synthetic PostMessage input entirely and only
|
||||
// react to real hardware input while their window is visible, so real input
|
||||
// is always on: on click the plugin window dances over the embed panel rect
|
||||
// (anchored) and real mouse events are injected at the mapped screen pos.
|
||||
// Probe-based fallback (4bb0db2 era) removed: frame-hash probing was defeated
|
||||
// by animated GUIs (meter repaint) — the frame always changes, g_miss never
|
||||
// reached 2, PostMessage stayed on and every click silently died.
|
||||
std::atomic<bool> g_realInput{true}; // always real hardware input
|
||||
std::atomic<bool> g_dance{false}; // window temporarily on-screen/topmost
|
||||
std::atomic<DWORD> g_parkUntil{0}; // safety restore deadline (GetTickCount)
|
||||
POINT g_savedCursor = {0, 0}; // cursor to restore after the dance
|
||||
int g_miss = 0; // consecutive no-response PostMessage clicks
|
||||
uint64_t g_preClickHash = 0;
|
||||
// Anchored mode: the frontend sends the embed panel's screen rect (physical
|
||||
// px) with every /input, so the real-input dance places the plugin window
|
||||
// exactly over the panel -- cursor never moves, no park/restore churn. Zero
|
||||
@@ -261,13 +258,6 @@ uint64_t g_preClickHash = 0;
|
||||
RECT g_embedRect = {0, 0, 0, 0};
|
||||
bool g_anchored = false; // window anchored over panel (stays until /close)
|
||||
|
||||
uint64_t frameHash(HWND hwnd) {
|
||||
std::vector<unsigned char> jpg;
|
||||
if (!captureHwndToJpeg(hwnd, jpg, 70)) return 0;
|
||||
uint64_t h = 1469598103934665603ull; // FNV-1a 64
|
||||
for (unsigned char b : jpg) { h ^= b; h *= 1099511628211ull; }
|
||||
return h;
|
||||
}
|
||||
|
||||
// Lift the window above the foreground app without making it TOPMOST (the
|
||||
// foreground-lock would otherwise swallow real clicks). Attaching to the
|
||||
@@ -390,25 +380,6 @@ void relayInput(HWND hwnd, const json_object_s* o) {
|
||||
// Mouseleave reports -1,-1: finish the drag without clicking a bogus point.
|
||||
const bool outside = x < 0 || y < 0;
|
||||
|
||||
// Probe while still on PostMessage: a changed frame means PostMessage
|
||||
// responds (reset miss); two consecutive no-response clicks mean the
|
||||
// editor rejects synthetic input — switch to real hardware input.
|
||||
if (!g_realInput) {
|
||||
if (isDown) {
|
||||
g_preClickHash = frameHash(hwnd);
|
||||
} else if (isUp && g_preClickHash) {
|
||||
Sleep(250);
|
||||
uint64_t nowHash = frameHash(hwnd);
|
||||
bool resp = nowHash != g_preClickHash;
|
||||
if (resp) {
|
||||
g_miss = 0; // PostMessage responded — keep it
|
||||
} else if (++g_miss >= 2) {
|
||||
g_realInput = true; // synthetic input rejected — go real hardware input
|
||||
g_miss = 0;
|
||||
}
|
||||
g_preClickHash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_realInput && (isDown || isUp || type == "mousemove" || type == "wheel")) {
|
||||
if (isDown) {
|
||||
@@ -583,17 +554,12 @@ void runHttpServer(HWND hwnd, std::atomic<bool>& stop) {
|
||||
if (!IsWindowVisible(hwnd))
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, -20000, -20000, 0, 0,
|
||||
SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
if (!g_realInput) {
|
||||
// Real-input active: the dance/anchor state is owned by the
|
||||
// input relay — /show must not yank the window off-screen.
|
||||
g_dance = false;
|
||||
g_anchored = false;
|
||||
g_parkUntil = 0;
|
||||
}
|
||||
// Dance/anchor state is owned by the input relay — /show must not
|
||||
// yank the window off-screen (real input is always on).
|
||||
sendResponse(c, 200, "application/json", "{\"ok\":true}", 11);
|
||||
} else if (req.path == "/preset") {
|
||||
std::string b64;
|
||||
fxGuiRequestCapture(hwnd, b64, 1500);
|
||||
fxGuiRequestCapture(hwnd, b64, 500); // 1500 blocked the HTTP worker, stalling /input (clicks)
|
||||
const std::string body = "{\"preset_b64\":\"" + b64 + "\"}";
|
||||
sendResponse(c, 200, "application/json", body.data(), (int)body.size());
|
||||
} else if (req.path == "/shm") {
|
||||
|
||||
Reference in New Issue
Block a user