Files
SonicForgeStudio/IMPLEMENT_PHASE_3.md
T

8.0 KiB
Raw Blame History

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/midi vào app/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}); đổi triggerMidiExport() (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.mjs OK + PyInstaller rebuild engine + copy dist/daw_engine/* sang src-tauri/target/release/daw_engine/ + bump cache-bust app.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ần allOnServer && connected), client clientSideExport (OfflineAudioContext, raw buffer không fade), bounce triggerBounceExport (realtime capture master bus, trước đây luôn +1.2s tail + blob download). Worker app/tasks/worker.py tôn trọng fade_in_ms/fade_out_ms từ 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), clip export_loop_clip startTime 0, luôn đi client path (buffer đã cắt phía client), gọi clientSideExport(exportTracks, {loopStart, loopDur}).
  • clientSideExport(activeTracks, loopInfo): durationLimit = loopDur khi loop (không kéo đuôi); midi cache buffer cắt subarray(0, loopDur*sr); fix bug: midi cache không được kéo dài durationLimit khi loop (file không dư đuôi); WAV thay blob download bằng downloadBlobViaServer.
  • triggerBounceExport: khi loop → durationLimit = loopDur (bỏ +1.2 tail), sau capture cắt cứng [loopStart, loopStart+loopDur) frames khỏi stereo buffer, download qua downloadBlobViaServer.
  • Build: node build.mjs OK (2 lần — lần 2 sau fix durationLimit) + PyInstaller rebuild engine + copy dist/daw_engine/* sang src-tauri/target/release/daw_engine/ + bump cache-bust app.precompiled.js?v=202608211105.
  • Verify chạy thật: engine serve app.precompiled.js mới (md5 khớp build local, đủ loop UI/loopActive/downloadBlobViaServer/export_loop_clip), index.html ?v=202608211105; /upload + /download roundtrip 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.py PASS (energy 12.337, ring decay), tools/test_vst2_gui.py PASS (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 theo loopStart — đú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).
  • 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ũ. addMod giờ reset setActiveVstIdx(-1) để quay về editor builtin khi thêm DSP.
  • MasteringModal: header MASTERING 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 → FXRackModal lookup track giờ dùng (activeTracksRef.current || []).find(...) || tracks.find(...) || null — an toàn khi activeTracks là session-tab clones ≠ root tracks (trước: tracks.find có thể null → modal không mở).
  • Build: node build.mjs OK (2 lần — lần 2 sau fix lookup); copy app/static/js/app.precompiled.js + app/templates/index.html sang bundle src-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_energy energy 0 = app chưa load instrument đúng; test_sf2_ring energy 12.337→0.046; test_vst2_gui DUNE 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)