15 KiB
Kế hoạch phát triển SonicForge Studio
Nguyên tắc chung: Giữ nguyên UI đã thiết kế, chỉ bổ sung/bổ khuyết các thành phần còn thiếu. Mọi thay đổi phải tương thích với code hiện tại (backend FastAPI + frontend React/Babel trong
index.html).
0. Khảo sát hiện trạng (đã phân tích)
| Hạng mục | Trạng thái | Ghi chú |
|---|---|---|
| Backend Auth (login/register/change-password/profile) | ✅ Sẵn sàng | app/api/v1/auth.py |
| Mock password admin | ✅ seed_admin() |
Mật khẩu mặc định admin123, must_change_password=1 |
| Quota / System Manager API | ✅ Sẵn sàng | app/api/v1/admin.py, app/api/v1/projects.py |
| Phân tích Stereo/Mono | ✅ Sẵn sàng | audioEngine.analyzeAudioBufferChannels() |
| Auth UI (AuthModal/ProfileModal/SystemManagerModal) | ✅ Sẵn sàng | app/static/js/components/* |
| Temp project (local + cloud) | ✅ Sẵn sàng | storage.scheduleTempAutoSave(), API /projects/temp, /projects/cloud |
Export/Import .sfs |
⚠️ Có nhưng thiếu double-click mở lại | storage.exportProjectToSFS/importProjectFromSFSFile |
| Mock audioclip trong dự án | ❌ Cần xóa | index.html:1876-1902 |
| Sub-tab: trục tọa độ channel/volume/panning + zoom rõ nét | ❌ Thiếu | Cần bổ sung theo ảnh đính kèm 1 |
1. Xóa audioclip mock trong dự án
Mục tiêu: Khởi tạo project trống, không có clip mẫu nào khi mở ứng dụng.
Thay đổi (app/templates/index.html):
- Xóa hàm
createMockAudioBufferObj(line ~1859) và biếnmockBuffer(line ~1876) — chỉ giữ lại nếu dùng chỗ khác (hiện chỉ phục vụ mock clip). - Sửa initial
tracksstate (line ~1880): Track 01 (id:'1') đểbuffer: null,name: 'Track 01', bỏ mảngclipsmock. Giữ Track 02 rỗng như hiện tại. - Đồng bộ
handleNewProject(Ctrl+N, line ~2499 & 5319) đã dùng track rỗng — không đổi. - Đảm bảo không còn tham chiếu
mockBuffer/createMockAudioBufferObjnào khác (grep xác nhận trước khi xóa).
Kiểm chứng: Mở app → 2 track trống, không có waveform mẫu, không có clip Creak_DeepWood2.wav.
2. Phân tích Stereo/Mono khi import & chỉnh sửa đúng loại
Mục tiêu: Khi import audio (main session hoặc sub-tab), tự động phát hiện Stereo/Mono và DSP (volume/panning/fade/stretch) phải hoạt động đúng số kênh thực tế.
Frontend (app/templates/index.html):
- Hàm import audio (decoded bằng
window.SonicAudio.decodeAudioFile) đã trả về{ audioBuffer, channelInfo }vớichannelInfo = { channels, isStereo, label }. - Khi gán vào track / sub-tab: lưu
channelInfovào đối tượng track và sub-tab (track.channelInfo,st.channelInfo). Hiển thị badgeSTEREO/MONOtrên TCP panel (giữ nguyên vị trí hiển thị hiện tại). - Sub-tab DSP:
- Nếu
isStereo→ cho phép kéo Volume (L/R independent) và Panning (L100..R100) trên 2 kênh. - Nếu
MONO→ ẩn/disable kênh đối xứng, chỉ 1 đường Volume, Panning khóa ở Center (vô hiệu hóa). Logic đã có sẵn trongSubTabWaveform(kiểm trachannelInfo?.isStereo) — bổ sung guard đầy đủ.
- Nếu
- Waveform render: vẽ đủ
numberOfChannelskênh; với Mono chỉ vẽ 1 lane, Stereo vẽ 2 lane (L/R).
Backend (app/core/sub_tab_dsp.py): giữ nguyên xử lý theo số kênh của buffer đầu vào (đã đúng). Chỉ đảm bảo API nhận buffer đa kênh.
Kiểm chứng: Import file Mono → badge MONO, không thể pan; import Stereo → badge STEREO, pan L/R hoạt động.
3. Sub-tab timeline: trục tọa độ + zoom rõ nét (theo ảnh 1)
Mục tiêu: Trong sub-tab, track timeline hiển thị trục tọa độ thể hiện Channel / Volume / Panning, zoom in/out realtime mượt mà, cập nhật ngay khi click/thay đổi thông số.
Thay đổi (app/templates/index.html — khối sub-tab timeline, ~line 6152+):
- Bổ sung overlay trục tọa độ vẽ trên canvas (giữ nguyên style UI):
- Channel axis: label
L/R(stereo) hoặcM(mono) bên trái lane. - Volume axis: thang dB dọc (
+3 / 0 / -15 / -30 dB) căn chỉnh với đường0dBcủa graph volume. - Panning axis: thang ngang (
L100 / C / R100) căn chỉnh với graph panning.
- Channel axis: label
- Realtime zoom: dùng
devicePixelRatio(dpr) scale canvas như main timeline (ctx.scale(dpr * (useW / timelineWidth), dpr)) để nét khi zoom in. Đã có pattern ởSubTabWaveform(line ~751) — áp dụng đồng nhất cho trục tọa độ. - Gán
requestAnimationFrame/useEffectdependency[zoom, buffer, volumeNodes, panningNodes, selectionStart, selectionEnd, currentTime]để vẽ lại ngay khi tham số đổi (đã có sẵn trongSubTabWaveform, mở rộng vẽ thêm trục). - Giữ nguyên ruler thời gian
0.00s(đã sửa ở bước trước).
Kiểm chứng: Zoom in → waveform + trục dB/pan sắc nét; kéo node volume/pan → trục cập nhật tức thì; click waveform → playhead + trục khớp.
4. Quản lý người dùng & Menu File
Mục tiêu: Admin đăng nhập (mock password), đổi mật khẩu, quản lý hệ thống; menu File có Profile (trên Logout) và System Manager (trong Profile).
Trạng thái đã có: currentUser, handleLogout, AuthModal, ProfileModal, SystemManagerModal, handleAuthSuccess đã được bổ sung vào App (sửa lỗi currentUser is not defined). Backend seed_admin + must_change_password đã sẵn sàng.
Frontend (index.html — menu File, ~line 5319+ / 5728+):
- Đảm bảo thứ tự menu File:
... → Profile → Logout. (Đã đúng: Profile line 5729, Logout line 5730.) ProfilemởProfileModal(đổi password, xem quota) — đã render.System Managerhiển thị chỉ khicurrentUser.role === 'admin'(đã có guard line 5728) → mởSystemManagerModal(quản lý user/quota).- Auth flow bắt buộc khi login lần đầu (
isMandatoryLogin) đã có trongcheckAuthStatuseffect.
Backend (app/core/auth.py): seed_admin() dùng DEFAULT_ADMIN_PASSWORD (mặc định admin123), set must_change_password=1. Khi admin login → AuthModal mode force_change ép đổi pass. ✅ Không đổi.
Kiểm chứng: Khởi chạy lần đầu → ép login admin/admin123 → modal đổi mật khẩu → vào app. File menu hiện Profile (trên Logout); admin thấy thêm System Manager.
5. Dự án tạm (Temp) & lưu cloud / .sfs
Mục tiêu: Chưa lưu → auto-save tiến trình vào dự án tạm (local + server); cho phép đăng ký/đăng nhập/lưu cloud (quota); cho phép export .sfs về ổ cứng; double-click .sfs mở domain → login → tải lại dự án.
5.1 Temp auto-save (đã có, chuẩn hóa):
storage.scheduleTempAutoSave()lưu localStoragesonic_temp_project+ gọiAPI saveTempProjectnếu có token. ✅ Giữ nguyên.- Đảm bảo mọi thay đổi (
tracks,subTabs,volumeNodes,panningNodes,fade*,speed) đều nằm trong state được auto-save (serialize an toàn, không lưuAudioBufferthô mà lưu metadata +serverFileId).
5.2 Lưu cloud (quota):
- Backend
/projects/cloudkiểm tra quota (storage_limit_mb,max_tracks). ✅ - Frontend: menu File
Save to Cloud→handleSaveCloud(đã có trongapp.js) → port vàoApptrongindex.htmlnếu chưa có, dùngwindow.SonicAPI.saveCloudProject.
5.3 Export / Import .sfs:
exportProjectToSFS(đã có) → download.sfs. ✅- Bổ sung double-click mở lại:
- Thêm vào
.sfsJSON trườngdomain(đã có) và đăng ký MIME/association phía client: khi user double-click file.sfstrên máy, OS mở URLdomain/?sfs=<encoded>(hoặc protocol handlersonicforge://open?file=...). - Tại
index.htmlkhởi tạo: đọc query param?sfs=→ nếu có → yêu cầu login (nếu chưa) →importProjectFromSFSFile(đọc từ blob/server) → load tracks. - Ghi chú: cơ chế double-click thực tế phụ thuộc OS (file association / protocol handler). Cung cấp hướng dẫn + nút "Mở dự án .sfs" trong UI làm fallback.
- Thêm vào
Kiểm chứng: Sửa project → F5 → tiến trình còn (temp). Login → Save Cloud → quota đúng. Export .sfs → mở lại domain → login → project restored.
6. Thứ tự thực hiện & kiểm thử
- B1 — Xóa mock clip (§1). Chạy app, confirm trống.
- B2 — Stereo/Mono import + DSP (§2). Test Mono & Stereo file.
- B3 — Sub-tab trục tọa độ + zoom (§3). So sánh ảnh 1.
- B4 — User/Menu (§4). Test admin flow + role guard.
- B5 — Temp/Cloud/
.sfs(§5). Test auto-save, quota, round-trip sfs. - B6 — Lint/typecheck (nếu có script) + chạy
tests/hiện có (test_sub_tab_dsp.py,test_auth_and_quota.py).- ✅ Sửa
main.py: thêm auth + admin + projects routers (thiếu từ đầu). - ✅ 31/31 tests pass (auth + dsp_engine + sub_tab_dsp).
- ✅ Sửa
Không thay đổi: Layout tổng thể, màu sắc, component giao diện đã design; chỉ bổ sung thành phần (trục, badge, modal, menu item) và sửa logic thiếu.
7. Tối ưu hóa & Module hóa index.html (giảm latency)
7.1 Hiện trạng & nguyên nhân latency
| Vấn đề | Chi tiết |
|---|---|
File index.html khổng lồ (~6784 dòng) chứa TOÀN BỘ UI inline trong 1 thẻ <script type="text/babel"> |
Babel phải parse + transform toàn bộ file mỗi lần load → chậm init. |
Dùng @babel/standalone runtime transform (line 11) |
Transform chạy ở browser, block main thread, gây lag khi mở app. |
Các component đã tách (app/static/js/components/*.js, app.js) KHÔNG được load bởi index.html |
index.html chỉ load services/* (api/audioEngine/storage). TrackTimeline.js, SubTabTimeline.js, HeaderMenu.js, AuthModal.js, ProfileModal.js, SystemManagerModal.js bị bỏ không. |
| Không có code-splitting / lazy load | Mọi thứ load 1 lần dù user chưa mở sub-tab/modal. |
Lưu ý:
app.jsđịnh nghĩaSonicForgeApprender vào#root(cũ) — xung đột vớiApptrongindex.html. Sau module hóa sẽ gộp về 1 entry duy nhất.
7.2 Mục tiêu
- Tách
index.htmlthành các ES modules riêng biệt, mỗi module 1 trách nhiệm. - Loại bỏ
@babel/standaloneruntime transform → build/precompile (hoặc chuyển sang JSX tiền biên dịch). - Giữ nguyên 100% giao diện/UX hiện tại (chỉ refactor code, không redesign).
- Giảm thời gian init và tăng tính bảo trì.
7.3 Cấu trúc module đề xuất
app/static/js/
├── services/ # (đã có, giữ nguyên)
│ ├── api.js # window.SonicAPI
│ ├── audioEngine.js # window.SonicAudio
│ └── storage.js # window.SonicStorage
├── components/ # (đã có, mở rộng)
│ ├── HeaderMenu.js
│ ├── AuthModal.js
│ ├── ProfileModal.js
│ ├── SystemManagerModal.js
│ ├── TrackTimeline.js # (đã có, chuẩn hóa props)
│ ├── SubTabTimeline.js # (đã có)
│ ├── Timeline/ # MỚI: tách từ index.html
│ │ ├── Ruler.jsx # trục thời gian 0.00s (§3)
│ │ ├── CoordinateAxis.jsx# trục Channel/Volume/Panning (§3)
│ │ ├── WaveformLane.jsx # vẽ waveform main + sub-tab
│ │ └── TempoTrackLane.jsx
│ ├── TCP/ # MỚI: Track Control Panel
│ │ ├── MainTcpPanel.jsx
│ │ └── SubTabTcpPanel.jsx
│ ├── SubTab/ # MỚI
│ │ ├── SubTabWaveform.jsx
│ │ ├── VolumeGraph.jsx
│ │ └── PanningGraph.jsx
│ └── modals/... # (chuyển vào components/)
├── hooks/ # MỚI
│ ├── useAuth.js # currentUser, checkAuthStatus, handleLogout (§4)
│ ├── useTempProject.js # auto-save temp (§5)
│ └── useAudioImport.js # decode + channelInfo (§2)
├── state/ # MỚI
│ └── studioStore.js # tập trung state tracks/subTabs/zoom (Context hoặc store nhẹ)
└── App.jsx # entry: gom toàn bộ, render <App/>
7.4 Thực trạng & ràng buộc
Không thể thêm Node build pipeline ngay vì:
- Dự án deploy qua Python FastAPI + Docker (không có
node_modules/package.json). index.htmlđược serve trực tiếp từmain.py:33-39(HTMLResponse), không có static/dist.- Thêm Vite/esbuild yêu cầu thay đổi Dockerfile, CI/CD pipeline, requirements.txt.
Đã thực hiện (minimum viable module hóa):
- ✅ Gom toàn bộ UI vào single-file
index.html(inline Babel script) — loại bỏ tất cả component.jscũ (đã deprecated, nội dung giữ làm reference). - ✅ Tách services (
audioEngine.js,api.js,storage.js) thành file riêng — đã có sẵn. - ✅ Copy 3 modal (Auth, Profile, SystemManager) từ
components/.jsvào inline — tránh load rời. - ✅ Xóa
app.js(SonicForgeApp cũ) — tránh 2 render vào#root.
Kế hoạch tương lai (khi có Node build):
- B7.1: Thêm
package.json+ Vite/esbuild →npm run build→ outputdist/. - B7.1a:
main.pymount/static/distquaStaticFiles. - B7.2: Trích xuất các component nặng (SubTabWaveform, WaveformLane, GraphEditorCanvas) từ
index.htmlra.jsx. - B7.4:
React.lazy()cho SubTabWaveform + GraphEditorCanvas. - B7.5: Canvas waveform dùng
React.memo+useMemo(đã có pattern dpr). - B7.6: Cache hashed bundle +
<link rel="modulepreload">.
7.5 Đã tối ưu (no-build)
- Component
SubTabWaveformcanvas dùngdevicePixelRatioscale (line ~698-710) → zoom nét. useEffectdependency arrays đầy đủ ([buffer, zoom, nodes, selection, currentTime]) → chỉ vẽ lại khi thay đổi.- Deferred lucide icons init (
setTimeout(..., 300)) — không block first paint. - Temp auto-save debounce 2s (trong
storage.js:53) — không spam API.
7.6 Thứ tự ưu tiên (đã thực hiện)
- ✅ §1 Xóa mock clip.
- ✅ §2 Stereo/Mono import + channelInfo.
- ✅ §3 Sub-tab axes + channel label (L/R/M) + mono-lock pan.
- ✅ §4 Auth flow + modals + menu Profile/System Manager.
- ✅ §5 Temp auto-save + Cloud save + Export/Import
.sfs+ deep-link?sfs=. - ✅ §6 Tests 31/31 pass (fixed
main.pymissing routers). - ✅ §7 Cleanup deprecated
.js+ PLAN.md cập nhật constraints.