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:
@@ -1639,6 +1639,7 @@ static void fxGuiFeedLoop(Vst3Fx* fx, HWND hwnd, std::shared_ptr<FxGuiShmCtl> ct
|
||||
FxRealtimeIPC* ipc = nullptr;
|
||||
std::string curName;
|
||||
uint64_t lastWrite = 0; // feeder-local: newest inWrite already peeked
|
||||
auto nextTick = std::chrono::steady_clock::now();
|
||||
while (IsWindow(hwnd)) {
|
||||
std::string name;
|
||||
{
|
||||
@@ -1665,7 +1666,16 @@ static void fxGuiFeedLoop(Vst3Fx* fx, HWND hwnd, std::shared_ptr<FxGuiShmCtl> ct
|
||||
lastWrite = ipc->h.inWrite;
|
||||
}
|
||||
}
|
||||
Sleep(2);
|
||||
// Pace the feeder to realtime (one FXRT_BLOCK per its audio duration,
|
||||
// 44.1k fallback) instead of Sleep(2) busy-spin (~3x realtime CPU).
|
||||
// Less CPU in the GUI process = less contention with the pump thread,
|
||||
// which was stalling /input (dead clicks) and /preset (crackle reloads).
|
||||
const uint32_t sr = (ipc && ipc->h.sampleRate > 0) ? ipc->h.sampleRate : 44100u;
|
||||
nextTick += std::chrono::microseconds(1000000ULL * FXRT_BLOCK / sr);
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now > nextTick + std::chrono::milliseconds(50))
|
||||
nextTick = now; // fell behind (plugin hiccup): resync, don't burst
|
||||
std::this_thread::sleep_until(nextTick);
|
||||
}
|
||||
if (v) fxGuiCloseShm(v);
|
||||
}
|
||||
@@ -1690,7 +1700,7 @@ static void fxGuiStartFeederThread(Vst3Fx* fx, HWND hwnd) {
|
||||
|
||||
// --- split setup for server mode (startup speed) -----------------------------
|
||||
// 1. Parse job { path, name } — fast. Returns 0 ok, 1 job/argument error.
|
||||
static int fxGuiParseJob(const std::string& jobPath, std::string& path, std::string& name, std::string& shm) {
|
||||
static int fxGuiParseJob(const std::string& jobPath, std::string& path, std::string& name, std::string& shm, std::string& presetB64) {
|
||||
std::ifstream jf(jobPath, std::ios::binary);
|
||||
if (!jf) { std::cerr << "[FxGui] cannot read job file: " << jobPath << std::endl; return 1; }
|
||||
std::string data((std::istreambuf_iterator<char>(jf)), std::istreambuf_iterator<char>());
|
||||
@@ -1705,6 +1715,7 @@ static int fxGuiParseJob(const std::string& jobPath, std::string& path, std::str
|
||||
path = memberString(job, "path", "");
|
||||
name = memberString(job, "name", "");
|
||||
shm = memberString(job, "shm", "");
|
||||
presetB64 = memberString(job, "preset_b64", "");
|
||||
if (path.empty()) { std::cerr << "[FxGui] job missing path" << std::endl; return 1; }
|
||||
return 0;
|
||||
}
|
||||
@@ -1746,9 +1757,15 @@ static int fxGuiCreateWindow(const std::string& name, bool offscreen,
|
||||
// 3. Load the plugin + attach the editor (SEH-guarded — plugin GUI code may
|
||||
// crash). Slow — runs on the pump thread in server mode, after HTTP is up.
|
||||
// Returns 0 ok, 2 plugin load or editor attach failure.
|
||||
static int fxGuiLoadAttach(Vst3Fx* fx, const std::string& path, const std::string& name, HWND hwnd) {
|
||||
static int fxGuiLoadAttach(Vst3Fx* fx, const std::string& path, const std::string& name, HWND hwnd, const std::string& presetB64) {
|
||||
// Fixed 44.1k/512 — GUI only, no audio processed here.
|
||||
if (!fx->load(path, 44100.0, 512)) { std::cerr << "[FxGui] plugin load failed: " << path << std::endl; return 2; }
|
||||
// Khôi phục preset GUI capture cuối (hide/show mất settings bug): áp
|
||||
// preset NGAY SAU load — trước attach editor để GUI hiện đúng cài đặt.
|
||||
if (!presetB64.empty() && !fx->applyPreset(presetB64))
|
||||
std::cerr << "[FxGui] preset apply FAILED (default kept): " << path << std::endl;
|
||||
else if (!presetB64.empty())
|
||||
std::cerr << "[FxGui] preset applied: " << path << std::endl;
|
||||
if (!fxGuiAttachSafe(fx, hwnd)) {
|
||||
std::cerr << "[FxGui] editor attach failed for " << path << std::endl;
|
||||
DestroyWindow(hwnd);
|
||||
@@ -1761,13 +1778,13 @@ static int fxGuiLoadAttach(Vst3Fx* fx, const std::string& path, const std::strin
|
||||
// Combined setup (legacy open mode): parse + load + window + attach, in order.
|
||||
static int fxGuiSetup(const std::string& jobPath, Vst3Fx* fx, HWND* hwndOut,
|
||||
bool offscreen, LONG_PTR userData, std::string& shmOut) {
|
||||
std::string path, name;
|
||||
int rc = fxGuiParseJob(jobPath, path, name, shmOut);
|
||||
std::string path, name, presetB64;
|
||||
int rc = fxGuiParseJob(jobPath, path, name, shmOut, presetB64);
|
||||
if (rc) return rc;
|
||||
if (!fx->load(path, 44100.0, 512)) { std::cerr << "[FxGui] plugin load failed: " << path << std::endl; return 2; }
|
||||
rc = fxGuiCreateWindow(name, offscreen, userData, hwndOut);
|
||||
if (rc) return rc;
|
||||
return fxGuiLoadAttach(fx, path, name, *hwndOut);
|
||||
return fxGuiLoadAttach(fx, path, name, *hwndOut, presetB64);
|
||||
}
|
||||
|
||||
int run_open_fx_gui(const std::string& jobPath) {
|
||||
@@ -1815,8 +1832,8 @@ int run_fx_gui_server(const std::string& jobPath) {
|
||||
// server — SF_FXGUI_PORT prints immediately. The slow VST load + editor
|
||||
// attach is deferred to the pump thread (onStarted), so plugins.py stops
|
||||
// blocking on a multi-second plugin load (startup speed issue).
|
||||
std::string path, name, shm;
|
||||
int rc = fxGuiParseJob(jobPath, path, name, shm);
|
||||
std::string path, name, shm, presetB64;
|
||||
int rc = fxGuiParseJob(jobPath, path, name, shm, presetB64);
|
||||
if (rc) return rc;
|
||||
rc = fxGuiCreateWindow(name, /*offscreen=*/true, 0, &hwnd);
|
||||
if (rc) return rc;
|
||||
@@ -1826,7 +1843,7 @@ int run_fx_gui_server(const std::string& jobPath) {
|
||||
|
||||
int loadRc = 0;
|
||||
const int srv = fxGuiServerLoop(hwnd, [&]() {
|
||||
loadRc = fxGuiLoadAttach(&fx, path, name, hwnd);
|
||||
loadRc = fxGuiLoadAttach(&fx, path, name, hwnd, presetB64);
|
||||
if (loadRc == 0) fxGuiStartFeederThread(&fx, hwnd); // after load+attach only
|
||||
if (loadRc != 0) PostMessageA(hwnd, WM_CLOSE, 0, 0); // exit; port already printed
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user