14 KiB
14 KiB
WALKTHROUGH — Thực thi tuần tự (SonicForgeStudio standalone)
Nạp file này mỗi session mới. Làm ĐÚNG 1 task, cập nhật bảng trạng thái, commit, dừng. Kiến trúc/lý do:
GIAI_PHAP.md. File này chỉ chứa thao tác + trạng thái → context nhỏ.
1. Thông tin cố định (mọi session)
- Repo:
C:/Users/locpham/SonicForgeStudio— branch:standalone— HEAD:cadb540(đã commit; KHÔNG push) - Build JS:
node node_modules/@babel/cli/bin/babel.js app/static/js/app.jsx --config-file ./babel.config.json -o app/static/js/app.precompiled.js - Test:
python -m pytest tests/→ 110 passed, 1 skipped, 1 failed (test_vst_engine.py::TestPluginManager::test_init— env-fail Linux path, bỏ qua, không sửa) - Rust check:
cd src-tauri && cargo check(nếu task C) - CRLF rule: file repo dùng CRLF. Đọc =
sed -n/grep(không dùng read_file). Sửa = python:s = open(p, encoding='utf-8', newline='').read()→ thay chuỗi\n→s.replace('\n','\r\n')→open(p,'w',encoding='utf-8',newline='').write(s). Verify lại bằng grep/repr. - Commit rule: sau mỗi task xanh (build+test OK) →
git add+ commitstandalone. Trước mỗi task:git statusphải sạch. - Trình tự: A (T1–T3) → B (T4–T7) → C (T8–T15) → D (T16).
2. Trạng thái
| ID | Phase | Task | Trạng thái |
|---|---|---|---|
| T1 | A | Bug 1: token fix | [ ] |
| T2 | A | Bug 2: chain re-apply + sig order + neutral add | [ ] |
| T3 | A | Bug 3: autosample pre-warm + toast + re-sample | [ ] |
| T4 | B | unifiedMidiRouter.js |
[ ] |
| T5 | B | TrackInstrument + keybed → router; xóa native preview/token |
[ ] |
| T6 | B | Scheduler items → router | [ ] |
| T7 | B | Hardware MIDI → router | [ ] |
| T8 | C | Rust vst_gui.rs + raw-window-handle |
[ ] |
| T9 | C | VST3 GUI bridge (vst3_host_bridge.cpp + DLL) |
[ ] |
| T10 | C | VST2 engine (VST2AudioEngine.cpp, vestige.h) |
[ ] |
| T11 | C | IPC param sync 2 chiều | [ ] |
| T12 | C | Native audio loop: SPSC + driver WASAPI/ASIO | [ ] |
| T13 | C | NativeMixer mirror mastering_engine.py |
[ ] |
| T14 | C | Port FluidSynth native (hoặc loopback) | [ ] |
| T15 | C | Bỏ WebAudio master; IPC gain/pan; test matrix | [ ] |
| T16 | D | Autosample hoàn thiện (VST2 .sf3, preset, re-sample) |
[ ] |
Trạng thái dùng: [ ] chưa làm, [▶] đang làm/dang dở, [x] xong, [!] fail — ghi lỗi vào Ghi chú.
3. Tiếp tục sau fail
- Đọc file này. Tìm task đầu tiên có
[ ]hoặc[▶]. - Nếu
[▶]: đọc Ghi chú (lỗi đã ghi), sửa tiếp. Nếu[ ]: nạp đúng mục "Context tối thiểu" của task (chỉ đọc các file/line đó — không đọc cả app.jsx). - Làm xong → Verify → build → pytest → cập nhật bảng
[x]+ Ghi chú 1 dòng → commit. - Fail không gỡ được: cập nhật
[!]+ Ghi chú lỗi đầy đủ (command, output, đoạn code), dừng, báo user.
4. Các task
T1 — Bug 1: token fix (Phase A)
- Mục tiêu: bấm phím keyboard preview lần 2+ không câm.
- File:
app/static/js/app.jsx - Context tối thiểu:
grep -n '_sfWasmFallbackNote\|playNativeSfNote\|_nativeSfPreviews' app/static/js/app.jsx→_sfWasmFallbackNoteL213-236,playNativeSfNoteL239-283. - Bước:
- Trong
_sfWasmFallbackNote: stale check_nativeSfPreviews[k].token !== token→ đổi thành> token(claim-then-compare) và LUÔN ghi đè_nativeSfPreviews[k] = { wasm, token }. - Cùng pattern
!== tokenở path API-success (~L257): sửa giống hệt.
- Trong
- Verify: build babel; mở app; bấm cùng phím 2–5 lần liên tiếp → mọi lần đều có tiếng; bấm nhanh nhiều phím → không câm.
- Done khi: không còn câm; không đổi hành vi khác.
- Rollback:
git checkout -- app/static/js/app.jsx - Ghi chú: Nếu T4–T5 đã xong (router xóa cơ chế này) → bỏ qua task này, đánh
[x]kèm "đã xóa tận gốc ở T5".
T2 — Bug 2: chain mastering (Phase A)
- Mục tiêu: thêm/sắp xếp module mastering không làm volume nhỏ đi; reorder đúng thứ tự.
- File:
app/static/js/app.jsx - Context tối thiểu:
grep -n 'applyMasteringSettings\|_lastMasteringSig\|chainSignature\|addModuleToChain\|_buildLimCurve\|_buildMaxCurve\|toggleMasteringOnMaster' app/static/js/app.jsx(L635+, L1097, L1486, L913-935, L12446). - Bước:
chainSignature(L1097): order-sensitive — signature phải gồm thứ tự module (không phải(type+active).join(',')set-insensitive).applyMasteringSettings(L635+, early-return L642): bỏ phụ thuộc sig; mọi thay đổi chain → rebuild + re-apply TOÀN BỘ tham số (debounce ~50ms thay vì skip).- Thêm module (limiter/compressor) qua
addModuleToChain: neutral-then-apply — limiter threshold mặc định 0dB (không clamp −1dB), compressor makeup bù loss (hoặc threshold cao hơn), để "thêm module" không tự cắt volume.
- Verify: build; thêm limiter → volume không giảm rõ rệt; thêm compressor → không giảm; reorder limiter↔compressor → nghe đổi thứ tự; master fader vẫn hoạt động đúng.
- Done khi: 3 bước trên đúng;
python -m pytest tests/xanh. - Rollback:
git checkout -- app/static/js/app.jsx
T3 — Bug 3: autosample UX (Phase A)
- Mục tiêu: VSTi không autosample được phải hiện rõ, không âm thầm ra oscillator default.
- File:
app/static/js/services/vstiAutosample.js,app/static/js/app.jsx(nơi chọn instrument), util toast hiện có. - Context tối thiểu:
grep -rn 'SonicVstiAutosample\|ensure' app/static/js/+grep -n '_playNoteFallback' app/static/js/soundfontPlayer.js(L787+). - Bước:
ensure(): bắt lỗi (404/501/import fail) → trả về object{ ok:false, reason }, không throw âm thầm.- UI: khi chọn instrument VSTi → gọi ensure NGAY (pre-warm) + progress indicator; note đầu tiên chờ ready (hoặc disable tới ready) — không rơi vào oscillator.
- Khi fail → toast "không autosample được VSTi: ".
- Nút re-sample trong panel instrument.
- Verify: chọn VSTi path sai → toast hiện reason, không ra âm oscillator; chọn VSTi hợp lệ → pre-warm xong, note đầu tiên ra đúng âm; bấm re-sample → chạy lại.
- Done khi: không còn fail âm thầm; pre-warm hoạt động.
- Rollback:
git checkout -- app/static/js/services/vstiAutosample.js app/static/js/app.jsx
T4 — unifiedMidiRouter.js (Phase B)
- Mục tiêu: một điểm dispatch MIDI cho mọi nguồn (spec Part II).
- File mới:
app/static/js/services/unifiedMidiRouter.js - Nội dung:
UnifiedMidiEvent(trackId, channel, command, pitch int 0-127, velocity int 1-127, sourceType, timestampNs) +UnifiedMidiRoutervớiengineRegistry: Map<trackId, TrackInstrument>(thay "1 engine toàn cục" của spec — route theo trackId);dispatchMidiEvent;activeVoiceTrackerkey${channel}_${pitch}chống stuck notes;panicAllNotesOff(). - Verify:
node --check app/static/js/services/unifiedMidiRouter.js; viết 1 test assert nhỏ (pitch clamp, velocity normalize float→int, tracker đếm đúng, panic clear) — chạy bằng node. - Done khi: file + test chạy xanh; chưa nối vào app.
- Rollback: xóa file.
T5 — TrackInstrument + keybed → router (Phase B)
- Mục tiêu: keyboard preview đi router; xóa cơ chế token/native.
- File:
app/static/js/app.jsx, có thể thêmapp/static/js/services/trackInstrument.js - Bước:
TrackInstrument: bọc FluidSynth channel per-track —playNote(pitch, vel, dur); dùng cho cả preview lẫn items.- Keybed
onMouseDown/onMouseUp→ router.dispatchMidiEvent (sourceType VIRTUAL_KEYBOARD). - XÓA:
playNativeSfNote,_sfWasmFallbackNote,_nativeSfPreviews, mọi__nativeSfOk/token logic.
- Verify: bấm phím liên tiếp 10 lần → không câm; không còn
_nativeSfPreviewstrong code (grep); build OK. - Done khi: Bug 1 hết tận gốc; preview vẫn ra tiếng đúng track/channel.
- Rollback:
git checkout -- app/static/js/app.jsx(xóa file mới nếu thêm).
T6 — Scheduler items → router (Phase B)
- Mục tiêu: MIDI items khi chạy timeline đi cùng router.
- File:
app/static/js/app.jsx(scheduler/playhead) - Bước: note-on/note-off của scheduler →
router.dispatchMidiEvent(TIMELINE_SCHEDULER); note-off dùng tracker đối xứng. - Verify: chạy timeline → items phát đúng pitch/vel; dừng giữa chừng →
panicAllNotesOffkhông stuck note. - Done khi: preview + timeline cùng engine, không nuốt voice.
T7 — Hardware MIDI → router (Phase B)
- File:
app/static/js/app.jsx(L15689navigator.requestMIDIAccess/onmidimessage) - Bước:
onmidimessage→router.handleHardwareKeyboardMessage(...)→ dispatch (HARDWARE_KEYBOARD); xóa nhánh xử lý cũ. - Verify: bấm phím MIDI hardware → ra tiếng qua router đúng track active; log sourceType.
- Done khi: 3 nguồn (keybed/scheduler/hardware) cùng một router.
T8 — Rust vst_gui.rs (Phase C)
- Mục tiêu: command
open_vst_gui(plugin_id, track_id)tạo floating child window. - File:
src-tauri/Cargo.toml(+raw-window-handle),src-tauri/src/vst_gui.rs,src-tauri/src/lib.rs(register command). - Nội dung (spec vsti_gui):
WebviewWindowBuilderlabelvst_gui_{track}_{plugin}, 800×600, always_on_top, mở lại thì focus;raw_window_handle()lấy HWND. - Verify:
cargo check; gọi từ JSinvoke('open_vst_gui', {...})→ cửa sổ nổi mở/đóng không crash. - Done khi: window nổi mở được; chưa gắn plugin GUI (T9).
T9 — VST3 GUI bridge (Phase C)
- File mới:
native_host/vst3_host_bridge.cpp+ build DLL (MSVC/CMake) - Nội dung (spec):
attach_vst3_editor_to_handle(plugin_id, parent_handle)→createView(kEditor)→attached(hwnd, kPlatformTypeHWND)→getSize()→ resize. Đóng:removed()+release(). - Quyết định trước khi làm: VST3 SDK thuần (nhẹ) HAY JUCE (gộp luôn VST2 — xem T10). Khuyến nghị: JUCE nếu muốn 1 engine cho cả 2; SDK thuần nếu chỉ VST3.
- Verify: mở GUI VST3 trong cửa sổ nổi, chỉnh param, đóng → không leak/crash.
- Done khi: GUI plugin hiển thị + đóng sạch.
T10 — VST2 engine (Phase C)
- File mới:
native_host/VST2AudioEngine.cpp(dùngvestige.h— clean-room, KHÔNG dùng aeffect.h chính thức; legal note spec III) - Nội dung (spec VST2):
loadPlugin(LoadLibraryW +VSTPluginMain/main+ dispatcher init),sendMidiNoteOn(effProcessEvents,deltaFrames),attachGUI(effEditOpen+effEditGetRect),processAudioBlock(processReplacing), destructor teardown.HostAudioMasterphải trả lời opcode version/sampleRate/blockSize. - Verify: load .dll VST2 → mở GUI → processReplacing ra tín hiệu.
- Done khi: VST2 GUI + audio thô chạy (chưa vào mixer).
T11 — IPC param sync (Phase C)
- File:
src-tauri/src/vst_gui.rs+ JS side - Nội dung: native param đổi →
vst_param_changed→ JS cập nhật state DAW; JS automation →set_param/setParamNormalized→ native. - Verify: xoay knob plugin → JS state đổi; đổi tham số từ DAW → plugin đổi.
- Done khi: sync 2 chiều không lag, không vòng lặp vô hạn (guard flag).
T12 — Native audio loop (Phase C)
- File mới:
native_host/VST3AudioEngine.cpp(hoặc gộp với T9/T10 theo quyết định JUCE), SPSC ring buffer - Nội dung (spec audio): UI thread → SPSC queue → audio thread build EventList/VstEvents →
process()/processReplacing→ pre-alloc buffers; zero-allocation trong audio thread; driver WASAPI Exclusive/ASIO 128–256 samples;setProcessing(true)1 lần lúc init; sampleRate từ negotiate. - Verify: phát preview qua native driver, 0 underrun; đo latency ≤ ~5ms.
- Done khi: VSTi ra loa native, không qua WebAudio.
T13 — NativeMixer (Phase C)
- File mới:
native_host/NativeMixer.cpp - Nội dung (spec audio tier 3): track gain
10^(dB/20), pan constant-power, master sum, brickwall limiter — MIRRORapp/core/mastering_engine.py(đọc file đó trước). - Verify: golden test — render cùng 1 đoạn bằng native mixer và server
render_project(cùng mastering settings) → so RMS/peak sai lệch < 0.1dB. - Done khi: native mixer khớp server; track gain/pan áp cho cả VST2/VST3.
T14 — Port FluidSynth native (Phase C)
- Mục tiêu: SF track vào CÙNG native mixer (một master duy nhất).
- Bước: tích hợp libfluidsynth C++ vào native host; track SF → FluidSynth native → NativeMixer. (Nếu không port được: loopback WASM→native — phức tạp, ghi quyết định vào Ghi chú.)
- Verify: SF track + VSTi track cùng master; gain/pan/mastering áp đúng cả 2; preview + items + offline nhất quán.
- Done khi: một mixer duy nhất, hết WebAudio cho track instrument.
T15 — Bỏ WebAudio master (Phase C)
- Bước: track instrument không còn đi
masterBusWebAudio; IPCset_master_gain,set_track_gain_pantừ JS → native; giữ WebAudio chỉ cho UI/aux nếu còn. - Verify: test matrix spec V: VST3/VST2/SF × preview/live/offline đều đúng âm lượng và mastering.
- Done khi: Bug 2 không còn drift (một engine duy nhất); checklist spec 2 (negotiate SR/buffer, jitter <0.1ms, 0% underrun) đạt.
T16 — Autosample hoàn thiện (Phase D)
- File:
tools/autosample_vsti.py,app/static/js/services/vstiAutosample.js - Nội dung: autosample VST2 →
.sf3cho offline (Pedalboard không hỗ trợ VST2 → giữ con đường này cho export); tôn trọng preset_path/preset_data; hiển thị note_count/size; re-sample khi đổi preset. - Verify: export track VST2 ra file đúng âm; đổi preset → re-sample tự động; UI hiện size.
- Done khi: matrix hoàn chỉnh; toàn bộ test xanh; commit cuối.
5. Checkpoint tổng
- Sau T3: 3 bug đã vá (tạm) — bản phát hành an toàn.
- Sau T7: unified MIDI pipeline xong phía client — Bug 1 hết tận gốc.
- Sau T15: native audio hoàn chỉnh — Bug 2 hết drift, Bug 3 hết fallback mặc định.
- Sau T16: matrix spec V đầy đủ.