Phase 5: Virtual MIDI Keyboard (F2/menu Tools) — QWERTY mapping spec §3, note routing theo attachMidiHandler (bridge/native SF-VST/SonicSF/Carla), live record forward, drag+persist localStorage, verify Playwright + 3 regression PASS
This commit is contained in:
+13
-2
@@ -71,10 +71,21 @@
|
||||
|
||||
|
||||
## PHASE 4 — NÚT FX TCP
|
||||
(chưa bắt đầu)
|
||||
- [x] Nút "FX" trên Track Control Panel (TCP) mỗi track: `onClick → window.__openFxRack(track.id, track.name)` → `setFxRackTarget` → mở `FXRackModal`; sáng cyan khi track có `fxChain` (builtin/VST FX).
|
||||
- [x] `window.__openFxRack = (trackId, trackName) => setFxRackTarget({ trackId, trackName });`
|
||||
- [x] Fix lookup track trong modal: `(activeTracksRef.current || []).find(t => t.id === fxRackTarget.trackId) || 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ở).
|
||||
- [x] Gộp chung vào commit Phase 3 `5fc45a5`.
|
||||
|
||||
## PHASE 5 — VIRTUAL MIDI KEYBOARD
|
||||
(chưa bắt đầu)
|
||||
- [x] Component `VirtualMidiKeyboard` (top-level, trước `const App`): floating draggable QWERTY keybed — drag header (mousemove/mouseup document), Oct -/+ (0..7), Vel slider 1..127 (default 100), Channel select 1..16 (default = channel track arm/select đầu tiên + 1; chỉ hiển thị, routing vẫn per-track alloc — ponytail), Transpose -24..24, Scale dropdown (None/Major/Minor/Pentatonic Major/Pentatonic Minor/Dorian/Mixolydian — highlight ring-amber chỉ hiển thị không chặn), keybed 15 white + 10 black (KEY_W=24 KEY_H=84, BLACK_W=14 BLACK_H=54 zIndex 5, black left=(wi+1)*KEY_W-BLACK_W/2), highlight `activePitches.has(pitch)` (white bg-emerald-500 / black bg-emerald-600), mousedown noteOn / mouseup+mouseleave noteOff, nhãn C keys `C{octave}`, footer hint QWERTY.
|
||||
- [x] Mapping QWERTY (spec §3 — BIG_PHASE_3 §5.2 cũ ghi sai thiếu H): Lower `Z=0 S=1 X=2 D=3 C=4 V=5 G=6 B=7 H=8 N=9 J=10 M=11`, Upper `Q=12 2=13 W=14 3=15 E=16 R=17 5=18 T=19 6=20 Y=21 7=22 U=23 I=24` (I = +2 oct C). Pitch = `(Octave+1)*12 + offset + transpose`, clamp 0..127, vel = velocity/127.
|
||||
- [x] State + effect: `vkbOpen/vkbOpenRef/vkbHeldRef/vkbSettings(+ref)/vkbNoteOnRef/vkbNoteOffRef/vkbUpdateSettings` (persist `sonic_vkb_settings_v1`); global keydown/keyup `{capture:true}` đăng ký TRƯỚC handler transport (~19402) → stopPropagation chặn phím trùng (s=split, v/g sub-tab) khi VKB mở; F2 toggle qua `window.__toggleVkb`; bỏ `e.repeat`; KHÔNG bắt khi focus input/textarea/contentEditable; bỏ qua ctrl/meta/alt; không chặn Space/R.
|
||||
- [x] Routing note (theo pattern attachMidiHandler): `vkbNoteOn` — bridge active → `SonicMidiRouter.pushEvent({cmd:'NOTE_ON', channel: t.id, pitch, velocity (0..1), percussion, live:true})` cho ALL armed tracks (fallback all tracks + toast 1 lần khi không arm); native SF/VST qua `playNativeSfNote/playNativeVstNote(t, pitch, vel, 60000, undefined, 'vkb_'+t.id+'_'+pitch)` (key prefix cho stop); `SonicSF.playNote` fallback; Carla noteOn; VU `triggerMidiVuActivity(t.id, vel*127)`; giữ `heldMidiNotesRef.current[t.id]`; `vkbNoteOff` — pushEvent NOTE_OFF / stopNativeSfNote+stopNativeVstNote / `SonicSF.stopNote` / Carla noteOff, giảm counter.
|
||||
- [x] Live record: forward `rec.handleMIDIMessage({data:[0x90|0x80,pitch,vel]}, null)` tới mọi recorder trong `activeMIDIRecordersRef` (sourceInputId null → không bị filter selectedMidiInputId) — timeline take + piano roll đều ghi.
|
||||
- [x] Menu Tools: item "Virtual MIDI Keyboard" icon 'piano' shortcut 'F2' → `window.__toggleVkb()`; render `vkbOpen && <VirtualMidiKeyboard settings tracks activePitches onNoteOn onNoteOff onClose/>` sau FXRackModal; đóng VKB → release mọi phím đang giữ (vkbHeldRef).
|
||||
- [x] Build: `node build.mjs` OK; copy `app.precompiled.js` + `index.html` sang bundle `src-tauri/target/release/daw_engine/_internal/app/` (frontend-only); bump cache-bust `?v=202608211150` (repo + bundle); md5 bundle khớp (`2fabf1b0...`).
|
||||
- [x] Verify chạy thật (Playwright, login admin/admin123): F2 mở/đóng; bấm Z..M → highlight C4..B4 đúng pitch (60..71), Q=72, Oct+ → C5; highlight tắt khi release; input focus → không bắn note; kéo thả header → persist x/y, reload khôi phục vị trí; record (arm + input MIDI_KEYBOARD:ALL) → R → bấm Z → recorder nhận NOTE_ON [144,60,100] + NOTE_OFF [128,60,0] (đợi ~5s sau REC — headless context.resume chậm, không phải bug forward; cùng hạn chế với hardware MIDI).
|
||||
- [x] 3 regression cũ PASS: `shm_note_energy` energy 0 (app chưa load instrument — đúng); `test_sf2_ring` energy 12.337→0.046 (= baseline Phase 3); `test_vst2_gui` DUNE host + editor VSTGUI, proc alive.
|
||||
|
||||
## PHASE 6 — CHORDS PANEL
|
||||
(chưa bắt đầu)
|
||||
|
||||
Reference in New Issue
Block a user