8.0 KiB
8.0 KiB
IMPLEMENT_PHASE_3 — Nhật ký thực hiện BIG_PHASE_3
Cập nhật theo từng phase. Trạng thái: ✅ hoàn thành · 🔄 đang làm · ⏳ chưa làm · ⚠️ cần xác nhận/review Repo:
C:/Users/locpham/SonicForgeStudio· branch:standalone-shm-bridge
TỔNG QUAN
| Phase | Nội dung | Trạng thái | Ghi chú |
|---|---|---|---|
| 1 | Fix Export MIDI | ✅ | backend + frontend + build + verify xong (commit riêng) |
| 2 | Fix Export Mix + loop | ✅ | frontend + build + verify xong (commit riêng) |
| 3 | FX CHAIN (hợp nhất modal) | ⏳ | |
| 4 | Nút FX TCP → FX CHAIN | ⏳ | wiring đã có, cần verify |
| 5 | Virtual MIDI Keyboard | ⏳ | |
| 6 | Chords Panel | ⏳ | AI search defer |
| 7 | Build tổng + verify | ⏳ |
PHASE 1 — FIX EXPORT MIDI
Đã làm
- Khảo sát: 2 nơi export MIDI đều dùng blob download (
a.click()+URL.createObjectURL) — nghi vấn WebView2 chặn (đã fix bằng server download, verify chạy thật OK). - Backend: thêm
POST /api/v1/export/midivàoapp/api/v1/audio.py— sinh file .mid format 1 (stdlib thuần), lưu vào PROCESSED_DIR, trảfile_id+download_url(download qua GET network — pattern đã chạy ổn định). Bỏ track không nốt; velocity 0..1 → scale 1..127. - Frontend: thêm helper
serverDownloadMidi(payload, filename)(POST endpoint → anchor download${API_BASE_URL}${download_url}); đổitriggerMidiExport()(File menu, bỏ velocity*100, bỏ rest-track pitch 0) + nút piano roll "Export MIDI" (payload 1 track, guard không nốt). - Build:
node build.mjsOK + PyInstaller rebuild engine + copydist/daw_engine/*sangsrc-tauri/target/release/daw_engine/+ bump cache-bustapp.precompiled.js?v=202608211103. - Verify: chạy app thật (engine mới), POST
/export/midi→ GET download → parse bytes: format 1, division 480, đủ track, velocity đúng (0.5→64, 1.0→127, 0.8→102), track rỗng bị bỏ (2→1 track).
Tồn tại / cần bổ sung
- (trống)
Cần xác nhận (review)
- (trống)
PHASE 2 — FIX EXPORT MIX + LOOP
Đã làm
- Khảo sát: 3 đường xuất — server
POST /multitrack/mix(cầnallOnServer && connected), clientclientSideExport(OfflineAudioContext, raw buffer không fade), bouncetriggerBounceExport(realtime capture master bus, trước đây luôn +1.2s tail + blob download). Workerapp/tasks/worker.pytôn trọngfade_in_ms/fade_out_mstừ clip. - UI ExportModal: checkbox "Xuất loop (không fade in / không fade out)" + input "Số ô nhịp" (1..256, mặc định 4).
- Helper
downloadBlobViaServer(blob, filename): POST/api/v1/audio/upload→ download${API_BASE_URL}/api/v1/audio/download/{file_id}; fail → fallback blob download + throw (pattern Phase 1). triggerWavExport: khi loop → cắt buffer từng track đúng[loopStart, loopStart+loopDur)(loopSecPerBar=(60/bpm)*4,loopStart= selection nếu hợp lệ else playhead), clipexport_loop_clipstartTime 0, luôn đi client path (buffer đã cắt phía client), gọiclientSideExport(exportTracks, {loopStart, loopDur}).clientSideExport(activeTracks, loopInfo):durationLimit = loopDurkhi loop (không kéo đuôi); midi cache buffer cắtsubarray(0, loopDur*sr); fix bug: midi cache không được kéo dàidurationLimitkhi loop (file không dư đuôi); WAV thay blob download bằngdownloadBlobViaServer.triggerBounceExport: khi loop →durationLimit = loopDur(bỏ +1.2 tail), sau capture cắt cứng[loopStart, loopStart+loopDur)frames khỏi stereo buffer, download quadownloadBlobViaServer.- Build:
node build.mjsOK (2 lần — lần 2 sau fix durationLimit) + PyInstaller rebuild engine + copydist/daw_engine/*sangsrc-tauri/target/release/daw_engine/+ bump cache-bustapp.precompiled.js?v=202608211105. - Verify chạy thật: engine serve
app.precompiled.jsmới (md5 khớp build local, đủ loop UI/loopActive/downloadBlobViaServer/export_loop_clip), index.html?v=202608211105;/upload+/downloadroundtrip OK;POST /multitrack/mix(server path) SUCCESS → file 2.0s đúng, all samples nonzero (không regression). - Self-check toán loop (node
tools/_loopcheck.mjs): độ dài WAV =loopBars*4*60/bpm±0 sample với mọi tổ hợp (120bpm×4, 120×4 target 48k, 90×8 start 1.5s, 140×1), biên đầu/cuối giữ nguyên mẫu (không fade) — PASS. - Regression cũ:
tools/shm_note_energy.py(energy 0 — app chưa load instrument, đúng),tools/test_sf2_ring.pyPASS (energy 12.337, ring decay),tools/test_vst2_gui.pyPASS (DUNE host + editor VSTGUI, proc alive).
Tồn tại / cần bổ sung
- Loop với track MIDI: midi cache cắt từ đầu cache (
subarray(0, loopDur*sr)), chưa offset theoloopStart— đúng với thiết kế cache preview (cache bắt đầu từ preview), ghi nhận để Phase 7 review nếu cần.
Cần xác nhận (review)
- (trống)
PHASE 3 — FX CHAIN (BUILTIN DSP + VST FX 1 CHAIN)
FXRackModal: header{track.name} — FX RACK PANEL→{track.name} — FX CHAIN(grep sạch "FX RACK PANEL" còn lại chỉ comment nội bộ dòng 11478).- Gộp CHAIN RACK STRIP + block VST FX CHAIN thành MỘT strip
unifiedChain = [...chain.map(m=>({kind:'builtin',m,i})), ...vstChain.map(s=>({kind:'vst',s,i}))]:- Slot builtin = card cũ nguyên vẹn (power/toggleMod, name, remove, click →
setActiveType(m.type)). - Slot vst = card compact (power/toggleVst, name, removeVst, click →
setActiveVstIdx(u.i), active = viền teal). - Drag reorder trên unifiedChain → split lại
setChain(nb)+setVstChain(nv); khi kéo slot vst →setActiveVstIdx(nv.indexOf(mv.s))giữ đúng slot đang chọn. - 2 nút thêm cuối strip:
[+](addModuleOpen, DSP) +[VST](vstPickerOpen).
- Slot builtin = card cũ nguyên vẹn (power/toggleMod, name, remove, click →
- Giữ toolbar VST mỏng dưới strip: "Nghe thử (render offline)" (
previewFx) + checkbox Bypass + hint VST3 áp dụng lúc Export/Offline. - Workspace: khi
activeVstIdx>=0 && < vstChain.length && !fxGuiOpen→ hiện detail VST đầy đủ (vstRow: select đổi plugin, Preset upload, GUI, Carla, lên/xuống, xóa); ngược lại → module editor builtin như cũ.addModgiờ resetsetActiveVstIdx(-1)để quay về editor builtin khi thêm DSP. MasteringModal: headerMASTERING SUITE→MASTER FX CHAIN(giữ badge "WEB MASTERING V10.5" làm subtitle); popup thêm module →THÊM MODULE VÀO MASTER FX CHAIN. Chain strip master đã unified sẵn từ trước (builtin + vst3 1 strip).- Nút "MASTERING PANEL" (MasterStripConsole) → "FX CHAIN"; tooltip "Mở/Bật-Tắt MASTERING PANEL ..." → "MASTER FX CHAIN ..."; menu File "Mastering Suite" → "Master FX Chain" (giữ Ctrl+Shift+M).
- Phase 4 verify sẵn:
__openFxRack(track.id, track.name)→fxRackTarget→FXRackModallookup track giờ dùng(activeTracksRef.current || []).find(...) || tracks.find(...) || null— an toàn khi activeTracks là session-tab clones ≠ root tracks (trước:tracks.findcó thể null → modal không mở). - Build:
node build.mjsOK (2 lần — lần 2 sau fix lookup); copyapp/static/js/app.precompiled.js+app/templates/index.htmlsang bundlesrc-tauri/target/release/daw_engine/_internal/app/(frontend-only, không cần PyInstaller); bump cache-bust?v=202608211133. - Verify chạy thật: engine serve precompiled md5
9d49f209...khớp build local, index?v=202608211133, bundle chứa "FX CHAIN"/"MASTER FX CHAIN"/unifiedChain/lookup activeTracksRef; 3 regression cũ PASS (shm_note_energyenergy 0 = app chưa load instrument đúng;test_sf2_ringenergy 12.337→0.046;test_vst2_guiDUNE host + editor VSTGUI, proc alive).
PHASE 4 — NÚT FX TCP
(chưa bắt đầu)
PHASE 5 — VIRTUAL MIDI KEYBOARD
(chưa bắt đầu)
PHASE 6 — CHORDS PANEL
(chưa bắt đầu)
PHASE 7 — BUILD TỔNG & VERIFY
(chưa bắt đầu)