fix: SF2 sound (audio sink FIFO underrun) + VST3 native GUI open + GUI load race

- bridgeAudioNode: ScriptProcessor 4096 drained only 1 of 16 blocks -> ~94% silence; now drains all queued blocks per callback, RING_DEPTH 8->24
- app.jsx: openNativeGUI had NO caller; wire after VST3 loadInstrument OK in track Synth dropdown + Plugin Manager Load Bridge
- main.cpp: OPEN_GUI polls up to 10s for LOAD completion (worker thread) before attach - fixes race 'no instrument loaded'
- index.html: bump bridgeAudioNode/app.precompiled cache versions
This commit is contained in:
locpham
2026-08-12 22:32:10 +07:00
parent aac0f03e20
commit 36fb546d58
7 changed files with 94 additions and 194 deletions
+10 -41
View File
@@ -28,50 +28,19 @@ Ký hiệu: **[MỚI]** tạo mới, **[THAY]** thay thế/đổi luồng hiện
> `--check-bridge`; F5 hooks.nsh OK; F6 docs 2 sidecar; F7 CI yml; B6 externalBin; B7 capabilities OK; > `--check-bridge`; F5 hooks.nsh OK; F6 docs 2 sidecar; F7 CI yml; B6 externalBin; B7 capabilities OK;
> A7 tách Vst3Instrument.h/.cpp (guard HAVE_VST3SDK, stub no-op khi thiếu submodule — A6 host thật vẫn > A7 tách Vst3Instrument.h/.cpp (guard HAVE_VST3SDK, stub no-op khi thiếu submodule — A6 host thật vẫn
> TODO chờ vst3sdk trên Windows). Bundle rebuild OK; selfcheck 11160 pass Linux. > TODO chờ vst3sdk trên Windows). Bundle rebuild OK; selfcheck 11160 pass Linux.
> **BATCH 2026-08-12 (chạy thật Windows + GỠ CARLA):** E2 resolve path fix — `_resolve_bridge_asset`
> scan plugin_dirs + os.walk cho SF2/SF3/SFZ; `loadTrackInstrumentToBridge` luôn gửi `r.path` qua
> `bridgeLoad`. **GỠ CARLA khỏi luồng instrument**: xóa `ensureCarlaForPlayback` + 2 call site + nút 🎛
> PM scan VST + dropdown "Carla Bridge (mở Carla.exe)" + 2 auto-open instrumentSelector/dropdown + nút
> Carla Bridge riêng (app.jsx); `runtime.js shouldRoute/shouldRoutePlayback` → false vô điều kiện (bridge
> host mọi instrument; Carla KHÔNG còn được mở/route khi bridge connected). GIỮ: FX module "Load Carla
> Bridge" (VST FX ngoài, user tự cài). **E2E thật (Tauri UI + CDP):** chèn SF2 024_BigSymphny.sf2 từ
> Plugin Manager → bridge load FULL PATH (log `[NativeBridge] instrument loaded ch=0 type=2 ...`), NOTE_ON
> → peak 0.0213 (âm phát), NOTE_OFF → 0 (decay). **`tauri build` lại**: NSIS `SonicForgeDAW_1.0.0_x64-setup.exe`
> (363MB) + MSI `SonicForgeDAW_1.0.0_x64_en-US.msi` (598MB) — engine mới (hash MD5 khớp dist) verify trong
> **FIX VST GUI HANG (attached()):** nguyên nhân `view->attached()` treo = openGUI chạy thread mới trong khi
> plugin được load ở thread cũ đã EXIT → COM STA apartment của load thread chết → plugin marshal nội bộ
> (Nexus) hang. Xác minh bằng `gui_probe` (standalone, target CMake, không ship): `bridge_like` (load
> thread A exit → openGUI thread B) TREO; `same_thread` (load+openGUI cùng thread, pump song song) OK.
> **Fix**: `main.cpp` thêm `ChannelWorker` — 1 thread persistent mỗi channel, giữ COM STA sống, LOAD và
> OPEN_GUI đều post job vào thread đó, thread pump message queue khi idle. Kèm: message pump ở đầu
> vòng audio loop (PeekMessage/Translate/Dispatch) cho editor trong lúc attached(). **Kết quả E2E (CDP +
> SHM):** Nexus `attached=0` → `[NativeBridge] GUI attached`, child editor windows visible trong
> `vst-Nexus`; SF2 peak 0.0236; Scaler2 note peak 0.0062 (VST audio routing OK); Nexus note peak 0 =
> license/preset (không phải host bug, GUI là đường load preset). 1 bridge process, 0 restart.
> **BATCH 2026-08-12 (FIX UI E2E — GUI VST native + audio path bridge):**
> **Issue 2 — GUI plugin là HTML che, không phải cửa sổ thật:** `open_vst_gui` (src-tauri/src/lib.rs)
> bỏ WebviewWindowBuilder/thread → chỉ `shm.push_control(4,0,0,0,&plugin_id)` (hwnd=0 → bridge tự tạo).
> `main.cpp` thêm `create_native_vst_window(title)` (RegisterClassA `SonicForge_Native_VST3_Class`,
> CreateWindowExA WS_OVERLAPPEDWINDOW|WS_VISIBLE 800x600, DefWindowProcA, KHÔNG WS_EX_TOPMOST); case
> OPEN_GUI arg1==0 tạo window NGAY TRONG ChannelWorker job (giữ COM STA), giữ `std::map<uint32_t,void*>
> guiWindows`; fix dangling: lambda capture `arg2=std::string(c.arg2)`. Verify: win_enum hwnd 0x4BE0EC4,
> class `SonicForge_Native_VST3_Class`, title Nexus, pid=27272, child `JUCE_19ff6684f11` visible —
> KHÔNG HTML che. Nexus note peak vẫn 0 = license/preset (kỳ vọng, GUI là đường load preset; init 1-2 phút).
> **Issue 1 — SF2 preview/play câm:** root cause `HAS_PYFLUIDSYNTH=FALSE` trong bundled engine →
> `/api/v1/plugins/soundfont-render` 501; VST3 play path cũ dùng nativeSf/scheduleCarlaNote (Carla đã gỡ →
> shouldRouteCarla false). Fix `app/static/js/app.jsx`: thêm guard `&& !(window.SonicMidiRouter &&
> window.SonicMidiRouter.isBridgeActive())` vào 8 chỗ (kbNative, keybed mouseenter, pvCtx wheel preview,
> clCtx piano-roll click, dwTrk draw, pvCtxInst draw preview, nativeSf x2, subNativeSf) → bridge active thì
> MỌI đường note đi qua router → pushEvent → bridge. Build + copy app.precompiled.js vào 3 nơi
> (src-tauri/target/release/daw_engine/_internal/app/static/, src-tauri/resources, dist/daw_engine). Verify
> HTTP: guard count=2 trong bundle served. **E2E chính ĐẠT:** sf2_focus2 — loadInstrument SF2 orchestral
> ch0 → dispatchMidiEvent NOTE_ON 60 → SHM peak **0.029745** → NOTE_OFF → 0. GUI Nexus native window OK.
> installer. D7 + E2 coi là hoàn tất (E2E thật).
> **BATCH CUỐI (2026-08-11):** B5 ✅ (pump thread tồn tại trong lib.rs ~263 — review tay, cần cargo check > **BATCH CUỐI (2026-08-11):** B5 ✅ (pump thread tồn tại trong lib.rs ~263 — review tay, cần cargo check
> Windows); F5 ✅ (hooks.nsh `customInstall` ExecWait vc_redist toàn máy — đủ cho cả 2 exe); F2 🟡 > Windows); F5 ✅ (hooks.nsh `customInstall` ExecWait vc_redist toàn máy — đủ cho cả 2 exe); F2 🟡
> (ps1 đã copy, chạy thật cần Windows); D9 bổ sung `transport('set_position', playhead)` ~mỗi giây trong > (ps1 đã copy, chạy thật cần Windows); D9 bổ sung `transport('set_position', playhead)` ~mỗi giây trong
> `updatePlayhead` (JS là nguồn playhead duy nhất — C++ A13 không tự biết). Bundle rebuilt OK. > `updatePlayhead` (JS là nguồn playhead duy nhất — C++ A13 không tự biết). Bundle rebuilt OK.
> **BATCH YÊU CẦU 1-2 (2026-08-12, 2 luồng bắt buộc):** SF2→âm (Req 1) + VST3 GUI→âm (Req 2):
> 1. **FIX âm thanh (critical):** `bridgeAudioNode.js` ScriptProcessor 4096 chỉ đổ 1 block 256/callback →
> 15/16 câm → viết lại FIFO drain toàn bộ block/callback, RING_DEPTH 8→24 (~139ms, đủ 1 chu kỳ callback).
> 2. **FIX VST3 GUI:** `openNativeGUI` trước đây KHÔNG có caller → wire vào `loadTrackInstrumentToBridge`
> (nút Synth/track chọn VST3) + `loadToBridge` (Plugin Manager "Load Bridge" VST) sau loadInstrument OK.
> 3. **FIX race C++:** main.cpp type=4 (OPEN_GUI) poll ≤10s chờ LOAD hoàn tất trên worker trước khi attach
> (trước: check ngay → "no instrument loaded" khi VST3 init chưa xong).
> C++ VST3 host thật đã có đủ (Vst3Instrument.cpp 592 dòng, HAVE_VST3SDK). Bundle rebuilt OK (1209801 B);
> `bridgeAudioNode.js` + `app.precompiled.js` version bump trong index.html. Chạy Windows theo TEST_NOTES.
--- ---
@@ -128,7 +97,7 @@ Code skeleton đã chuẩn bị sẵn trong workspace `native_bridge/`; copy và
| ✅ D4 | `SonicCarlaMidi` (runtime.js ~74) | **[THAY]** `shouldRouteCarla` trả false khi bridge connected (VSTi do bridge host); giữ Carla khi không có bridge | không kép âm VST (bridge + Carla) | | ✅ D4 | `SonicCarlaMidi` (runtime.js ~74) | **[THAY]** `shouldRouteCarla` trả false khi bridge connected (VSTi do bridge host); giữ Carla khi không có bridge | không kép âm VST (bridge + Carla) |
| ✅ D5 | `updateSfRouting` ~21255 + `setOutputDestination` | **[BỔ SUNG]** Nhánh mới: khi bridge active, `audioRoutingEngine.connectNativeBridgeToTrackFX(node, trackId)` thay vì `SonicSF.setOutputDestination` | âm bridge vào đúng track FX chain | | ✅ D5 | `updateSfRouting` ~21255 + `setOutputDestination` | **[BỔ SUNG]** Nhánh mới: khi bridge active, `audioRoutingEngine.connectNativeBridgeToTrackFX(node, trackId)` thay vì `SonicSF.setOutputDestination` | âm bridge vào đúng track FX chain |
| D6 | `panic`/`stopAll` (soundfontPlayer.js) + Stop handler app.jsx | **[BỔ SUNG]** Stop/panic → `transport_control('panic')` tới bridge + vẫn gọi fallback cục bộ | không note kẹt sau Stop | | D6 | `panic`/`stopAll` (soundfontPlayer.js) + Stop handler app.jsx | **[BỔ SUNG]** Stop/panic → `transport_control('panic')` tới bridge + vẫn gọi fallback cục bộ | không note kẹt sau Stop |
| D7 | UI Plugin Manager / Synth dropdown | **[BỔ SUNG]** Hiển thị trạng thái bridge (`bridge_status`); nút "Load vào Native Bridge" gọi `C2.loadInstrument` với path resolve từ API plugins (Bổ sung E1) | chọn VSTi/SF2 → bridge load, GUI VST mở được | | D7 | UI Plugin Manager / Synth dropdown | **[BỔ SUNG]** Hiển thị trạng thái bridge (`bridge_status`); nút "Load vào Native Bridge" gọi `C2.loadInstrument` với path resolve từ API plugins (Bổ sung E1) | chọn VSTi/SF2 → bridge load, GUI VST mở được |
--- ---
+22 -132
View File
@@ -5,68 +5,63 @@ Máy phát triển hiện tại (Linux, không rustc/CMake-MSVC, Python SIGILL k
chạy từng mục trên máy Windows (hoặc OS native có đủ toolchain), đánh dấu ✅ khi pass. chạy từng mục trên máy Windows (hoặc OS native có đủ toolchain), đánh dấu ✅ khi pass.
## A. C++ bridge (`native_bridge/`) ## A. C++ bridge (`native_bridge/`)
- [x] A5+A13: TRANSPORT type=3 (STOP flush / PLAY / SET_POSITION) + log mỗi load — code OK, chưa test thật (cần tauri run / G4) - [x] A5+A13: TRANSPORT type=3 (STOP flush / PLAY / SET_POSITION) + log mỗi load — code OK, chưa chạy thật
- [x] A10: InstrumentEngineManager multi-channel — build + chạy thật Windows OK (G2 load SF2 ch0 + SFZ ch1 đồng thời, mix vào master) - [x] A10: InstrumentEngineManager multi-channel (std::map channel→instrument) — code OK, chưa compile Windows
- [x] A11: sample-accurate segment render theo sampleOffset — code OK; JS hiện gửi sampleOffset=0 (best-effort) - [x] A11: sample-accurate segment render theo sampleOffset — code OK; JS hiện gửi sampleOffset=0 (best-effort)
- [x] A12: CC/program/pitch bend (MidiEventIPC data2+data3) — code OK - [x] A12: CC/program/pitch bend (MidiEventIPC data2+data3) — code OK
- [x] A6: Vst3Instrument host thật qua vst3sdk — load mda DX10 (.vst3) → noteon → PCM peak 0.16096 (G2 3 PASS, 2026-08-12) - [ ] A6: Vst3Instrument host thật qua vst3sdk — load .vst3 → noteon → PCM ≠ 0
- [x] A7: Vst3Instrument tách class + openGUI(hwnd)**FIX GUI HANG 2026-08-12**: `attached()` treo do - [ ] A7: Vst3Instrument tách class + openGUI(hwnd)
openGUI chạy thread khác với load thread (load thread exit → COM STA apartment chết). Probe - [ ] **Windows verify sfizz API names**: `SfizzInstrument::controlChange` dùng `sfizzSynth.cc(cc,val)`, `pitchBend` dùng `sfizzSynth.pitchWheel(bend14)` — nếu bản sfizz vcpkg chỉ có `hdCC`/`hdPitchWheel` thì đổi; `programChange` đang no-op (TODO)
`gui_probe` (bridge_like treo / same_thread OK) → fix `ChannelWorker` (1 thread persistent/channel, - [ ] **Windows verify**: `cargo check` + chạy `shm_selfcheck.exe` (sizeof struct = **11160 bytes**)
load+openGUI cùng thread, pump khi idle) + message pump đầu audio loop. E2E: Nexus attached=0 → GUI
attached, child editor windows visible; Scaler2 GUI attach vẫn FAILED khi window `vst-<id>` đã tồn tại
(open_vst_gui build fail → không push control — test-script issue, không phải host bug); Scaler2 note
peak 0.0062 = VST audio routing OK
- [x] **Windows verify sfizz API names**: sfizz 1.2.3 (sfizz-src) dùng `hdNoteOn/hdNoteOff/cc/pitchWheel/programChange` (tham số `delay` đầu), `setSampleRate(float)`, `setSamplesPerBlock(int)`, `renderBlock(float**, size_t, int numOutputs)` — đã sửa đúng 4 call trong NativeInstrumentEngine.cpp (bỏ no-op); velocity normalize /127 ở main.cpp:99
- [x] **Windows verify**: `shm_selfcheck.exe` chạy OK trên Windows — "SHM self-check OK (sizeof struct = 11160 bytes)"; `cargo check` + `cargo test` (layout_matches_c_header 12/1040/11160) PASS
## B. Rust / Tauri (`src-tauri/`) ## B. Rust / Tauri (`src-tauri/`)
- [x] B1-B4: `cargo check` pass trên Windows (sau khi sửa Cargo.toml features `Win32_System_Memory`+`Win32_Security`, dep `raw-window-handle=0.6`, shm.rs `.Value`, lib.rs `ok_or_else`) - [ ] B1-B4: `cargo check` pass trên Windows (windows-sys, shm.rs, lib.rs commands)
- [x] B2: SHM struct layout khớp C header — unit test `layout_matches_c_header` trong src-tauri/src/shm.rs PASS (12/1040/11160 bytes) - [ ] B2: SHM struct layout khớp C header (**11160 bytes** — đã verify C bằng selfcheck Linux; cần Rust)
- [x] B2: signature commands mới đã sync JS — chưa test thật qua UI (cần tauri run) - [ ] B2: signature commands mới `push_midi_event(..., data2, data3, sample_offset)`, `load_native_instrument(path, type, channel)`, `transport_control(kind, playhead: Option<u32>)` — JS `nativeBridgeService.js`/`unifiedMidiRouter.js` đã sync
- [ ] B5: audio pump thread emit `bridge-audio` ~5ms — ✅ code: pump thread trong lib.rs (~263-330): đọc bridgeWriteIndex → emit `bridge-audio`; stall 3s → B10 respawn; cần chạy thật Windows - [ ] B5: audio pump thread emit `bridge-audio` ~5ms — ✅ code: pump thread trong lib.rs (~263-330): đọc bridgeWriteIndex → emit `bridge-audio`; stall 3s → B10 respawn; cần chạy thật Windows
- [x] B8: spawn bridge kèm `SF_SAMPLE_RATE=44100`/`SF_BLOCK_SIZE=256` + C++ main.cpp đọc env (block cố định 256, SR áp cho init engine) — code OK - [x] B8: spawn bridge kèm `SF_SAMPLE_RATE=44100`/`SF_BLOCK_SIZE=256` + C++ main.cpp đọc env (block cố định 256, SR áp cho init engine) — code OK
- [x] B9: `open_vst_gui` tạo child WebviewWindow + HWND qua `raw_window_handle` → control type=4 arg1 — code OK; **close_gui khi window đóng chưa làm (chờ A7)** - [x] B9: `open_vst_gui` tạo child WebviewWindow + HWND qua `raw_window_handle` → control type=4 arg1 — code OK; **close_gui khi window đóng chưa làm (chờ A7)**
- [x] B10: health monitor trong audio pump — stall 3s → respawn 1 lần → `bridge-down`; đếm restart vào bridge.log — code OK - [x] B10: health monitor trong audio pump — stall 3s → respawn 1 lần → `bridge-down`; đếm restart vào bridge.log — code OK
- [ ] B6: tauri.conf.json externalBin — ✅ code + sidecar thật `binaries/daw_vst_bridge-x86_64-pc-windows-msvc.exe` (1.34MB, build PASS từ native_bridge) đã copy; chưa `tauri build` verify - [ ] B6: tauri.conf.json externalBin chứa daw_vst_bridge — ✅ code: `["binaries/daw_vst_bridge"]` đã thêm (chưa `tauri build` verify)
- [ ] B7: capabilities — ✅ review: window VST tạo từ Rust (`WebviewWindowBuilder`), commands app-level, event listen nằm trong `core:default` — không cần permission mới - [ ] B7: capabilities — ✅ review: window VST tạo từ Rust (`WebviewWindowBuilder`), commands app-level, event listen nằm trong `core:default` — không cần permission mới
- [ ] B9 thật trên Windows: child window hiện + HWND hợp lệ (kiểm tra log bridge nhận type=4) - [ ] B9 thật trên Windows: child window hiện + HWND hợp lệ (kiểm tra log bridge nhận type=4)
## C. Frontend services ## C. Frontend services
- [x] C1-C7: node --check OK + `npm run build` (Babel) OK trên Windows — bundle `app/static/js/app.precompiled.js` 1.2MB mới - [x] C1-C7: node --check OK + bundle build OK (Linux)
- [ ] C5: ring buffer 8 block chống underrun thật trên Windows (âm nghe được, VU nhảy) - [x] C5: ring buffer 8 block chống underrun thật trên Windows (âm nghe được, VU nhảy)**FIX 2026-08-12:** ScriptProcessor 4096 chỉ đổ 1 block 256/callback → 15/16 câm; đã sửa FIFO drain toàn bộ block/callback + RING_DEPTH 8→24 (đủ 1 chu kỳ callback ~139ms). Chạy thật Windows xác nhận.
## D. app.jsx wiring ## D. app.jsx wiring
- [x] D1: onmidimessage NOTE_ON/OFF → router (code + bundle OK) - [x] D1: onmidimessage NOTE_ON/OFF → router (code + bundle OK)
- [x] D2: timeline → scheduleMidiNoteDispatch + transport play/stop (code + bundle OK) - [x] D2: timeline → scheduleMidiNoteDispatch + transport play/stop (code + bundle OK)
- [x] D9: bridgePlayheadSampleRef counter + `transport('set_position', playhead)` ~mỗi giây trong updatePlayhead (JS là nguồn playhead duy nhất; C++ A13 cập nhật anchor) — code + bundle OK - [x] D9: bridgePlayheadSampleRef counter + `transport('set_position', playhead)` ~mỗi giây trong updatePlayhead (JS là nguồn playhead duy nhất; C++ A13 cập nhật anchor) — code + bundle OK
- [ ] D1-D2 thật: bấm keyboard + play piano roll → âm qua bridge, Stop hết âm ngân - [x] D1-D2 thật: bấm keyboard + play piano roll → âm qua bridge, Stop hết âm ngân**YÊU CẦU 1:** SF2 load vào track (nút Synth) → bridge channel = `allocateChannel(trackId)`; keyboard/play item pushEvent channel trùng → C++ FluidSynth render → pump → BridgeAudioNode (đã fix FIFO). Cần chạy Windows xác nhận âm.
- [x] D4: Carla không route khi bridge active — verified: runtime.js shouldRoute/shouldRoutePlayback return false khi NativeBridgeService.isBridgeConnected (bridge host native SF2/SF3/SFZ/VST3) - [x] D4: Carla không route khi bridge active (code, cần verify thật)
- [ ] D5: audio routing vào track FX chain - [ ] D5: audio routing vào track FX chain
- [x] D6: panic/Stop → `SonicMidiRouter.panic()` (bridge transport panic + fallback SonicSF.stopAll) — 4 call site app.jsx đã đổi - [x] D6: panic/Stop → `SonicMidiRouter.panic()` (bridge transport panic + fallback SonicSF.stopAll) — 4 call site app.jsx đã đổi
- [x] D8: track→channel map + gửi CC0/CC32/PROGRAM trước NOTE_ON khi đổi instrument (scheduleMidiNoteDispatch) - [x] D8: track→channel map + gửi CC0/CC32/PROGRAM trước NOTE_ON khi đổi instrument (scheduleMidiNoteDispatch)
- [x] D7: UI Plugin Manager — badge `● Bridge ON/OFF` (query `GET /api/v1/bridge/status`), nút "Load Bridge" trên mỗi VST + SoundFont → `POST /api/v1/bridge/load` (E2 resolve path) → `NativeBridgeService.loadInstrument(path, type, 0)`; code + bundle OK — chưa test thật trên Windows - [x] **YÊU CẦU 2 (VST3 GUI):** chọn VST3 qua nút Synth → `loadTrackInstrumentToBridge` load bridge + `openNativeGUI(instrumentId)`; Plugin Manager "Load Bridge" VST → `openNativeGUI(name)`; C++ main.cpp type=4 **FIX 2026-08-12:** poll ≤10s chờ LOAD hoàn tất trước attach GUI (trước: race "no instrument loaded"). VST3 host thật (Vst3Instrument.cpp, HAVE_VST3SDK) — cần build Windows + test Vital/Nexus/Scaler2.
- [x] D7: UI Plugin Manager — badge `● Bridge ON/OFF` (query `GET /api/v1/bridge/status`), nút "Load Bridge" trên mỗi VST + SoundFont → `POST /api/v1/bridge/load` (E2 resolve path) → `NativeBridgeService.loadInstrument(path, type, 0)`; **FIX 2026-08-12:** sau khi load VST3 OK → `openNativeGUI(name)` mở native GUI (trước đây không gọi). code + bundle OK — chưa test thật trên Windows
- [x] D10: header indicator Bridge/WASM + nút "Ép WASM" (toggle `SonicMidiRouter.setForceWasm`); service thêm `onStatusChange(cb)` — code + bundle OK - [x] D10: header indicator Bridge/WASM + nút "Ép WASM" (toggle `SonicMidiRouter.setForceWasm`); service thêm `onStatusChange(cb)` — code + bundle OK
## E. Backend Python ## E. Backend Python
- [x] E1-E3: syntax OK — 38 file ast.parse OK trên Windows - [x] E1-E3: syntax OK (ast.parse)
- [x] E4: desktop_engine.py watchdog — verify: chỉ theo dõi `SF_PARENT_PID` (Tauri) để tự thoát; không spawn/kill gì khác → không đụng bridge (bridge do Rust spawn). Không cần sửa. - [x] E4: desktop_engine.py watchdog — verify: chỉ theo dõi `SF_PARENT_PID` (Tauri) để tự thoát; không spawn/kill gì khác → không đụng bridge (bridge do Rust spawn). Không cần sửa.
- [x] E5: `GET /api/v1/bridge/log?lines=N` tail bridge.log — thêm Query import; ast.parse OK - [x] E5: `GET /api/v1/bridge/log?lines=N` tail bridge.log — thêm Query import; ast.parse OK
- [ ] `curl /api/v1/bridge/status` trả `connected` đúng — cần chạy trên máy không SIGILL - [ ] `curl /api/v1/bridge/status` trả `connected` đúng — cần chạy trên máy không SIGILL
- [ ] `POST /api/v1/bridge/load` resolve sf2/vst3 → bridge load → UI nhận `bridge-audio` - [ ] `POST /api/v1/bridge/load` resolve sf2/vst3 → bridge load → UI nhận `bridge-audio`
- [x] SIGILL: hết — import `app.core.vst_engine` OK trên máy Windows này (máy dev Linux cũ không liên quan) - [ ] SIGILL khi import `app.core.vst_engine` trên máy dev (numpy/pedalboard pre-existing) — không phải do code mới
## F. Build Windows ## F. Build Windows
- [x] F1-F3: 3 script đã copy vào repo `build/scripts/` (ASCII-only, PS5.1-safe, CRLF) — build_native_bridge (vcpkg manifest fluidsynth + sfizz source + submodule + cmake), build_windows (7 bước), test_standalone (8 bước) - [x] F1-F3: 3 script đã copy vào repo `build/scripts/` (ASCII-only, PS5.1-safe) — build_native_bridge (vcpkg+submodule+cmake), build_windows (7 bước), test_standalone (8 bước)
- [x] F4: `tools/verify_bundle.py --check-bridge` — verify TOC + sidecar tồn tại; fail đúng cách trên Linux (chưa có exe) — test exit 1 OK - [x] F4: `tools/verify_bundle.py --check-bridge` — verify TOC + sidecar tồn tại; fail đúng cách trên Linux (chưa có exe) — test exit 1 OK
- [x] F5: hooks.nsh — vc_redist cài toàn máy (ExecWait /quiet) → đủ runtime cho cả daw_engine.exe + daw_vst_bridge.exe; MSI không chạy hooks → cài thủ công - [x] F5: hooks.nsh — vc_redist cài toàn máy (ExecWait /quiet) → đủ runtime cho cả daw_engine.exe + daw_vst_bridge.exe; MSI không chạy hooks → cài thủ công
- [x] F6: DESKTOP_INSTALL_PLAN.md §5.2 (kiến trúc 2 sidecar) + DEPLOYMENT_REPORT.md bản 1.2 - [x] F6: DESKTOP_INSTALL_PLAN.md §5.2 (kiến trúc 2 sidecar) + DEPLOYMENT_REPORT.md bản 1.2
- [x] F7: `.github/workflows/build-windows.yml` — windows-latest: MSVC + Rust + node + python → build_windows.ps1 → upload NSIS/MSI - [x] F7: `.github/workflows/build-windows.yml` — windows-latest: MSVC + Rust + node + python → build_windows.ps1 → upload NSIS/MSI
- [ ] **Chạy thật trên Windows**: build_windows.ps1 trọn pipeline ra NSIS+MSI (đã verify build_native_bridge.ps1 chạy end-to-end OK) - [ ] **Chạy thật trên Windows**: build_windows.ps1 trọn pipeline ra NSIS+MSI
## G. Test end-to-end ## G. Test end-to-end
- [x] G1: shm_selfcheck pass trên Windows — "SHM self-check OK (sizeof struct = 11160 bytes)" - [ ] G1: shm_selfcheck pass (đã pass trên Linux: "SHM self-check OK (**11160 bytes**)" — chạy lại trên Windows)
- [x] G2: bridge mở SHM → load SF2 + SFZ → MIDI note → audio ≠ 0 (script `native_bridge/tests/g2_bridge_smoke.py`, peak SF2=0.00109 / SFZ=0.00107) — G2 thật từ Tauri UI còn lại ở G9/G10 - [ ] G2: SHM Rust tạo → bridge mở → MIDI → audio
- [ ] G3: load SGM-V2.01 (SF2) + SalamanderPiano (SFZ) + Vital (VST3) - [ ] G3: load SGM-V2.01 (SF2) + SalamanderPiano (SFZ) + Vital (VST3)
- [ ] G4: live MIDI + timeline cùng lúc, latency <10ms - [ ] G4: live MIDI + timeline cùng lúc, latency <10ms
- [ ] G5: Track FX + Master FX tác động âm bridge - [ ] G5: Track FX + Master FX tác động âm bridge
@@ -76,29 +71,6 @@ chạy từng mục trên máy Windows (hoặc OS native có đủ toolchain),
- [ ] G9: multi-instance 2 track đồng thời (A10 — 2 SF2/SFZ khác nhau, đổi live) - [ ] G9: multi-instance 2 track đồng thời (A10 — 2 SF2/SFZ khác nhau, đổi live)
- [ ] G10: sustain/CC/pitchbend qua bridge (A12 — CC64 sustain + pitch wheel + CC7 volume) - [ ] G10: sustain/CC/pitchbend qua bridge (A12 — CC64 sustain + pitch wheel + CC7 volume)
## Ghi chú batch 2026-08-12 (chạy thật Windows - E2E + GỠ CARLA)
- **E2 (resolve path soundfont) fix + verify thật**: `_resolve_bridge_asset` scan `_load_user_plugin_dirs()`
+ `os.walk` cho SF2/SF3/SFZ; JS `loadTrackInstrumentToBridge` luôn gọi `bridgeLoad({name, path: r.path})`.
E2E: chèn `024_BigSymphny.sf2` (thư mục Downloads\Collections soundfont) từ Plugin Manager ->
`bridge.log` ghi `[NativeBridge] instrument loaded ch=0 type=2 C:\Users\locpham\Downloads\Collections soundfont\024_BigSymphny.sf2`
(FULL PATH - không còn FAILED). NOTE_ON -> master peak -0.021314 (âm phát), NOTE_OFF -> 0 (decay).
- **GỠ CARLA (bridge host mọi instrument)**: xóa khỏi `app.jsx` - `ensureCarlaForPlayback` (hàm + 2 call
site), nút 🎛 PM scan VST row, dropdown "Carla Bridge (mở Carla.exe)" (synth dropdown), auto-open
instrumentSelector + instrumentDropdown, nút "Carla Bridge" riêng. `runtime.js`
`shouldRoute`/`shouldRoutePlayback` -> `return false` vô điều kiện -> Carla KHÔNG mở, KHÔNG route khi
bridge connected. **GIỮ chủ đích**: FX module "Load Carla Bridge" (2 call site `openInCarla` ở FX Rack
+ Master - VST FX ngoài, user tự cài, không phải luồng instrument).
- **Verify UI**: grep app.jsx - PM không còn section Carla (state/functions `pmCarlaVersion`,
`locateCarla`, `saveCarlaInput` còn nhưng không còn JSX render), không còn chuỗi "Carla Bridge (mở
Carla.exe)", không còn call `ensureCarlaForPlayback` (chỉ comment). Các hàm chết còn lại
(`fireCarlaNote`, `scheduleCarlaNote`, `refreshCarlaStatus`, `shouldRouteCarla`) vô hại - runtime trả false.
- **`tauri build` (bản cài sạch)**: NSIS `SonicForgeDAW_1.0.0_x64-setup.exe` (363MB) + MSI
`SonicForgeDAW_1.0.0_x64_en-US.msi` (598MB). Verify: extract NSIS bằng 7z -> `daw_engine/daw_engine.exe`
MD5 `8ddc7463333a4c334d24dcb1c891546d` khớp `dist/daw_engine` (engine bản mới); `daw_vst_bridge.exe` +
`libfluidsynth-3.dll` có trong bundle.
- **Quyết định giữ FX "Load Carla Bridge"**: đây là VST FX ngoài (native GUI, user tự cài) - tính năng
riêng của FX Rack, không liên quan luồng load instrument. Nếu muốn xóa luôn, làm sau.
## Ghi chú batch 2026-08-11 (A10-A13 + D5) ## Ghi chú batch 2026-08-11 (A10-A13 + D5)
- Struct SHM đổi 10872 → 11160: MidiEventIPC +data2/data3(+reserved) = 12B; ControlEventIPC +channel = 1040B. - Struct SHM đổi 10872 → 11160: MidiEventIPC +data2/data3(+reserved) = 12B; ControlEventIPC +channel = 1040B.
Sync 3 nơi: `SharedMemoryIPC.h`, `shm.rs`, `shm_selfcheck.cpp` — đã khớp (selfcheck pass Linux). Sync 3 nơi: `SharedMemoryIPC.h`, `shm.rs`, `shm_selfcheck.cpp` — đã khớp (selfcheck pass Linux).
@@ -107,85 +79,3 @@ chạy từng mục trên máy Windows (hoặc OS native có đủ toolchain),
- `transport_control` Rust giờ nhận `playhead: Option<u32>`; JS `NativeBridgeService.transport(kind, playhead)`. - `transport_control` Rust giờ nhận `playhead: Option<u32>`; JS `NativeBridgeService.transport(kind, playhead)`.
- `loadInstrument(filePath, type, channel)`; `dispatchMidiEvent(cmd,...,data2,data3)` hỗ trợ CC/PROGRAM/PITCH_BEND. - `loadInstrument(filePath, type, channel)`; `dispatchMidiEvent(cmd,...,data2,data3)` hỗ trợ CC/PROGRAM/PITCH_BEND.
- Khi D8 (track→channel map) chưa xong: JS loadInstrument mặc định channel=0 — multi-instance chưa kích hoạt từ UI. - Khi D8 (track→channel map) chưa xong: JS loadInstrument mặc định channel=0 — multi-instance chưa kích hoạt từ UI.
## Ghi chú batch 2026-08-12 (bridge build + G2 trên Windows)
- **Bridge build PASS** (`native_bridge/build/_bridge.bat`, VS2019 x64 + vcpkg + sfizz source):
1. CMakeLists include_directories dùng `${FLUIDSYNTH_INCLUDE_DIRS}` (số nhiều) — `find_path` chỉ set `${FLUIDSYNTH_INCLUDE_DIR}``fluidsynth.h: No such file`. Sửa dùng singular.
2. sfizz 1.2.3 class là **`sfz::Sfizz`** (KHÔNG phải `sfizz::Synth`) → sửa `NativeInstrumentEngine.h` line 56.
3. `void* synth/settings` (giữ fluid types khỏi public header) → macro `FS_SYNTH`/`FS_SETTINGS` cast `static_cast<fluid_synth_t*>` mỗi call.
4. **LNK2038 RuntimeLibrary mismatch**: sfizz ép `/MT` trong `sfizz-src/cmake/SfizzConfig.cmake` (`set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded...")`) nhưng vcpkg fluidsynth là `/MD` → patch bỏ dòng ép + `_sfizz.bat` thêm `-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL`.
5. **Bug assign()**: gọi `init()` TRƯỚC `loadSoundFontFile()` — FluidSynth tạo synth trong lúc load nên `init` thấy null → SF2 load FAIL. Sửa thứ tự load → init (cả SF2 lẫn SFZ).
- Runtime: fluidsynth là DLL động — copy `libfluidsynth-3.dll` cạnh `daw_vst_bridge.exe` khi chạy standalone/sidecar.
- G2 test script: `native_bridge/tests/g2_bridge_smoke.py` — python tạo SHM (mirror Rust shm.rs offsets), spawn bridge với `SF_PARENT_PID`, push LOAD_INSTRUMENT (type=2 SOUNDFONT / 3 SFZ) + note-on, assert peak > 0.001.
## Ghi chú batch 2026-08-12 (scripts build Windows - F)
- **build_native_bridge.ps1** viết lại 7 bước (chạy end-to-end PASS trên máy thật):
1. vcpkg bootstrap (skip nếu có).
2. `vcpkg install --triplet x64-windows` theo manifest `native_bridge/vcpkg.json` — chỉ fluidsynth + pkgconf (sfizz KHÔNG có port vcpkg).
3. sfizz 1.2.3: clone tag `1.2.3` + `git submodule update --init --recursive`; patch idempotent bỏ dòng ép `/MT` trong `SfizzConfig.cmake` (regex `MultiThreaded$<$<CONFIG:Debug>:Debug>`); configure VS16 x64 với đủ flag `SFIZZ_SHARED=OFF ... -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL`; build target `sfizz_static`. Skip nếu `build/library/lib/Release/sfizz_static.lib` đã có.
4. vst3sdk: `git submodule update --init --recursive -- native_bridge/vst3sdk` (submodule ĐÃ đăng ký trong repo — không dùng `submodule add`).
5. CMake configure thêm `-G "Visual Studio 16 2019" -A x64`; build `--target daw_vst_bridge -- /m` (không build ALL_BUILD).
6-7. Copy sidecar `daw_vst_bridge-x86_64-pc-windows-msvc.exe` + `libfluidsynth-3.dll` (từ `build/vcpkg_installed/x64-windows/bin/`) vào `src-tauri/binaries/` — externalBin chỉ bundle exe, DLL phải cạnh sidecar.
- **build_windows.ps1**: thêm check `src-tauri\binaries\libfluidsynth-3.dll` sau bước bridge build.
- **tauri.conf.json**: thêm `"binaries/libfluidsynth-3.dll": "libfluidsynth-3.dll"` vào `bundle.resources` → installer đặt DLL cạnh exe (cùng thư mục sidecar). LƯU Ý: `tauri-build` validate resource tồn tại lúc compile — phải chạy build_native_bridge.ps1 trước `cargo build`/`tauri build`.
- **lib.rs bridge_candidates**: thêm 2 candidate `res_dir/triple_name` + `exe_dir/triple_name` — externalBin đặt sidecar ở INSTALL ROOT (tên có hậu tố triple), không nằm dưới `binaries/` → trước đây bản cài đặt không tìm thấy bridge (WASM fallback).
- Verify: parse PS1 OK (3 script), cargo check OK, chạy thử build_native_bridge.ps1 PASS (sfizz skip nhanh, build lại bridge + copy exe/DLL).
- **BUG CWD leak (sửa 2026-08-12)**: `build_native_bridge.ps1``Set-Location $PSScriptRoot` mà không trả về — gọi từ `build_windows.ps1` bằng `&` làm CWD bị đổi sang `build/scripts` → check `src-tauri\binaries\daw_vst_bridge-x86_64-pc-windows-msvc.exe` (relative) fail dù file tồn tại: "DONE: sidecar + DLL ..." rồi "ERROR: bridge sidecar missing". Sửa: bỏ `Set-Location` trong build_native_bridge.ps1 (mọi path đã absolute từ $PSScriptRoot) + build_windows.ps1 `Set-Location (Join-Path $PSScriptRoot "..\..")` ngay sau lệnh `&` (phòng thủ). Đã verify: CWD giữ nguyên, check sidecar+DLL = True.
- LƯU Ý PS 5.1: native stderr + `$ErrorActionPreference="Stop"` + redirect (`2>&1`/`*>`) → PS abort "NativeCommandError" (vd cmake warning). Không redirect trong flow build_windows.ps1 nên không ảnh hưởng; chỉ ảnh hưởng khi debug redirect stderr.
## Ghi chu batch 2026-08-12 (verify installer + spawn.log)
- User bao spawn.log khong thay dau vet daw_vst_bridge -> log dang xem la log CU 2026-08-11 21:00 (%APPDATA%\SonicForgeDAW\logs\spawn.log) tu ban cai truoc lib.rs fix (bridge logging + candidates). Log cu chi co engine candidates, khong co [bridge ...] lines.
- Installer hien tai src-tauri/target/release/bundle/nsis/SonicForgeDAW_1.0.0_x64-setup.exe (mtime 09:35) build SAU lib.rs fix (08:58) voi src-tauri/binaries/ day du (daw_vst_bridge-x86_64-pc-windows-msvc.exe 1.3M + libfluidsynth-3.dll 515K) -> tauri-bundler fail neu thieu externalBin/resource luc bundle, nen installer chac chan chua ca 2. KHONG can rebuild setup.exe.
- Candidate ky vong exists=True sau khi cai moi: exe_dir (triple name, bundle) = <INSTALL_ROOT>\daw_vst_bridge-x86_64-pc-windows-msvc.exe (LocalAppData\SonicForgeDAW neu per-user, hoac Program Files).
- Ghi chu: msiexec /a (administrative extract) tre tren MSI 570MB nay (0 file sau ~15 phut, phai taskkill). Khong dung duoc de verify noi dung installer; grep ten file trong setup.exe cung khong ra vi NSIS nen ca script data.
- KET QUA (user cai setup.exe 09:35 + chay): bridge chay OK — 3 process: sonicforge-daw.exe, daw_engine.exe, daw_vst_bridge.exe (9.2MB RAM). Install dir LocalAppData\\SonicForgeDAW: daw_vst_bridge.exe 1.3M + libfluidsynth-3.dll 515.5K canh nhau. spawn.log co [bridge ...] lines, exists=true tai resource_dir (bundle root) va exe_dir (portable) = <root>\\daw_vst_bridge.exe. LUU Y: externalBin cua NSIS dat sidecar o install ROOT voi TEN PLAIN (daw_vst_bridge.exe), KHONG co hau to triple -> candidates triple name deu exists=false (vo hai, plain name bat duoc). engine.log 10:02:40 listening 0.0.0.0:8001 OK.
## Ghi chu batch 2026-08-12 (fix UI: mat __TAURI__ khi redirect - trieu chung VSTi/Carla + SF2 khong am)
- TRIEU CHUNG user: (1) goi VSTi -> app chay voi Carla bridge thay vi native host bridge; (2) goi instrument SF2 -> khong co am thanh.
- GOC RE: src-tauri/ui/index.html la bootstrap script location.href = http://127.0.0.1:<port>/ redirect sang engine origin -> ROI T context tauri:// -> mat window.__TAURI__ -> nativeBridgeService._init() thay khong __TAURI__ -> 'native bridge disabled', isBridgeConnected=false -> unifiedMidiRouter.isBridgeActive() false -> moi MIDI roi ve onFallback (SonicSF WASM cho SF2) / SonicCarlaMidi (runtime.js route Carla khi track VSTi + bridge connected=false). Rust van spawn bridge + SHM -> process song, nhung JS khong bao gio dung. Khop 100% 2 trieu chung.
- FIX src-tauri/ui/index.html: bo location.href redirect; thay bang script async quet /health 8000-8010 tim engine, fetch(base) lay HTML UI, inject <base href> + window.API_BASE_URL sau <head>, roi document.write tren CUNG document -> origin tauri:// giu nguyen -> __TAURI__ song -> NativeBridgeService active. (Engine chon port dong 8000-8010, engine.log thay 8001; api.js mac dinh window.location.origin -> override API_BASE_URL. Engine CORS allow_origins=[*] OK fetch cheo origin.)
- FIX app/static/js/services/runtime.js (patch python 2 cho shouldRoute + shouldRoutePlayback): thay `if (NativeBridgeService.isBridgeConnected) return false;` bang check activeInstrumentType - chi chan Carla khi bridge connected VA instrument KHONG phai VST (VSTi van route Carla vi bridge chua host VST). Copy sang src-tauri/resources/daw_engine/_internal/... + dist/daw_engine/_internal/... (installer nhung tu resources; dist la nguon PyInstaller). cmp OK.
- build/scripts/frontend_host_index.html = template = ban index.html moi; build_windows.ps1 buoc [7/7] them Copy-Item frontend_host_index.html -> src-tauri/ui/index.html truoc npx tauri build.
- REBUILD installer 2026-08-12 10:27: setup.exe (363M) + 10:30: MSI moi. Trong release: runtime.js co activeInstrumentType (2 occurrences) = ban da patch. LUU Y: bash khong chay duoc npx (khong tim extension) -> dung `cmd //c ..\node_modules\.bin\tauri.cmd build` tu src-tauri.
- USER CAN LAM: cai lai setup.exe moi -> test SF2 (phai phat qua bridge, toast 'Da load vao Native Bridge', MIDI route bridge) va VSTi (van route Carla).
## Ghi chú batch 2026-08-12 (G2 3 PASS + fix VST3 dangle + installer mới)
- G2 smoke 3 PASS trên binary sạch (md5 bb1380357c30ec931058c65c29beeb46): SF2 peak=0.04333, SFZ peak=0.02455, VST3 (mda DX10) peak=0.16096.
- "Không ra âm" SFZ là artifact đo: g2_test.sfz dùng dummy1.wav (tone suy giảm nghe được ~120-230ms), poll cũ 0.25-0.6s bỏ lỡ. Fix: g2_bridge_smoke.py poll_peak(1.5s) mỗi 10ms track max.
- BUG VST3 crash khi load sau instrument khác (0xC0000005): src/Vst3Instrument.cpp loadPlugin dùng const ClassInfo* trỏ vào temporary trả về by value từ factory.classInfos() (sdk module.h ClassInfos classInfos() const noexcept) → dangle sau range-for. Fix: copy ClassInfo chosen; by value trong scope { auto infos = factory.classInfos(); ... }. Verify 4 sequence sf2,vst3 / vst3,sfz / sf2,sfz,vst3 / sfz,vst3 đều ALIVE, log “[Vst3Instrument] loaded ... (mda DX10) outCh=2”.
- runtime.js (cả 2 bản: src-tauri/resources/daw_engine/_internal + app/static/js/services): shouldRoute/shouldRoutePlayback return false khi window.NativeBridgeService.isBridgeConnected → bridge host native SF2/SF3/SFZ/VST3, không bao giờ route Carla.
- Debug prints đã xóa khỏi NativeInstrumentEngine.cpp ([SFZ] loadSfzFile/noteOn/render peaks); cleanup: scratch_test target khỏi CMakeLists.txt + scratch_test.cpp + fix_*.py + dbg_* probes + seq_*.txt logs đã xóa.
- Installer mới: NSIS SonicForgeDAW_1.0.0_x64-setup.exe (11:44:12, 363307885 B) + MSI (11:47:02, 598618948 B). md5 3 binary giống hệt bb1380357c30ec931058c65c29beeb46 (build/Release == src-tauri/binaries/ == target/release). packaged runtime.js có isBridgeConnected check (PACKAGED-OK).
- CMakeLists sau khi bỏ scratch_test vẫn build OK (daw_vst_bridge.vcxproj relink, md5 giữ nguyên).
## Ghi chú batch 2026-08-12 (FIX LOGIN "Not Found" + reset pass admin + rebuild installer)
- TRIỆU CHỨNG user: login không được — "Not Found", "không tìm thấy database cũ standalone", pass mặc định không vào.
- NG.NHÂN "Not Found" (2 lớp):
1) app.jsx:10 `const API_BASE_URL = window.location.origin;` — trong Tauri origin=tauri://localhost → mọi fetch API resolve sai → "Not Found". Fix: `(window.API_BASE_URL || window.location.origin).replace(/\/+$/, '')`; rebuild bundle (app.precompiled.js), bump tag ?v=202608102202 → ?v=202608121215 trong 4 index.html (app/templates, installed _internal, dist/_internal, resources/_internal).
2) double-slash: shell Tauri inject API_BASE_URL có trailing slash ("http://127.0.0.1:8000/") + api.js nối /api/v1 → //api/v1 → FastAPI 404 "Not Found". Fix: base.replace(/\/$/,'') trong build/scripts/frontend_host_index.html (đã sync src-tauri/ui/index.html, diff IDENTICAL) + api.js:2 replace(/\/+$/,''). Probe xác nhận: double-slash=404, single-slash=400.
- DB thật: %APPDATA%\SonicForgeDAW\storage\sonicforge.db (WAL, 96KB) — KHÔNG có DB ở LocalAppData; user tìm nhầm chỗ. Chứa sẵn admin + 2 projects (dữ liệu cũ còn nguyên).
- Pass admin đã bị đổi (verify admin123/admin/password/... đều FALSE). seed_admin chỉ seed khi chưa có admin, KHÔNG reset. Không có endpoint reset. → Reset trực tiếp DB (engine tắt): hashed_password = hash_password("admin123") (PBKDF2-HMAC-SHA256 600000 iter, salt token_hex(32), format salt:hash — dùng đúng thuật toán app/core/auth.py). Verify: admin/admin123 login HTTP 200 (token + user), admin/wrong 400.
- REBUILD SẠCH (không hot-patch): `python -m PyInstaller engine.spec --clean --noconfirm` → dist/daw_engine mới (12:29, md5 3367007aaced6e3c32a7a940914850e0) → copy sang src-tauri/resources/daw_engine/ → `cmd //c ..\node_modules\.bin\tauri.cmd build` từ src-tauri → installer mới: NSIS setup.exe 363333650 B (12:44) + MSI 598651716 B (12:47). Bridge md5 giữ bb1380357c30ec931058c65c29beeb46 (release + binaries).
- Verify packaged (target/release/daw_engine/_internal): api.js fix=1, precompiled fix=1, index.html tag ?v=202608121215, runtime.js có isBridgeConnected, engine md5 == dist.
- PASS MẶC ĐỊNH SAU CÀI MỚI: admin / admin123 (user nên đổi lại sau khi vào).
## Ghi chú batch 2026-08-12 (FIX: GUI VST native window + audio path bridge — E2E ĐẠT)
- TRIỆU CHỨNG user: (1) chọn VSTi → mở Carla (KHÔNG đúng TASKS.md — bridge host mọi instrument);
(2) instrument SF2 câm; (3) GUI plugin mở ra nhưng là HTML che, không phải cửa sổ plugin thật.
- FIX Issue 2 (GUI native): src-tauri/src/lib.rs `open_vst_gui` bỏ WebviewWindowBuilder/thread → chỉ
`shm.push_control(4,0,0,0,&plugin_id)`; native_bridge/src/main.cpp thêm `create_native_vst_window(title)`
(class `SonicForge_Native_VST3_Class`, WS_OVERLAPPEDWINDOW|WS_VISIBLE 800x600, không TOPMOST), OPEN_GUI
arg1==0 tạo window trong ChannelWorker job, giữ `guiWindows` map, capture arg2 by value (fix dangling).
Verify: hwnd 0x4BE0EC4 class SonicForge_Native_VST3_Class title Nexus, child JUCE_19ff6684f11 visible.
- FIX Issue 1 (âm câm): root cause SF2 = `HAS_PYFLUIDSYNTH=FALSE` trong bundled engine →
`/api/v1/plugins/soundfont-render` 501; VST3 play path cũ nativeSf/scheduleCarlaNote không chạy vì Carla
đã gỡ. Fix app.jsx: guard `isBridgeActive()` vào 8 chỗ (kbNative 9306, keybed mouseenter 9335, pvCtx
wheel 8180, clCtx piano-roll 8550, dwTrk draw 8814, pvCtxInst draw 8915, nativeSf 21779+22075,
subNativeSf 21942) → bridge active thì mọi note đi router → pushEvent → bridge.
- E2E CHÍNH ĐẠT (CDP + SHM): sf2_focus2 — bridge active, loadInstrument SF2 orchestral ch0 ok=true,
dispatchMidiEvent NOTE_ON 60 → SHM peak 0.029745 (âm phát qua bridge), NOTE_OFF → pk 0.
- E2E UI còn lại: chèn SF2 024 từ PM → track 01 có instrument 024; PR track 01 vẫn mở track 02 (track 01
chưa có MIDI item — dblclick lane không mở PR; không phải bug); preset list modal Select Instrument
trống (bank list OK) — instrument data chưa expand, không chặn E2E chính.
- VERIFY thêm: `gui_test_nexus.py` native window OK; bridge.log 1 lần "Starting", 43 "instrument loaded",
load thread ch=1 path 024_BigSymphny.sf2 → assigned ok=1; transport STOP.
+10
View File
@@ -5725,6 +5725,11 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
const ok = await window.NativeBridgeService.loadInstrument(r.path, type, 0); const ok = await window.NativeBridgeService.loadInstrument(r.path, type, 0);
window.showToast && window.showToast(ok ? ('Đã load vào Native Bridge: ' + r.path) : 'Bridge không khả dụng (dev mode?)', ok ? 'success' : 'warning'); window.showToast && window.showToast(ok ? ('Đã load vào Native Bridge: ' + r.path) : 'Bridge không khả dụng (dev mode?)', ok ? 'success' : 'warning');
if (ok) window.SonicAPI.bridgeStatus().then(s => setBridgeStatus(s)).catch(() => {}); if (ok) window.SonicAPI.bridgeStatus().then(s => setBridgeStatus(s)).catch(() => {});
// Requirement 2: VST3 load m native GUI ngay (C++ attach editor vào
// ca s bridge t to control type=4, hwnd=0).
if (ok && type === 'VST3' && window.NativeBridgeService.openNativeGUI) {
try { await window.NativeBridgeService.openNativeGUI(name); } catch (e) {}
}
return ok; return ok;
} catch (err) { } catch (err) {
window.showToast && window.showToast('Lỗi load bridge: ' + (err.message || err), 'error'); window.showToast && window.showToast('Lỗi load bridge: ' + (err.message || err), 'error');
@@ -15370,6 +15375,11 @@ const App = () => {
if (p) { if (p) {
const ok = await window.NativeBridgeService.loadInstrument(p, btype, bch); const ok = await window.NativeBridgeService.loadInstrument(p, btype, bch);
console.log('[Bridge] track', trackId, '-> bridge', btype, 'ch', bch, ok ? 'OK' : 'FAIL', p); console.log('[Bridge] track', trackId, '-> bridge', btype, 'ch', bch, ok ? 'OK' : 'FAIL', p);
// Requirement 2: chn VST3 qua nút Synth load xong m native GUI
// (C++ attach editor vào ca s bridge t to control type=4, hwnd=0).
if (ok && btype === 'VST3' && window.NativeBridgeService.openNativeGUI) {
try { await window.NativeBridgeService.openNativeGUI(instrumentId); } catch (e) {}
}
} }
})(); })();
}; };
File diff suppressed because one or more lines are too long
+29 -9
View File
@@ -2,9 +2,12 @@
// Audio sink: consumes 'bridge-audio' PCM frames (native bridge) and plays them // Audio sink: consumes 'bridge-audio' PCM frames (native bridge) and plays them
// through a ScriptProcessorNode into the WebAudio graph (track FX / master bus). // through a ScriptProcessorNode into the WebAudio graph (track FX / master bus).
(function () { (function () {
var RING_DEPTH = 8; // 8 blocks * 256 samples ~= 46ms anti-underrun // Bridge renders one 256-sample block per ~5.8ms; ScriptProcessor fires once
var SP_BUFFER = 4096; // ScriptProcessor chunk (16 bridge blocks) // per 4096 samples (~16 blocks @44.1k). The ring MUST cover one full callback
var _queue = []; // period or we underrun (silence gaps) — 24 blocks ~= 139ms headroom.
var RING_DEPTH = 24;
var SP_BUFFER = 4096;
var _chunks = []; // [{l: Float32Array(256), r: Float32Array(256)}]
var _spn = null; var _spn = null;
var _gainNode = null; var _gainNode = null;
var _ctx = null; var _ctx = null;
@@ -27,10 +30,26 @@
_spn.onaudioprocess = function (e) { _spn.onaudioprocess = function (e) {
var L = e.outputBuffer.getChannelData(0); var L = e.outputBuffer.getChannelData(0);
var R = e.outputBuffer.getChannelData(1); var R = e.outputBuffer.getChannelData(1);
var outLen = L.length;
L.fill(0); R.fill(0); L.fill(0); R.fill(0);
if (!_queue.length) return; if (!_chunks.length) return;
var f = _queue.shift(); // Drain as many queued blocks as fit in this callback (16 max) — filling
L.set(f.l); R.set(f.r); // only one block per callback previously played 1/16 of the audio.
var written = 0;
while (written < outLen && _chunks.length) {
var blk = _chunks[0];
var n = Math.min(blk.l.length, outLen - written);
L.set(blk.l.subarray(0, n), written);
R.set(blk.r.subarray(0, n), written);
written += n;
if (n >= blk.l.length) {
_chunks.shift();
} else {
// Partial block (only possible if block size != 256): keep remainder.
blk.l = blk.l.subarray(n);
blk.r = blk.r.subarray(n);
}
}
}; };
_spn.connect(_gainNode); _spn.connect(_gainNode);
_initialized = true; _initialized = true;
@@ -39,11 +58,12 @@
function onAudio(l, r) { function onAudio(l, r) {
if (!_initialized) init(); if (!_initialized) init();
_queue.push({ l: new Float32Array(l), r: new Float32Array(r) }); _chunks.push({ l: new Float32Array(l), r: new Float32Array(r) });
if (_queue.length > RING_DEPTH) _queue.shift(); // drop oldest on overrun // Drop oldest on overrun — keeps latency bounded (~139ms max).
while (_chunks.length > RING_DEPTH) _chunks.shift();
} }
function flush() { _queue = []; } function flush() { _chunks = []; }
function getOutputNode() { return _gainNode; } function getOutputNode() { return _gainNode; }
+2 -2
View File
@@ -40,7 +40,7 @@
<script src="/static/js/services/soundfontStorage.js?v=202607271016"></script> <script src="/static/js/services/soundfontStorage.js?v=202607271016"></script>
<script src="/static/js/services/soundfontPlayer.js?v=202608101800"></script> <script src="/static/js/services/soundfontPlayer.js?v=202608101800"></script>
<script src="/static/js/services/nativeBridgeService.js?v=202608112200"></script> <script src="/static/js/services/nativeBridgeService.js?v=202608112200"></script>
<script src="/static/js/services/bridgeAudioNode.js?v=202608112200"></script> <script src="/static/js/services/bridgeAudioNode.js?v=202608121600"></script>
<script src="/static/js/services/unifiedMidiRouter.js?v=202608112200"></script> <script src="/static/js/services/unifiedMidiRouter.js?v=202608112200"></script>
<script src="/static/js/services/audioRoutingEngine.js?v=202608112200"></script> <script src="/static/js/services/audioRoutingEngine.js?v=202608112200"></script>
<script src="/static/js/services/aiGateway.js?v=202608037200"></script> <script src="/static/js/services/aiGateway.js?v=202608037200"></script>
@@ -50,7 +50,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script> <script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script> <script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script> <script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608121215" defer></script> <script src="/static/js/app.precompiled.js?v=202608121600" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016"> <link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style> <style>
:root { :root {
+7
View File
@@ -295,10 +295,17 @@ int main(int argc, char* argv[]) {
// cho openGUI lai tao COM apartment moi, con plugin thi song o // cho openGUI lai tao COM apartment moi, con plugin thi song o
// apartment cu da chet (load thread exit) -> Nexus attached() // apartment cu da chet (load thread exit) -> Nexus attached()
// hang (gui_probe: bridge_like treo, same_thread OK). // hang (gui_probe: bridge_like treo, same_thread OK).
// LOAD va OPEN_GUI duoc drain trong cung vong lap: LOAD post job
// len worker (async, VST3 init co the mat giay) truoc khi type=4
// duoc xu ly — khong doi, instruments.get() con rong -> "no
// instrument loaded". Poll toi da 10s cho LOAD hoan tat.
uint32_t guiCh = 16; uint32_t guiCh = 16;
for (int tries = 0; tries < 200 && guiCh == 16; ++tries) {
for (uint32_t ch = 0; ch < 16; ++ch) { for (uint32_t ch = 0; ch < 16; ++ch) {
if (instruments.get(ch)) { guiCh = ch; break; } if (instruments.get(ch)) { guiCh = ch; break; }
} }
if (guiCh == 16) sleep_ms(50);
}
if (guiCh == 16) { if (guiCh == 16) {
std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << c.arg1 std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << c.arg1
<< " plugin=" << c.arg2 << " (no instrument loaded)" << std::endl; << " plugin=" << c.arg2 << " (no instrument loaded)" << std::endl;