- vst_engine: list_scan_all cache co TTL 300s (FX_SCAN_TTL) + chi cache khi
find_fx_bridge_exe() co exe (thieu bridge -> tra [] NHUNG khong cache de
lan sau thu lai) — truoc day ket [] vi mui gio, MASTERING PANEL rong.
- vst_engine: them invalidate_fx_scan() clear cache scan VST.
- plugins.py: POST /scan (Plugin Manager Scan) goi _invalidate_fx_scan_cache()
-> MASTERING PANEL lan mo ke tiep quet lai voi dirs moi; response them
fx_cache_invalidated.
- app.jsx: MasteringModal/FXRackModal load listFx() lai moi lan mo modal
(deps [isOpen]/[track.id]) + loading state; VstPluginPicker nhan prop
loading -> hien 'Dang quet VST FX...' thay vi 'Khong co VST FX'.
- precompiled rebuild (LF) + index.html ?v=202608220330; dist sync.
- tests: +4 test cache TTL/invalidate/no-bridge (TestFxScanCache).
- RenderFxJob: safeScanModule chay tren worker thread + TerminateThread neu
Module::create treo >30s (Ozone 11 Clarity treo vinh vien, SEH khong bat
hang) -> scan khong bao gio ket thuc -> list_fx() timeout -> fx_paths rong
-> filter /available vo hieu -> nut Synth hien ca VST FX
- Module crash/hang -> emit is_fx=false,is_instrument=false (UNKNOWN) thay vi
is_instrument=true (truoc day xep VST FX crash vao danh sach instrument)
- vst_engine: list_scan_all() tra toan bo (FX+instrument+unknown); list_fx()
loc is_fx tu cache chung
- plugins.py /available: loai ca fx_paths lan unknown_paths khoi
vst_instruments -> nut Synth chi hien VSTi that
- fxRtStart: so chainKey signature — insert/remove/bypass/reorder VST trong
MASTER FX CHAIN giờ restart session SHM với chuỗi FX mới (trước đây
early-return khi active → VST mới không bao giờ được áp dụng realtime)
- masterBus.splitter (leftAnalyser/rightAnalyser) chuyển sang sau fxRt.node
khi realtime active → VU meter master strip + peak graph hiển thị audio
data ĐÃ QUA VST; khôi phục output khi stop
- MasteringModal outPeak/outRms/LUFS đọc masterBus.analyser (post-VST) thay
vì outputAnalyser (pre-VST)
- VKB: stopImmediatePropagation thay stopPropagation (keydown+keyup) — chặn
transport hotkey 'v' mở prompt Gain khi VKB đang mở (VKB đăng ký trước).
- TCP FX button: onClick -> window.__openFxRack(track.id, track.name), title
'Mở Track FX Chain', giữ caret ▾ (title 'FX nhanh (cũ)') cho dropdown legacy.
- VST FX GUI embed: copy fx_vst_bridge.exe vào bundle (engine.spec binaries),
find_bridge_exe tìm được trong _internal/ — hết lỗi 409 'Không tìm thấy bridge'.
Verified bằng Playwright: VKB không mở Gain prompt, FX chain rack mở + GUI VST
embed hiển thị (cả track FX rack lẫn Master FX Chain).
- calibrate: probe sandbox (jbridge) theo real-time pacing — child render qua SHM
consumer-sync, probe µs-speed chỉ đo được silence/attack → makeup=1.0 mọi channel.
Thêm virtual probeNeedsRealtime() (SandboxVst2Host → true), calibrate() Sleep
~1 block/5.8ms để child kịp nhận MIDI + render steady-state → makeup đo peak thật
(3 channel Qin ≈ 2.6, trước đây = 1.0 → âm lượng đồng đều giữa các track/VSTi).
- OPEN_GUI: SetWindowPos(HWND_TOPMOST) + SetForegroundWindow + BringWindowToTop
→ GUI VSTi hiển thị ở top window, tương tác ngay.
- /api/v1/plugins/available: filter vst_instruments + extra theo is_fx của
fx_vst_bridge --scan (trước merge entry) — modal Select Instrument chỉ còn
soundfont + VSTi, không hiện VST FX (90 → 33).
Root cause: child plugin_host render ~50x real-time (9323 blocks/s), bridge
đọc slot đang bị child ghi đè (torn read) và mỗi segment của 1 DAW block
đọc từ nhiều child block khác nhau -> discontinuity -> tạch thay vì âm thật.
- SandboxHostIPC: thêm bridgeConsumed[2] cuối struct (append-only, offsets
cũ giữ nguyên) — child đếm publish/slot, bridge đếm consume/slot.
- plugin_host_main.cpp: sau khi publish 1 block, chờ bridge consume xong
block đó mới render block kế (wait-sau-publish, không phải wait-trước-
render — wait-trước vẫn cho phép publish 2 block liên tiếp giữa 2 lần
consume -> bridge đọc latest sẽ bỏ block). Child giờ publish đúng 1
block/DAW block = real-time; bridge không bao giờ stall (main loop DAW
render + pace mọi iteration) nên không deadlock; nếu bridge chết,
parent_alive break wait.
- SandboxVst2Host: copy full 256 mẫu vào local buffer khi slot đổi, serve
mọi segment của 1 DAW block từ 1 child block nhất quán.
Verify: child pace 371 blocks/2s (real-time 375, trước 18647); domFreq của
waveform = 257.8Hz đúng nốt C4 (note 60) — âm thật, không phải noise;
correlation giữa các block liên tiếp âm đều ~-0.4 (waveform tuần hoàn);
GUI open path không deadlock (pace vẫn real-time, jBridge dialog + Qin_VST_
Window hiện đủ); GUI đóng -> child idle delta 0; silence_all master peak
0.000016.
- plugin_host_main.cpp WM_CLOSE: destroy host window (sau effEditClose
jBridge dialog cũ vẫn sống trong host; mở lại trên cùng window tạo
jBridge dialog thứ 2 chồng lên -> nền trắng, editor không load). Mở lại
tạo window mới sạch, đúng 1 jBridge dialog + Qin_VST_Window.
- plugin_host_main.cpp: bỏ idle-skip khi editor đang attach — preview phím
MIDI trong GUI là âm nội bộ plugin, không qua host MIDI; vòng loop idle
không gọi processAudioBlock nên voice nội bộ không bao giờ render (câm).
GUI mở -> render liên tục; GUI đóng vẫn idle như cũ.
- Verify: mở/đóng/mở lại GUI ch1 -> host mới, đúng 1 editor, âm ch1 peak
0.13 (GUI mở) / 0.10 (sau đóng); bridgeWrite tăng liên tục khi GUI mở.
- plugin_host_main.cpp: chặn WM_CLOSE (đóng GUI nhưng giữ window sống, hide
thay vì destroy), WM_DESTROY reset hwnd; dùng g_hostHwnd toàn cục thay
hwnd local để control OPEN_GUI/CLOSE_GUI tái sử dụng đúng window.
- main.cpp: bỏ qua dedupe hasAttachedView cho instrument child-own
(SandboxVst2Host) — bridge guiOpen_ stale sau khi child tự đóng nên
OPEN_GUI lặp lại phải forward control 4; child tự chặn double-open.
- Verify: WM_CLOSE -> window ẩn còn sống; OPEN_GUI lần 2 -> window hiện lại,
âm ch1 vẫn kêu (peak 0.15), không câm.
1) wavesynth tự phát khi mở app: probe calibrate() render vào live mix
(renderAll process cùng instance) -> thêm calibratingCh_[16], renderAll
bỏ qua channel đang calibrate; plugin_host panic (sustain off + all
notes off + 128 noteOff) ngay sau preset apply để startup im lặng.
2) bấm GUI mở 2 cửa sổ Qin: sandbox VST2 child tự sở hữu top-level window
-> ownsGuiWindow() + main.cpp forward OPEN_GUI control, không tạo native
parent / không registry double-open.
3) play phát nhạc cụ sai: restore clobber — seed preset đè patch thật trong
state file (ch3 63ba3667 -> 7f07473c) do saveStateJob chạy lúc child còn
seed. Fix: queuePreset(waitApply=true) chờ child apply xong (presetOut
memcmp), consume-before-apply (zero presetInLen trước khi apply), deferred
double-load re-check trong main.cpp skip assign trùng path, watchdog
respawn re-queue lastPreset_.
Makes the unlimited-Qin fix durable: bridge writes
%APPDATA%\jBridge\SettingsFilesFor{64,32}BitPlugins\Qin_RV.jBridge
at startup when missing, so every Qin instance runs in its own
auxhost on any machine. Verified: 4 Qin ch0/1/2/9 live, 4 auxhost.exe,
0 ML_RELEASE_ERROR, GUI attachView ok=1 on all.
Qin_RV_x64.dll (Kong Audio) crashed with SEH 0xc0000005 in Bridger64.dll
using the stale install binary (pre-ABI-fix). Copy native_bridge build
Release binary into install/ so the packaged app runs the fixed host.
Also open the native GUI after loading a VST2 instrument (previously
VST3-only), so Qin_RV's editor (Qin_VST_Window via auxhost.exe) shows
automatically on track load.
React onWheel is passive so e.preventDefault() was a no-op, letting the
TCP panel scroll and the ruler slip away from the cursor (no snap).
Add native non-passive capture listener that preventDefault()s wheel
events starting on [data-sf-wheel] ruler containers.
- serializeProjectToSchema now writes master volume_db and mastering gui_open_path/name
- deserialize restores masterVolume + masteringSettings.gui_open_path/name
- track vst_gui_open_path/name fallback to live state (not just _persistFxGui*)
- MasteringModal/FXRackModal reopen effects react to isOpen + restored gui path
- openMasterVstGui/openVstGui store gui name for reopen
- bridge: /preset capture endpoint + RenderFxJob.captureState for GUI knob state
Scroll wheel giảm/tăng volume chỉ hoạt động trên input 4px; chuyển
onWheel+onDoubleClick lên row div (label+dB span cũng ăn), giữ bubbling
từ input. Áp dụng cho main TCP và sub-tab, cả app.jsx và precompiled.
Close/reopen of Mastering panel or FX Rack unmounted the modal, losing
masterGuiOpen/fxGuiOpen state. The native bridge window stayed alive but
hidden, so reopening the panel showed no GUI and no /show was re-posted —
user could not interact with the VST GUI. Persist the embed state at module
scope so remount re-anchors and re-shows the window automatically.
- RenderFxJob: offscreen fx-gui window now WS_POPUP (borderless) so
GetWindowRect == client rect; anchored placement over the embed panel is
exact. WS_OVERLAPPEDWINDOW put the client ~31px lower/right -> cursor
jumped on hover and clicks hit the webview (relay rejected).
- AdjustWindowRect sizes to the window's own style (no caption for POPUP).
- FxGuiServer: mousemove only SetCursorPos while dragging (buttons!=0);
hover no longer teleports the cursor.
- FxGuiServer /show: skip SetWindowPos/resize when rect unchanged (frontend
now re-POSTs /show every ~500ms to re-anchor on scroll/resize).
- app.jsx VstGuiEmbed: drop shownRef one-shot; re-POST /show each refresh.
- Rebuilt bridge (6 locations) + Tauri release (embedded sidecar) + v=202608190002.