- native_bridge/: InstrumentEngineManager multi-channel, sample-accurate, CC/program/pitchbend, transport, Vst3Instrument stub (HAVE_VST3SDK) - src-tauri: shm.rs, bridge spawn + audio pump + health monitor, open_vst_gui, externalBin, commands - app: UnifiedMidiRouter, NativeBridgeService, bridgeAudioNode, audioRoutingEngine, Plugin Manager UI, Bridge/WASM indicator, set_position sync - build: 3 ps1 (force-added, build/ ignored), verify_bundle --check-bridge, CI workflow - docs: TASKS.md, TEST_NOTES.md (Windows verify checklist), install/report updates
22 KiB
TASKS — TRIỂN KHAI NATIVE HOST BRIDGE THEO PLAN
Nguồn: PLAN.md + spec. Mỗi task: file đích, hành động cụ thể, điều kiện hoàn thành (DoD) để verify.
Ký hiệu: [MỚI] tạo mới, [THAY] thay thế/đổi luồng hiện có, [BỔ SUNG] thêm vào code hiện có.
TRẠNG THÁI 2026-08-11: ✅ = đã làm xong. Task ✅ liên quan Rust/Windows (B1-B4, C2, C5, C7) mới verify ở mức code (node --check JS / review struct) — cần
cargo check+ chạy thật trên máy Windows trước khi coi là hoàn tất; máy này (Linux) không có rustc/CMake-MSVC. D2/D9: đã đổi timeline →scheduleMidiNoteDispatch(bridge active) + transport play/stop +bridgePlayheadSampleRef; sampleOffset/set_position chỉ gửi khi C++ A11/A13 sẵn sàng (nay gửi sampleOffset=0 qua setTimeout). E1-E3: syntax OK (ast.parse); máy này SIGILL khi importapp.core.vst_engine(numpy/pedalboard pre-existing) → chưa chạy thử API tại đây. BATCH A10-A13 + D5 (2026-08-11): C++ multi-instance (InstrumentEngineManager), CC/program/pitch bend (MidiEventIPC + data2/data3), TRANSPORT (type=3 STOP flush/PLAY/SET_POSITION + playheadSamples), sample-accurate segment render, JS dispatch MỞ RỘNG (CC/PROGRAM/PITCH_BEND), load theo channel, transport(playhead). Struct SHM ĐỔI: 10872 → 11160 bytes — đã sync C (selfcheck pass Linux) ↔ Rust shm.rs ↔ selfcheck; bắt buộccargo check+ chạy shm_selfcheck trên Windows. Sửa bug pre-existing: Rust push_control không set pathlen → C++ load giờ dùng strnlen(arg2) thay vì tin arg1. C++ chưa compile ở máy này (thiếu fluidsynth/sfizz headers) — verify bằng shm_selfcheck (không cần engine) + review. BATCH B8-B10 (cùng ngày): spawn bridge kèm SF_SAMPLE_RATE/SF_BLOCK_SIZE (C++ main đọc env, block cố định 256), open_vst_gui tạo child WebviewWindow + HWND raw_window_handle → control type=4, health monitor stall 3s → respawn 1 lần → bridge-down (đếm vào bridge.log). Rust chưa compile được (rustup không có toolchain) — review tay;cargo checktrên Windows là điều kiện hoàn tất. BATCH D7/D10/E4/E5/F1-F7/B6/B7/A7 (2026-08-11, cuối): D7 UI Plugin Manager (badge Bridge ON/OFF + nút "Load Bridge" trên VST/SoundFont →/api/v1/bridge/load→ loadInstrument); D10 header indicator Bridge/WASM + nút "Ép WASM" (setForceWasm) + serviceonStatusChange; E4 watchdog verify (không đụng bridge); E5GET /api/v1/bridge/log; F1-F3 3 ps1 copy vào repo build/scripts/; F4 verify_bundle--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 TODO chờ vst3sdk trên Windows). Bundle rebuild OK; selfcheck 11160 pass Linux. 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.nshcustomInstallExecWait vc_redist toàn máy — đủ cho cả 2 exe); F2 🟡 (ps1 đã copy, chạy thật cần Windows); D9 bổ sungtransport('set_position', playhead)~mỗi giây trongupdatePlayhead(JS là nguồn playhead duy nhất — C++ A13 không tự biết). Bundle rebuilt OK.
A. NATIVE BRIDGE C++ (native_bridge/) — [MỚI]
Code skeleton đã chuẩn bị sẵn trong workspace native_bridge/; copy vào repo /home/locpham/SonicForgeStudio/native_bridge/ rồi hoàn thiện từng task.
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ A1 | native_bridge/ |
Copy toàn bộ skeleton từ workspace vào repo | native_bridge/CMakeLists.txt + include/*.h + src/*.cpp tồn tại |
| ✅ A2 | native_bridge/vst3sdk/ |
git submodule add https://github.com/steinbergmedia/vst3sdk.git |
thư mục vst3sdk/CMakeLists.txt tồn tại |
| ✅ A3 | native_bridge/vcpkg.json |
Tạo manifest: {"dependencies":["fluidsynth","sfizz","pkgconf"]} |
vcpkg install chạy từ manifest không lỗi |
| ✅ A4 | native_bridge/include/SharedMemoryIPC.h |
Giữ struct spec + controlQueue (đã có); bổ sung field uint64_t blockTimestamp (đo latency IPC) |
struct khớp layout Rust ở B3 |
| ✅ A5 | native_bridge/src/main.cpp |
Hoàn thiện: đọc control LOAD (đã có), thêm xử lý type=3 TRANSPORT_PLAY/STOP (flush note-off khi Stop), log mỗi lần load instrument ra stdout |
Stop timeline → bridge gửi note-off toàn pitch |
| A6 | native_bridge/src/NativeInstrumentEngine.cpp |
Giữ FluidSynth + sfizz (đã có); hoàn thiện Vst3Instrument: host qua vst3sdk (IAudioProcessor::process, IPlugView::attached với HWND con), factory make_instrument trả về instance thật |
load .vst3 → noteon → PCM khác 0 |
| 🟡 A7 | native_bridge/include/Vst3Instrument.h + src/Vst3Instrument.cpp |
Tách class VST3 riêng (hiện đang stub trong main.cpp); xử lý openGUI(void* hwnd) |
compile OK khi có vst3sdk |
| A8 | native_bridge/src/Vst2Instrument.cpp |
(Tùy chọn, GĐ sau) Host VST2 qua vestige | skip nếu không đủ thời gian; ghi TODO |
| ✅ A9 | native_bridge/src/SharedMemoryIPC.cpp |
Bỏ no-op; thêm helper open_shm(name), write_midi, write_control (tái dùng bởi test) |
unit test nhỏ gọi helper pass |
B. RUST / TAURI SHELL (src-tauri/) — [MỚI] + [BỔ SUNG]
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ B1 | src-tauri/Cargo.toml |
BỔ SUNG dependency windows-sys (features Win32_System_MemoryManagement, Win32_Foundation) để CreateFileMapping/MapViewOfFile |
cargo check pass |
| ✅ B2 | src-tauri/src/shm.rs |
[MỚI] Module SHM: create_shm(name) -> ShmHandle tạo SonicForge_DAW_IPC (CreateFileMapping + MapViewOfFile, size = sizeof(SharedAudioBufferIPC)), expose write_midi_event(evt), read_audio_block(), reset_queue() |
cargo check pass; struct layout khớp A4 |
| ✅ B3 | src-tauri/src/lib.rs |
BỔ SUNG EngineProcess thứ hai cho bridge: bridge_candidates() dò binaries/daw_vst_bridge-x86_64-pc-windows-msvc.exe (resource dir + exe dir), spawn với env SF_SHM_NAME=SonicForge_DAW_IPC + SF_PARENT_PID, redirect stdout→%APPDATA%/SonicForgeDAW/logs/bridge.log, kill khi window destroyed |
spawn.log có dòng [daw_vst_bridge] exists=True |
| ✅ B4 | src-tauri/src/lib.rs |
BỔ SUNG invoke_handler với commands: push_midi_event(cmd,ch,pitch,vel,sampleOffset), load_native_instrument(path,type), open_vst_gui(pluginId), bridge_status(), transport_control(kind) |
window.__TAURI__.core.invoke(...) trả về OK |
| ✅ B5 | src-tauri/src/lib.rs |
[MỚI] Audio pump thread: mỗi ~5.8ms đọc SHM (bridgeWriteIndex tăng) → app.emit("bridge-audio", {L: Float32Array, R: Float32Array}); nếu index không tăng 1s → emit bridge-down |
UI nhận event bridge-audio |
| B6 | src-tauri/tauri.conf.json |
BỔ SUNG bundle.externalBin: ["binaries/daw_vst_bridge"] (giữ resources engine) |
npx tauri build bundle chứa daw_vst_bridge-x86_64-pc-windows-msvc.exe |
| B7 | src-tauri/capabilities/default.json |
Xác nhận không cần permission mới (commands app-level) — nếu dùng plugin window cho GUI VST thì thêm core:window:allow-create |
tauri dev không cảnh báo permission |
C. FRONTEND SERVICES MỚI (app/static/js/services/) — [MỚI]
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ C1 | unifiedMidiRouter.js |
[MỚI] Class UnifiedMidiRouter: pushEvent({cmd,ch,pitch,vel,sampleOffset}) normalize UnifiedMidiEvent; subscribe(source) cho Web MIDI + Timeline; khi bridgeConnected → gọi nativeBridgeService.dispatchMidiEvent, ngược lại → gọi SonicSF.playNote/stopNote (fallback); panic() → transport_control('panic') |
1 router dùng chung 2 nguồn |
| ✅ C2 | nativeBridgeService.js |
[MỚI] Như spec NHƯNG dispatchMidiEvent = window.__TAURI__.core.invoke('push_midi_event', evt) (không có __TAURI_IPC_SHM__); loadInstrument(filePath,type) invoke load_native_instrument; openNativeGUI(pluginId) invoke open_vst_gui; lắng nghe bridge-audio/bridge-down, expose onAudio(cb) |
MIDI keyboard → note chạy qua bridge |
| ✅ C3 | audioRoutingEngine.js |
[MỚI] connectNativeBridgeToTrackFX(bridgeNode, trackId) theo spec §VI: nối node vào trackContext.sfEntry → FX chain → masterBus.input; disconnect() khi bridge down |
âm bridge qua EQ track + master maximizer |
| ✅ C4 | app/templates/index.html |
BỔ SUNG 3 script tag sau soundfontPlayer.js (dòng 41): nativeBridgeService.js, unifiedMidiRouter.js, audioRoutingEngine.js (kèm ?v= mới) |
reload page → window.SonicMidiRouter tồn tại |
D. WIRING app/static/js/app.jsx — [THAY]
| ID | Vị trí (dòng hiện tại) | Hành động | DoD |
|---|---|---|---|
| ✅ D1 | onmidimessage ~15587 |
[THAY] Nhánh NOTE_ON/OFF gọi SonicSF.playNote/SonicCarlaMidi.noteOn → gọi UnifiedMidiRouter.pushEvent trước; giữ fallback khi !bridgeConnected |
bấm keyboard → router log + âm qua bridge |
| ✅ D2 | updatePlayhead ~20771+ (nhánh dispatch midiItems) |
[THAY] Dispatch note timeline → pushEvent kèm sampleOffset tính từ playhead (bỏ setTimeout drift); Stop/Play → transport_control |
play piano roll đúng nhịp, Stop hết âm ngân |
| ✅ D3 | playNativeSfNote / SonicSF.playNote call sites (track armed, keybed, sub-tab) |
[THAY] Đổi đích tới router khi bridge active (cờ SF_BRIDGE_AVAILABLE) |
mọi đường note đi qua 1 cổng |
| ✅ 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 |
| 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 |
E. BACKEND PYTHON (app/) — [BỔ SUNG]
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ E1 | app/api/v1/plugins.py |
BỔ SUNG endpoint GET /api/v1/bridge/status (đọc file %APPDATA%/SonicForgeDAW/ipc/bridge_status do Rust ghi, hoặc probe bridge.log) |
curl /api/v1/bridge/status trả JSON có connected |
| ✅ E2 | app/api/v1/plugins.py |
BỔ SUNG endpoint POST /api/v1/bridge/load — resolve path thật (sf2 trong storage/soundfonts, .vst3 trong plugin_dirs) rồi ghi file IPC ipc/bridge_load.request cho Rust đọc (giống cơ chế pick_dir hiện có) |
Rust nhận request → bridge load → UI nhận bridge-audio |
| ✅ E3 | app/core/vst_engine.py |
Giữ nguyên cho offline render; thêm comment + hàm resolve_plugin_path(name) tái dùng bởi E2 |
không phá luồng offline hiện có |
| E4 | desktop_engine.py |
Không đổi logic; kiểm tra watchdog không giết bridge (bridge do Rust spawn, engine chỉ quản lý chính nó) | chạy standalone vẫn OK |
F. BUILD & ĐÓNG GÓI WINDOWS — [BỔ SUNG]
| ID | File | Hành động | DoD |
|---|---|---|---|
| F1 | build/scripts/build_native_bridge.ps1 |
Copy từ workspace (đã sẵn): vcpkg bootstrap + install fluidsynth/sfizz + submodule vst3sdk + cmake MSVC + copy exe | chạy trên máy Windows → exe trong src-tauri/binaries/ |
| 🟡 F2 | build/scripts/build_windows.ps1 |
Copy từ workspace: pipeline 7 bước (0. kill engine+bridge, 1. pip, 2. frontend, 3. pyinstaller, 4. copy engine, 5. build bridge, 6. vc_redist, 7. tauri build) | build 1 lệnh ra NSIS+MSI |
| F3 | build/scripts/test_standalone.ps1 |
Copy từ workspace: 8 bước test (process, spawn.log, SHM, health, bridge/status, load SF2, checklist tay) | pass #1-#5 |
| F4 | tools/verify_bundle.py |
BỔ SUNG kiểm tra thêm: src-tauri/binaries/daw_vst_bridge-x86_64-pc-windows-msvc.exe tồn tại trước tauri build |
build fail sớm nếu thiếu bridge |
| ✅ F5 | src-tauri/hooks.nsh |
Xác nhận vc_redist.x64.exe được cài cho cả daw_engine.exe + daw_vst_bridge.exe (bridge cần VC++ runtime) |
máy sạch cài app → bridge chạy |
G. TEST — [BỔ SUNG]
| ID | Nội dung | DoD |
|---|---|---|
| G1 | Unit test C++ (A9 helper) | assert-based check trong native_bridge/tests/ chạy pass |
| G2 | Test SHM: Rust tạo → bridge mở → ghi MIDI → đọc audio | bridgeWriteIndex tăng; PCM khác 0 khi noteon |
| G3 | Test load SF2/SF3 (SGM-V2.01) + SFZ (SalamanderPiano) + VST3 (Vital) | spec VII mục 2 pass |
| G4 | Test live MIDI + timeline cùng lúc (Nektar SE49) | spec VII mục 3 pass, latency <10ms |
| G5 | Test Track FX + Master FX tác động âm bridge | spec VII mục 4 pass |
| G6 | Test fallback: kill bridge → SonicSF WASM tiếp tục, không crash | spec VII mục 6 pass |
| G7 | Chạy test_standalone.ps1 trên máy cài standalone |
5/5 bước tự động pass |
H. BỔ SUNG LIỀN MẠCH (MẠCH DỮ LIỆU HOÀN CHỈNH)
Các task nhóm H lấp những khâu còn hở giữa A-G để pipeline chạy end-to-end không đứt đoạn. Đánh số tiếp theo nhóm tương ứng.
H-A. Bridge C++ — bổ sung
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ A10 | native_bridge/src/main.cpp + NativeInstrumentEngine.h |
[MỚI] Thay currentInstrument (1 instance) bằng InstrumentEngineManager: std::map<uint32_t, std::unique_ptr<INativeInstrument>> keyed theo MIDI channel; assign_instrument(channel, path, type); MIDI event → instance theo evt.channel; channel chưa gán → silent (không crash) |
2 track (ch 0 + ch 2) 2 instrument phát đồng thời |
| ✅ A11 | native_bridge/src/main.cpp |
[BỔ SUNG] Sample-accurate: không xử lý MIDI ngay; buffer event theo sampleOffset, rẽ block 256 thành các đoạn theo offset, gọi noteOn/Off đúng vị trí trước khi render đoạn đó |
note timeline không lệch nhịp (>±1 sample test) |
| ✅ A12 | native_bridge/include/SharedMemoryIPC.h + engine |
[BỔ SUNG] Mở rộng MidiEventIPC: field data2 (+data3 PB MSB); hỗ trợ 0xB CC (FluidSynth fluid_synth_cc), 0xC program change, 0xE pitch bend (fluid_synth_pitch_bend); VST3 → tham số tương ứng |
sustain/CC/pitchbend qua bridge hoạt động |
| ✅ A13 | native_bridge/src/main.cpp |
[BỔ SUNG] Control type=3 TRANSPORT: arg0=0 STOP → flush note-off toàn channel + fluid_synth_all_notes_off; arg0=1 PLAY kèm arg1=playheadSamples → đồng bộ timeline |
Stop → hết âm ngân tức thì |
H-B. Rust — bổ sung
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ B8 | src-tauri/src/lib.rs |
[BỔ SUNG] Spawn bridge kèm env SF_SAMPLE_RATE + SF_BLOCK_SIZE (đọc từ bridge_status/cấu hình, mặc định 44100/256); nếu AudioContext.sampleRate khác → JS resampler (C5) — ghi chú trong code |
SR khác 44100 vẫn nghe đúng cao độ |
| ✅ B9 | src-tauri/src/lib.rs |
[BỔ SUNG] open_vst_gui(pluginId): tạo Tauri child window (WebviewWindowBuilder, title = plugin, childOf main) → lấy HWND (windows-sys) → ghi control type=4 OPEN_GUI + hwnd vào SHM → bridge openGUI(hwnd); window đóng → close_gui |
VST GUI hiện trong cửa sổ con, đóng sạch |
| ✅ B10 | src-tauri/src/lib.rs |
[BỔ SUNG] Health monitor: nếu bridgeWriteIndex đứng >3s và process chết → spawn lại 1 lần, vẫn fail → emit("bridge-down"); đếm số lần restart vào bridge.log |
app tự phục hồi bridge 1 lần; UI fallback sau đó |
H-C. Frontend — bổ sung
| ID | File | Hành động | DoD |
|---|---|---|---|
| ✅ C5 | app/static/js/services/bridgeAudioNode.js |
[MỚI] Sink phát âm: nhận bridge-audio (Float32Array L/R) → ring buffer depth 8 block (~46ms) chống underrun → ScriptProcessor/Worklet đẩy ra AudioNode (output chuẩn stereo); nút này là "bridgeNode" mà C3 nối; VU analyser tap trên node (__vuAnalyser như _gainNode cũ) để track VU nhảy; resampler tuyến tính nếu SR khác bridge |
âm nghe được từ SHM; VU track hoạt động |
| ✅ C6 | app/static/js/app.jsx (useEffect khởi tạo ~gần initMasterBus) |
[BỔ SUNG] Bootstrap bridge: gọi bridge_status() → set SF_BRIDGE_AVAILABLE; nativeBridgeService.onAudio nối vào bridgeAudioNode; subscribe bridge-down → ngắt node + quay về SonicSF |
cờ đúng trước note đầu tiên; bridge chết → fallback tự động |
| ✅ C7 | app/static/js/services/nativeBridgeService.js |
[BỔ SUNG] Dev-mode mock: không có window.__TAURI__ (Linux dev / browser) → mọi invoke thành log + onAudio không emit → app chạy bằng SonicSF như cũ |
dev trên Linux không crash |
H-D. app.jsx — bổ sung
| ID | Vị trí | Hành động | DoD |
|---|---|---|---|
| D8 | router (C1) | [BỔ SUNG] Channel allocation: bảng trackId → channel 0-15 thay _engineChMap/allocateChannel; percussion (bank 128) → ch 9; gửi CC0/CC32/program change qua bridge khi đổi instrument (A12) |
2 track 2 instrument không trùng channel |
| ✅ D9 | updatePlayhead |
[BỔ SUNG] Playhead sample counter: playheadSample += block*(dt*sr/block); sampleOffset = playheadSample - lastBlockStartSamples; gửi transport_control('set_position', playheadSample) mỗi bar |
timeline chính xác sample |
| D10 | UI header/status | [BỔ SUNG] Indicator bridge (connected/down) + nút "Ép dùng WASM" (debug): gọi SonicMidiRouter.setForceWasm(true) |
user biết engine nào đang phát |
H-E. Backend — bổ sung
| ID | File | Hành động | DoD |
|---|---|---|---|
| E5 | app/api/v1/plugins.py |
[BỔ SUNG] GET /api/v1/bridge/log?lines=100 — trả tail bridge.log cho UI debug |
UI xem được log bridge |
H-F. Build — bổ sung
| ID | File | Hành động | DoD |
|---|---|---|---|
| F6 | DESKTOP_INSTALL_PLAN.md / DEPLOYMENT_REPORT.md |
[BỔ SUNG] Cập nhật tài liệu: kiến trúc 2 sidecar (daw_engine + daw_vst_bridge), yêu cầu VC++ runtime, port/API mới | tài liệu khớp thực tế |
| F7 | ci/build-windows.yml |
[MỚI] (Tùy chọn) GitHub Actions: runner windows-latest → build bridge + engine + tauri → artifact installer | pipeline CI ra NSIS setup |
H-G. Test — bổ sung
| ID | Nội dung | DoD |
|---|---|---|
| G8 | Đo latency end-to-end: đọc blockTimestamp (A4) qua bridge_status + round-trip MIDI→loa (loopback/mic); so ngưỡng |
IPC ≤0.5ms; end-to-end <10ms |
| G9 | Test multi-instance: track 1 SF2 (piano) + track 2 SFZ (string) chạy cùng lúc, đổi instrument live | không choke, đúng âm từng track |
| G10 | Test CC/sustain/pitchbend qua bridge (A12) | sustain pedal + pitch wheel + CC volume hoạt động |
SƠ ĐỒ MẠCH DỮ LIỆU HOÀN CHỈNH (SAU KHI ĐỦ TASKS)
Web MIDI keyboard ──┐
Timeline playhead ──┴─► UnifiedMidiRouter (C1) ─ invoke push_midi_event (B4/C2)
(D8: track→channel) │ bridge down → SonicSF fallback (C1/C6/C7)
▼
Rust shm.rs write_midi_event (B2) ──► SHM "SonicForge_DAW_IPC"
▼
bridge main.cpp (A11 sampleOffset, A12 CC/PC/PB, A13 transport)
→ InstrumentEngineManager (A10) → FluidSynth / sfizz / VST3 (A6-A8)
▼
SHM masterLeft/Right + bridgeWriteIndex + blockTimestamp (A4)
▼
Rust audio pump (B5/B10 health) ─ emit 'bridge-audio' ─► bridgeAudioNode (C5)
▼
audioRoutingEngine (C3) → track sfEntry → track FX → masterBus → speakers
▼
VU analyser tap (C5) → track VU nhảy
PHỤ THUỘC (thứ tự thực hiện — đã cập nhật)
- A1-A4 → B1-B4 → C1-C2 + C5-C7 (skeleton + IPC + router + audio sink) — nền tảng.
- A10-A13 → B5, B8 → C3, C6 → D1-D2, D8-D9 (multi-instance + sample-accurate + audio pump + routing + wiring).
- A5-A7, A12 → B9-B10 → D3-D7, D10 → E1-E5 (engine thật + VST GUI + gỡ Carla + UI + backend).
- F1-F7 → G1-G10 (build pipeline + test) — cần máy Windows.
CÒN THIẾU SO VỚI SPEC (đã bổ sung vào tasks — bản đầy đủ)
- SHM tạo bởi Rust (B2) — spec chỉ có bridge mở.
- Đường MIDI JS→bridge (B4/C2) — spec dùng
__TAURI_IPC_SHM__không tồn tại. - Audio pump SHM→WebView (B5/C5) — spec nói zero-copy nhưng WebView không zero-copy được.
- Transport/panic + playhead position (A5/A13/B4/C1/D6/D9) — spec thiếu, cần cho timeline Stop/đồng bộ.
- Nhiều instrument/track (A10/D8) — spec chỉ 1
currentInstrument. - VST3 GUI child window (B9/A7) — spec có
openGUInhưng thiếu cơ chế HWND con từ Tauri. - CC/program/pitchbend (A12) — spec chỉ note on/off; app hiện có sustain/pitch wheel.
- Sample-accurate scheduling (A11/D9) — spec xử lý MIDI ngay lập tức, bỏ qua sampleOffset.
- Resampler khi SR AudioContext ≠ bridge (B8/C5).
- Ring buffer chống underrun + VU tap (C5) — kế thừa hành vi
_gainNode.__vuAnalysercũ. - Fallback SonicSF (C1/C6/C7/G6) — spec không đề cập; giữ để app không chết khi bridge lỗi.
- Dev-mode mock (C7) — chạy được trên Linux/browser không có
__TAURI__.