feat: vẽ nhanh MIDI note
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Kế hoạch Thực hiện: Tính năng DAW mới (Track FX, Section Preview & Piano Roll Edit Shortcuts)
|
||||
|
||||
Bản kế hoạch này mô tả thiết kế kỹ thuật và các bước thực hiện các tính năng nâng cao liên quan đến xử lý hiệu ứng âm thanh (FX Chain) song song giữa Web Audio Client và Python Backend, kết xuất trực quan Section item trên Timeline, và bổ sung các phím tắt biên tập thông minh trong Piano Roll.
|
||||
|
||||
## Điểm lưu ý từ Người dùng
|
||||
1. **Kiến trúc hiệu ứng Song song (Dual FX Architecture)**:
|
||||
- **Phía Client (Trình duyệt)**: Vẫn phải sử dụng Web Audio API để xử lý hiệu ứng thời gian thực (Real-time Preview) khi nhấn Play nghe thử.
|
||||
- **Phía Server (Backend)**: Sử dụng các thư viện Python (`Pedalboard`, `pydub`, `pysndfx`, `Librosa`...) để xử lý hiệu ứng khi xuất bản kết xuất (Offline Export / Render).
|
||||
2. **Timeline Playback**: Khi Playhead trên Main Session quét qua Section item, trình phát nhạc sẽ phát nội dung của Section item đó giống như đang nghe thử trên Section-tab.
|
||||
3. **Sửa lỗi Release Note**: Khi click chọn nốt mới để kéo thả, các nốt cũ đã chọn trước đó phải được giải phóng hoàn toàn và không di chuyển theo nốt mới.
|
||||
|
||||
---
|
||||
|
||||
## Phân tích Ưu/Nhược điểm & Giải pháp Tối ưu hóa (Dual FX)
|
||||
|
||||
### 1. Ưu điểm (Pros)
|
||||
* **Trải nghiệm Tức thì (Zero Latency)**: Người dùng nghe thấy hiệu ứng ngay lập tức khi kéo nút hoặc đổi chế độ mà không cần đợi gửi file lên server xử lý.
|
||||
* **Giảm tải cực lớn cho Server**: Trình duyệt tự gánh vác phần giải mã và xử lý DSP thời gian thực trên thiết bị người dùng, máy chủ chỉ cần chạy xử lý khi người dùng xuất bản (Export).
|
||||
* **Chất lượng Master tuyệt đối**: Bản xuất ra (Render) trên Backend sử dụng thuật toán C++ tối ưu cao của `pedalboard` hoặc các bộ lọc khoa học của `scipy` giúp chất lượng âm thanh đạt chuẩn phòng thu, không bị méo tiếng do giới hạn tài nguyên của trình duyệt.
|
||||
|
||||
### 2. Nhược điểm (Cons)
|
||||
* **Độ lệch âm thanh (Sound Discrepancy)**: Bản nghe thử trên trình duyệt (Web Audio API) và bản xuất ra (Python Backend) có thể có khác biệt nhỏ về màu sắc âm thanh nếu thuật toán tạo Chorus/Reverb khác nhau.
|
||||
* **Trùng lặp mã nguồn (Code Duplication)**: Phải viết logic xử lý hiệu ứng ở cả 2 ngôn ngữ (JavaScript trên Client và Python trên Server).
|
||||
|
||||
### 3. Giải pháp Tối ưu hóa tốt nhất (Optimal Strategies)
|
||||
Để tối ưu hóa hiệu suất và chất lượng, chúng ta áp dụng các giải pháp sau:
|
||||
* **Đồng bộ hóa tham số (Unified Parameters)**: Cả Client và Server đều đọc chung các tham số hiệu ứng từ tệp JSON cấu hình dự án (ví dụ: `chorus_rate = 1.5 Hz`, `reverb_room_size = 0.5`).
|
||||
* **Đồng hóa thuật toán (DSP Matching)**:
|
||||
* *Chorus*: Cài đặt LFO sine điều tần thời gian trễ đồng bộ ở cả 2 bên.
|
||||
* *Reverb*:
|
||||
* **Client**: Sử dụng `ConvolverNode` nạp bộ đệm xung tự sinh (synthetic impulse response) dài 2 giây từ thuật toán nhiễu trắng phân rã lũy thừa.
|
||||
* **Server**: Sử dụng `pedalboard.Reverb` hoặc thuật toán chập tín hiệu phân rã tương đương trong Python.
|
||||
* **Tối ưu hóa tài nguyên phía Server**:
|
||||
* Không nạp lại SoundFont hoặc thư viện nhiều lần; sử dụng cơ chế singleton hoặc caching cho các module xử lý hiệu ứng của Python.
|
||||
|
||||
---
|
||||
|
||||
## Đề xuất Thay đổi cụ thể
|
||||
|
||||
### 1. Kiến trúc hiệu ứng Track FX (Chorus & Reverb)
|
||||
|
||||
#### Phía Client (Web Audio API)
|
||||
Chúng ta sẽ bổ sung chuỗi liên kết hiệu ứng âm thanh trực tiếp vào nút nguồn của từng track trong [app.jsx](file:///home/locpham/SonicForgeStudio/app/static/js/app.jsx). Bất cứ nguồn âm thanh nào đi qua track (cả clip tiếng động lẫn âm thanh MIDI qua SoundFont) đều chịu tác dụng của hiệu ứng:
|
||||
- **Chorus**: Sử dụng một dry gain và một wet gain kết nối tới LFO-modulated delay node. LFO dao động hình sin tần số `1.5 Hz` với độ lệch delay `2 ms` tạo tiếng đồng ca.
|
||||
- **Reverb**: Sử dụng dry gain, wet gain và `ConvolverNode` chứa bộ đệm phản hồi âm thanh nhân tạo tự sinh dài 2 giây từ thuật toán nhiễu trắng phân rã mũ.
|
||||
- **Giao diện Chọn hiệu ứng**: Khi người dùng nhấn nút **FX: None** trên Track Header, một popup selector sẽ hiển thị để người dùng chọn: **None / Chorus / Reverb**, cập nhật thuộc tính `track.fxType`.
|
||||
|
||||
#### Phía Server (Python Rendering)
|
||||
Khi kết xuất dự án trong [render_engine.py](file:///home/locpham/SonicForgeStudio/app/core/render_engine.py), chúng ta đọc thuộc tính `fx_type` từ track:
|
||||
- Nếu `track.fx_type == 'chorus'`: Sử dụng lớp `Chorus` của `pedalboard` (nếu có thư viện), hoặc dùng `scipy.signal` để tạo dịch pha điều tần chậm.
|
||||
- Nếu `track.fx_type == 'reverb'`: Sử dụng lớp `Reverb` của `pedalboard`, hoặc sử dụng chập phản hồi âm để tạo vang.
|
||||
|
||||
---
|
||||
|
||||
### 2. Vẽ lại trực quan Section item (Waveform nền & Note màu)
|
||||
|
||||
Cập nhật thuật toán vẽ Section item trên Canvas Timeline trong [app.jsx](file:///home/locpham/SonicForgeStudio/app/static/js/app.jsx):
|
||||
- **Phông nền Waveform**: Duyệt qua tối đa 4 track con trong `sec.tracks`. Nếu track con chứa audio clip có `buffer` dữ liệu, lấy mẫu thu gọn 100 peaks và vẽ thành đồ thị cột phân rã đối xứng màu xanh cyan làm nền bên trong phân vùng của Section.
|
||||
- **Vẽ note MIDI theo màu sắc**: Quét các MIDI item trong các track con. Vẽ các note nhạc đè lên waveform với màu sắc tương trưng cho thứ tự track con (ví dụ: track 1 dùng màu vàng hổ phách `#fbbf24`, track 2 dùng màu tím `#a78bfa`, v.v.), giúp người dùng nhận diện nhanh cấu trúc hòa âm.
|
||||
- **Phát Section Item trên Main Session**: Đảm bảo thuật toán lập lịch phát âm thanh trong `startTrackPlayback` quét qua toàn bộ clip và nốt nhạc con của Section item để lên lịch phát nhạc đồng bộ khi playhead quét qua.
|
||||
|
||||
---
|
||||
|
||||
### 3. Phím tắt Nâng cao trong Piano Roll
|
||||
|
||||
Chúng ta sẽ chỉnh sửa các hàm xử lý sự kiện chuột của canvas Piano Roll trong [app.jsx](file:///home/locpham/SonicForgeStudio/app/static/js/app.jsx):
|
||||
|
||||
#### Giải phóng nốt cũ khi chọn nốt mới (Sửa lỗi Drag & Release)
|
||||
- Trong `handleGridMouseDown`, khi người dùng click vào một nốt mới nằm ngoài vùng chọn hiện tại, chúng ta sẽ cập nhật danh sách chọn lựa bằng một biến cục bộ đồng bộ `nextSelectedIds = [clickedNote.id]` thay vì sử dụng state bất đồng bộ `selectedNoteIds`. Điều này đảm bảo danh sách nốt bị dịch chuyển (`selectedNotesOffset`) chỉ chứa duy nhất nốt mới click, giải phóng hoàn toàn nốt cũ.
|
||||
|
||||
#### Sao chép nốt nhanh (Ctrl + Click + Drag)
|
||||
- Trong `handleGridMouseDown`, nếu nhấn chuột trái đồng thời đè phím `Ctrl` trên một nốt nhạc:
|
||||
- Tạo các bản sao nhân bản (cloned copies) của tất cả các nốt đang được chọn với ID ngẫu nhiên mới.
|
||||
- Thêm các nốt nhân bản này vào danh sách `notes` của tab.
|
||||
- Chuyển trạng thái `draggedNote` sang chế độ di chuyển (`move`) áp dụng trực tiếp lên các bản sao mới này, giữ nguyên các nốt gốc ở vị trí cũ.
|
||||
|
||||
#### Giãn/Thu tỷ lệ thời gian các nốt (Alt + Drag Resize)
|
||||
- Khi người dùng nhấn giữ phím `Alt` và kéo cạnh phải (resize edge) của một nốt trong nhóm đang được chọn:
|
||||
- Xác định thời điểm bắt đầu của nốt đầu tiên trong nhóm tuyển chọn (`firstStartBeat`) và điểm kết thúc ban đầu của nốt bị kéo (`originalDraggedEndBeat`).
|
||||
- Tính toán tỷ lệ co giãn thời gian:
|
||||
$$\text{scaleFactor} = \frac{\text{newDraggedEndBeat} - \text{firstStartBeat}}{\text{originalDraggedEndBeat} - \text{firstStartBeat}}$$
|
||||
- Cập nhật thời điểm bắt đầu (`start_beat`) và thời lượng (`duration_beats`) của tất cả các nốt nhạc được chọn có thời điểm bắt đầu nhỏ hơn hoặc bằng điểm kết thúc ban đầu của nốt bị kéo bằng cách nhân với `scaleFactor`.
|
||||
- Các nốt nhạc nằm sau vị trí kéo (ví dụ nốt thứ 4) sẽ được giữ nguyên không đổi.
|
||||
|
||||
#### Vẽ nhiều nốt bằng cách di chuột (Brush/Drag to Draw)
|
||||
- Khi đang kéo vẽ nốt mới, lưu vết mảng các cao độ (pitch/row) đã đi qua trong `draggedNote.visitedPitches`.
|
||||
- Khi di chuyển chuột qua cao độ mới, thêm cao độ đó vào danh sách và phân bổ đều tổng khoảng cách kéo (`beat - startOffsetBeat`) thành các nốt nhạc nối tiếp nhau, mỗi nốt có thời lượng bằng `totalSpan / visitedPitches.length`.
|
||||
|
||||
---
|
||||
|
||||
## Kế hoạch Kiểm thử
|
||||
|
||||
### 1. Kiểm thử hiệu ứng âm thanh FX
|
||||
- **Thời gian thực (Client)**: Kích hoạt Chorus/Reverb trên track, bấm Play nghe thử để xác nhận tiếng vang/tiếng đồng ca chạy mượt mà.
|
||||
- **Kết xuất (Backend)**: Mixdown dự án có track bật Chorus hoặc Reverb, kiểm tra file wav đầu ra xem hiệu ứng có được áp dụng chuẩn.
|
||||
|
||||
### 2. Kiểm thử vẽ trực quan & Phát nhạc Section
|
||||
- Thêm nốt nhạc và audio clip vào Section, nhấn lưu. Kiểm tra xem Section item hiển thị đúng dạng sóng và nốt màu.
|
||||
- Phát nhạc trên Main Session, kiểm tra xem khi playhead đi qua Section item thì âm thanh của Section có phát ra đúng nhịp.
|
||||
|
||||
### 3. Kiểm thử phím tắt Piano Roll
|
||||
- Kiểm tra click chọn nốt mới để di chuyển xem nốt cũ có được giải phóng hoàn toàn và không di chuyển theo.
|
||||
- Giữ `Ctrl` kéo nốt để sao chép.
|
||||
- Giữ `Alt` kéo giãn nhóm nốt.
|
||||
- Di chuột chéo để vẽ chuỗi nốt bậc thang (Brush tool).
|
||||
Reference in New Issue
Block a user