120 KiB
120 KiB
[2026-07-31 07:03] Task: Fix Lucide icons not rendering on new tracks (TCP + SECTION-TAB)
- Tóm tắt thay đổi:
useEffectgọilucide.createIcons()thiếuactiveTrackstrong dependency array → khi track được tạo (user/AI/MIDI import/clone section), DOM nodes mới códata-lucidenhưng không được chuyển thành SVG → icon ẩn hoặc hiển thị sai. Fix: thêmactiveTracksvào deps để auto-refresh icons sau mỗi lần track list thay đổi. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Không cần build. Fix áp dụng cho tất cả đường dẫn tạo track: addNewTrack, insertTrackBelow, AI cut/composition/rearrange, MIDI import, section clone.
[2026-07-31 07:09] Task: Allow multiple simultaneous solo buttons (Track TCP)
- Tóm tắt thay đổi:
toggleTrackSoloEvaluatetrước đây setsolo: falsecho tất cả track trừ track được click → chỉ cho phép 1 solo. Fix: toggle solo chỉ trên track được click, giữ nguyên trạng thái solo của track khác.soloedTrackId(single track ID) được thay bằnghasAnySolo(boolean).startTrackPlaybackdùngtrack.solotrực tiếp thay vì so sánh vớisoloedTrackId. Loop restart với solo: iterate tất cả soloed tracks thay vì chỉ play 1 track. UI highlight các nút solo dùngtrack.solo. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Bấm solo track A → active, bấm solo track B → cả A và B đều solo. Audio playback chỉ play track có solo = true.
[2026-07-28 07:56] Task: Fix PIANO_ROLL activeTracks + instrument auto-assign trong SECTION-TAB
- Tóm tắt thay đổi: Fix
activeTracksuseMemo: khiactiveTablà PIANO_ROLL sub-tab từ SECTION-TAB (parent_tab_idbắt đầusession_), trả về tracks của section tab thay vì main tracks. Trước đâyactiveTracksluôn trả về main tracks khi activeTab !== session_xxx → dropdown MIDI item rỗng, track name không hiển thị, instrument lookup sai. Fix: checksubTabscho PIANO_ROLL type để resolve đúng parent session tracks. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần restart server + clear browser cache.
[2026-07-28 07:47] Task: Sync MIDI item editing + ghost notes từ MAIN vào SECTION-TAB
- Tóm tắt thay đổi: Fix
handleEditSectionInTabxoámidiItems: []khi clone section tracks. SECTION-TAB giờ giữ nguyên MIDI items từ section data → dropdown MIDI item switcher hoạt động, ghost notes extract từ các track khác trong section, instrument settings được preserve. Piano Roll Tab dùng chung componentPianoRollTabEditornên tất cả tính năng (ghost, session sync, CC lane, export, AI) đều hoạt động trên cả MAIN và SECTION-TAB. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes. Cần lưu section trước (handleSaveSectionTab) để tích luỹ midiItems vào section.tracks, sau đó mở lại section tab để kiểm tra ghost notes + MIDI dropdown.
[2026-07-28 07:13] Task: Instrument selector modal — 2 cột + font 14px
- Tóm tắt thay đổi: Mở rộng modal
max-w-md→max-w-4xl. Font size từtext-[10px]/text-xs→text-sm(14px). Bố cục 2 cột: trái danh sách soundfont, phải instruments của SF đang chọn. Click SF trái → lọc instrument phải. Nút "All Instruments" để xem tất cả. Search filter cả 2 cột. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes. Synth button → modal 2 cột → chọn SF trái → instrument phải.
[2026-07-26 18:05] Task: Fix SpessaSynth CDN 404 + AudioWorklet init
- Tóm tắt thay đổi: Sửa CDN URL từ
@latest/dist/spessasynth_lib.js(404) →@4.3.1/dist/index.js(200). DùngWorkletSynthesizerclass +audioWorklet.addModule(processor.min.js),soundBankManager.addSoundBank(),await isReady. Fallback WorkerSynthesizer nếu AudioWorklet không khả dụng. - Các file ảnh hưởng:
app/templates/index.html,app/static/js/services/soundfontPlayer.js,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): SpessaSynth CDN load OK (200). Cần kiểm tra browser console.
[2026-07-27 16:16] Task: Tăng chiều cao + fix phím mũi tên cho Copilot prompt + fix Babel build
- Tóm tắt thay đổi: Tăng rows từ 2 → 4, thêm resize-y. Sửa ArrowUp/ArrowDown: chỉ kích hoạt lịch sử khi con trỏ ở đầu/cuối text. Fix lỗi dư
}tại line 6795 khiến Babel build failed (pre-existing). Build thành công qua Babel, precompiled.js được sinh ra tự động. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Kiểm tra prompt input rows=4, resize-y, phím mũi tên di chuyển con trỏ trong multi-line text. Chạy
npm run buildđể kiểm tra.
[2026-07-26 20:15] Task: Stabilize client preview (oscillator + FluidSynth server)
- Tóm tắt thay đổi: Kết luận sau debug: SpessaSynth parser (fromArrayBuffer) không extract được presets từ SF2. Client luôn dùng oscillator (ADSR theo program). Server-side FluidSynth render cho âm thanh chính xác khi Export. Xoá debug logs, clean code.
- Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/api/v1/plugins.py,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Preview qua oscillator luôn có âm thanh. Export WAV dùng FluidSynth với SoundFont thật.
[2026-07-26 17:50] Task: Implement SF3 conversion + SpessaSynth client player
- Tóm tắt thay đổi: Python SF2→SF3 converter (ffmpeg Ogg Vorbis), startup auto-convert, download endpoint ưu tiên SF3. Client: IndexedDB cache, soundfontPlayer.js dual-mode (SpessaSynth + oscillator fallback), init + loadSF trong app.jsx.
- Các file ảnh hưởng:
Dockerfile,app/core/soundfont_converter.py(NEW),app/api/v1/plugins.py,app/main.py,app/templates/index.html,app/static/js/services/soundfontStorage.js(NEW),app/static/js/services/soundfontPlayer.js,app/static/js/app.jsx - Ghi chú/Test (nếu có): DSK 11M→1.1MB (90%), SGM 529M→18MB (97%). SpessaSynth load từ CDN. Fallback oscillator nếu offline.
[2026-07-26 17:25] Task: Fix duplicate Synth + MIDI keyboard silent handler
- Tóm tắt thay đổi: Xoá nút Synth nhỏ giữa ARM và Monitor (dư thừa). Sửa MIDI keyboard handler: bỏ try/catch rỗng, thêm fallback tìm track armed trong main session, pass synth_engine context.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): ARM track → MIDI keyboard → playNote với instrument đã chọn. Server render dùng đúng SGM/SoundFont; client preview dùng oscillator emulation (giới hạn browser).
[2026-07-26 17:20] Task: MIDI keyboard plays selected instrument on armed main track
- Tóm tắt thay đổi: Khi ARM track trong MAIN SESSION/SECTION-TAB, nhấn MIDI Keyboard sẽ play note với instrument đã chọn (synth_engine/instrumentProgram). Fallback: nếu không có sub-tab PIANO_ROLL nào đang armed, tìm track đã ARM trong main session và dùng instrument của nó. Audio routing qua track's gainNode nếu có.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): ARM track → chọn SF instrument → bấm MIDI keyboard → nghe âm thanh instrument đã chọn.
[2026-07-26 17:10] Task: Add Drum channel toggle on track panel
- Tóm tắt thay đổi: Thêm button 🥁 trên track panel (giữa Solo và Record) để chuyển track sang drum channel 9. Khi active: set
is_percussion=true,soundfont_bank=128. Thêm 🥁 indicator cho percussion presets (bank=128) trong instrument selector. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Click 🥁 → track chuyển drum mode → render chạy channel 9.
[2026-07-26 17:05] Task: Fix FluidSynth API mismatch (silent fallback)
- Tóm tắt thay đổi: Pyfluidsynth thực tế là low-level CFFI wrapper, không có class
FluidSynth(). Dùng sai API → tất cả SoundFont render fallback về oscillator (silence). Fix: dùngnew_fluid_synth,fluid_synth_sfload,fluid_synth_program_select,fluid_synth_write_s16_stereo. Đồng bộ internal clock bằng cách render silence quawrite_s16_stereothay vì chỉ advance cursor. - Các file ảnh hưởng:
app/core/render_engine.py - Ghi chú/Test (nếu có): DSK BAN-DI (prog=5) và SGM Brass (prog=56) đều có audio riêng biệt. Render peak ~0.04-0.056, spectral profile khác nhau rõ rệt.
[2026-07-26 16:55] Task: Fix SoundFont program selection flow
- Tóm tắt thay đổi: Sửa
_find_sf2_pathcase-insensitive (catalog IDs lowercase vs filename mixed-case), thêmsynth_engine,soundfont_id,instrument_sourcevào serialize/deserialize để project save/load không mất instrument selection. - Các file ảnh hưởng:
app/core/render_engine.py,app/static/js/app.jsx - Ghi chú/Test (nếu có): Verified:
_find_sf2_path('sgm_v2.01')→SGM_v2.01.sf2, render flat fields + synth_engine struct + roundtrip serialization all pass.
[2026-07-26 16:45] Task: Implement VST3/SoundFont engine plan
- Tóm tắt thay đổi: Fix SF2 path resolution (hardcoded → multi-dir scan), add
synth_enginestruct parsing in render & UI, 3-level fallback chain, pass context to client SoundFont player. - Các file ảnh hưởng:
app/core/render_engine.py,app/static/js/app.jsx,app/static/js/services/soundfontPlayer.js,app/static/js/services/aiGateway.js - Ghi chú/Test (nếu có): Verified: catalog API returns 31 condensed instruments, render with flat fields + synth_engine struct + drums (bank=128) + missing SF fallback all succeed.
[2026-07-25 06:57] Task: Fix Piano Roll runtime errors (rollBeats TDZ + handleCCMouseMove)
- Tóm tắt thay đổi: Sửa lỗi
Cannot access 'rollBeats' before initializationbằng cách di chuyển khai báorollBeats/rollBeatsReflên trướctotalBeats. Thêm hàmhandleCCMouseMovebị thiếu và cập nhậthandleCCMouseDownhỗ trợ Ctrl+Click paint velocity. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
cd /home/locpham/SonicForgeStudio && npm run build— build passes.
[2026-07-25 07:25] Task: 4 tính năng mới (Piano Roll zoom fill + Shift+scroll playhead + auto-scroll drag + SoundFont cache)
- Tóm tắt thay đổi: (1) Piano Roll zoom-out không còn màn hình đen — bars fill toàn bộ viewport. (2) Shift+scroll trong Piano Roll di chuyển playhead và play notes MIDI như fast-forward. (3) Khi drag section/MIDI/clip đến cạnh phải timeline, auto-scroll container. (4) Server-side cache FluidSynth instances + PluginManager singleton + list_soundfont_instruments cache.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/core/vst_engine.py - Ghi chú/Test (nếu có):
npm run buildpass.vst_engine.pythêmload_soundfont_cached,release_soundfont,get_plugin_manager,_SF_INSTRUMENTS_CACHE— refcount-based cache.
[2026-07-26 09:36] Task: Piano Roll 4 features (SNAP, velocity selected, auto-scroll, Synth button)
- Tóm tắt thay đổi: (1) SNAP trong MIDI tab — grid snap cho note drawing, selection marquee, loop range. (2) Ctrl+drag velocity — selected notes màu xanh dương, affect only selected; no selection = paint all. (3) Auto-scroll Piano Roll khi brush-drag gần cạnh top/bottom. (4) Synth button trong MIDI tab toolbar — mở instrument selector khi nhấn. Layout fix: thêm
h-fullcho outer div. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass. Fix thiếu 1)tại line 5959 return statement (gây build error pre-existing).
[2026-07-26 10:08] Task: Fix auto-scroll continuous + CC velocity batch + flicker
- Tóm tắt thay đổi: (1) Brush-draw gần cạnh top/bottom giờ dùng
setInterval30ms để cuộn liên tục (trước đây cuộn 1 lần per mousemove). Stop interval khi mouseup hoặc chuột rời khỏi threshold. (2) Ctrl+drag velocity cho selected notes — gộp tất cảsetNotesvào 1 batch call (thay vì 1 call per note), tránh flicker do multi re-render. (3)handleCCMouseDownselectedMode cũng batchsetNotes. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass. Thêm refbrushAutoScrollRefcho setInterval auto-scroll. BatchsetNotestrong cảhandleCCMouseDownvàhandleCCMouseMoveđể tránh flicker.
[2026-07-26 10:12] Task: Fix brush pitch-based scroll + CC velocity per-note paint
- Tóm tắt thay đổi: (1) Brush auto-scroll: chuyển từ scroll theo viewport edge (clientY) sang pitch-based — scroll khi note mới được vẽ ở pitch gần rìa visible area (6 note threshold). Dùng
NoteHeight * 2scroll step, setInterval 30ms. (2) CC velocity selected mode: thay vì paint all selected notes cùng lúc, paint từng note một theo vị trí chuột — tìm note gần cursor beat nhất, chỉ paint note đó nếu đang được selected. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass. ThaylastPaintedtrong selectedMode từ array all indices thành array single-node index.
[2026-07-26 10:20] Task: Fix brush scroll speed + CC snap + non-selected flicker
- Tóm tắt thay đổi: (1) Giảm auto-scroll step từ
NoteHeight * 2xuốngmax(1, floor(NoteHeight * 0.5))— cuộn chậm hơn để draw note đúng pitch. (2) CC lanehandleCCMouseDownvàhandleCCMouseMovedùnggetSnapBeat(beat, snapVal)khi tìm note index — snap đúng grid. (3) Non-selected CC flicker: xóapaintNotehelper, inlinesetNotestrực tiếp trong mousedown, không gọi setNotes riêng lẻ gây re-render dư thừa. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-25 07:25] Task: Fix auto-scroll + maxDuration tab isolation + 1-bar margin
- Tóm tắt thay đổi: (1)
maxDurationdùngactiveTracks+ 4-bar buffer. (2) Cách ly MAIN vs SECTION-TAB. (3) Clip/section/MIDI drag/stretch/resize clamp 1-bar from right. (4) Clip drag + stretched clip dùngupdateActiveTracks. (5) Stretched clip handler thêm auto-scroll. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 07:31] Task: Fix zoom cursor + right edge black + AI Copilot for Piano Roll
- Tóm tắt thay đổi: (1) Tách
drawWidth(content) vàcssWidth(CSS) — canvas coordinate system không còn bị sai khi zoom out. (2) Thêmmin-widthfill viewport, không còn mảng đen phải. (3) Thêm AI Copilot input + handler trong Piano Roll — gõ prompt tạo MIDI notes trực tiếp. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-25 08:08] Task: Fix velocity draw position, shift+scroll play, canvas redraw deps
- Tóm tắt thay đổi: (1) CC lane velocity stem vẽ tại start note thay vì center. (2) Shift+scroll chỉ play note khi playhead vượt qua start_beat. (3) Thêm
viewWidth/viewBeatsvào dependency array grid canvas + CC canvas để auto-redraw khi resize/zoom. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-25 08:31] Task: Fix AI Copilot tab scope + hide bottom panels + context menu Insert + autoscroll
- Tóm tắt thay đổi: (1) AI Copilot context dùng
activeTracksthaytracks— chỉ tác động tab đang active. (2) Bottom panels mặc định ẩn. (3) Right-click track timeline có 4 mục Insert. (4) Thêm auto-scroll vào stretched clip resize. SửainsertSectionAtPlayheaddùngupdateActiveTracks. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 08:36] Task: Fix auto-scroll + maxDuration tab isolation + 1-bar margin
- Tóm tắt thay đổi: (1)
maxDurationdùngactiveTracks+ 4-bar buffer. (2) Cách ly MAIN vs SECTION-TAB. (3) Clip/section/MIDI drag/stretch/resize clamp 1-bar from right. (4) Clip drag + stretched clip dùngupdateActiveTracks. (5) Stretched clip handler thêm auto-scroll. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 08:53] Task: Add timeline left/right margin + dynamic buffer expansion
- Tóm tắt thay đổi: (1) PADDING_LEFT thay đổi từ 2s thành 2-bar margin ở cả WaveformLane và TempoTrackLane. (2) Thêm state
scrollBufferExtra+ auto-expand 4 bars khi auto-scroll gần đến rìa timeline. (3)maxDurationthêmscrollBufferExtra+ 6 bars static buffer. (4) Clip drag/stretch dùngactiveTracks+updateActiveTracksthaytracks/setTracks. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:04] Task: Fix left margin 1-beat, items always visible, bar/timeline sync
- Tóm tắt thay đổi: (1) PADDING_LEFT từ 2-bar xuống 1-beat ở cả WaveformLane và TempoTrackLane. (2) Drag clamp left ≥ 1 beat, right ≤ maxDuration - 1 bar. (3) Bar numbers + grid vẽ lại đồng bộ với scroll+zoom.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:08] Task: Fix Piano Roll CC lane always visible + resizable height
- Tóm tắt thay đổi: (1)
showCCdefaulttrue— CC lane luôn hiển thị. (2) ThêmccHeightstate + resize handle (drag top edge) — cho phép thay đổi độ cao CC lane. (3) Canvas dùngccHeightthay hardcoded 80px. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:18] Task: Fix brush velocity isolation from CC lane
- Tóm tắt thay đổi: Thêm
brushVelocityRefriêng biệt (0.8) — brush không còn bị ảnh hưởng bởi velocity paint từ CC lane. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:20] Task: Auto-follow dragged item in viewport
- Tóm tắt thay đổi: (1) Clip drag: sau mỗi update, nếu item ra khỏi vùng 120px viewport edge, tự scroll để item luôn thấy. (2) Section/MIDI drag: tương tự. (3) Section/MIDI resize: theo dõi edge đang kéo.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:23] Task: Sync bar ruler redraw during drag
- Tóm tắt thay đổi: (1) TempoTrackLane nhận
canvasRedrawCountprop + deps — force redraw bar numbers khi drag. (2) Cả 3 drag handler incrementsetCanvasRedrawCountsau auto-follow scroll. (3) Sub-tab TempoTrackLane thêmviewportWidth+scrollLeft+canvasRedrawCount— đồng bộ ruler với timeline. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 09:30] Task: Fix bar numbers disappear past bar 12 during drag
- Tóm tắt thay đổi: TempoTrackLane đọc
scrollLefttừ DOM (walk parent chain tìm scroll container) thay vì React prop — canvas luôn dùng scroll position thực tế, không lag do async state. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
[2026-07-25 09:58] Task: Fix drag speed + bar numbers visibility + snap section items
- Tóm tắt thay đổi: (1) Xoá auto-follow phải (gây jump), chỉ giữ auto-follow trái 60px — scroll phải nhờ
autoScrollTimelinemượt. (2) ThêmPADDING_RIGHTvàotEndở TempoTrackLane và WaveformLane — bar numbers + grid hiển thị đến sát rìa phải. (3)if (localX < 0 || localX > drawWidth) continue;— skip phần tử ngoài canvas. (4) Buffer 12 bars chomaxDuration. (5) ThêmsnapTimevào section/MIDI item drag. (6) Thêm snap option'4'(1 bar). (7) Đồng bộPADDING_LEFT = barDurationở cả 2 lane. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npx babel app/static/js/app.jsx— compile pass.
[2026-07-25 10:08] Task: Merge tempo bar + timebar, beat-based ruler, snap grid
- Tóm tắt thay đổi: Thay thế seconds-based ruler (0.00s, 1.00s...) + TempoTrackLane bằng TimelineRuler (48px canvas) hiển thị bar:beat grid lines + snap sub-ticks + BPM label. Grid WaveformLane khớp ruler, snapTime() đã dùng grid spacing beat — snap items đúng vị trí ruler.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:35] Task: Restore time duration labels on TimelineRuler + TempoTrackLane
- Tóm tắt thay đổi: Thêm time duration labels (formatTimeSimple: "0.00s", "1.00s",...) vào TimelineRuler và TempoTrackLane. Hiển thị dạng màu cam (#ffb464) ở chân ruler, động điều chỉnh interval (1s/2s/5s/10s...) theo zoom level. Time labels đã bị mất từ commit
ff44fd4khi merge time bar + tempo bar thành TimelineRuler. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:39] Task: Sync TempoTrackLane leadIn + time labels on ruler top
- Tóm tắt thay đổi: Thêm
leadInMarginprop vào TempoTrackLane để đồng bộ column-0 (time=0) với vị trí leadIn của TimelineRuler. Thêm CLIP_BUFFER vào TempoTrackLane. Di chuyển time duration labels trong TimelineRuler từ dưới đáy (y=40) lên phía trên (y=11). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 11:09] Task: Fix TempoTrackLane scroll sync during drag auto-scroll
- Tóm tắt thay đổi: TempoTrackLane/WaveformLane/TimelineRuler hiện đọc scrollLeft trực tiếp từ DOM wrapper (canvases tìm parent scrollable) thay vì React prop. Tránh lệch vị trí khi auto-scroll nhanh trong clip drag do React state batching.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 11:04] Task: Fix PADDING_LEFT alignment + right-side bar rendering
- Tóm tắt thay đổi: Set PADDING_LEFT=0 trong TimelineRuler/WaveformLane/TempoTrackLane để grid column 0 khớp với clip position 0 (trước đây lệch 8px). TempoTrackLane dùng CLIP_BUFFER/zoom cho tEnd thay vì PADDING_LEFT để bar/beat vẽ đủ xa về phía phải khi scroll.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:58] Task: Fix clip/section snap to column 0
- Tóm tắt thay đổi: Xóa
Math.max(beatSec, ...)trong clip drag (10325), section drag (10495), section resize (10576) — thay bằngMath.max(0, ...)để item có thể snap vào column 0. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:54] Task: Fix TempoTrackLane height + clamp selection at column 0
- Tóm tắt thay đổi: Fix TempoTrackLane canvas height = 40px cố định (thay vì parentElement.clientHeight gây tràn). Wrapper
h-10 shrink-0 overflow-hiddenđể không đẩy track lanes lệch TCP. Clamp selLeft/selRight/ruler handlers >= 0 để selection không vào vùng âm. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:44] Task: Zero lead-in, time ruler + tempo track split layout
- Tóm tắt thay đổi: Xóa leadInMargin (về 0) — time 0 tại mép trái, chỉ còn 8px PADDING_LEFT. TimelineRuler đơn giản thành time-only ruler (40px, chỉ hiển thị time labels). Thêm TempoTrackLane ngay dưới (40px, bars/beats/BPM). Selection overlay top từ 48→80px. Đồng bộ WaveformLane PADDING_LEFT=8/zoom.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 10:18] Task: Lead-in margin, bar 0-indexed, grid clip fix
- Tóm tắt thay đổi: Thêm leadInMargin (1 bar) đẩy timeline content sang phải — bar 0, time 0s tại mép margin. Playhead bắt đầu từ đó. Bar numbering 0-indexed (bar 0 thay vì 1). Sửa CLIP_BUFFER từ 200px → max(400, barWidth+200) để bar number không bị che. Đồng bộ leadInMargin vào tStart/tEnd canvas + mouse handlers (ruler, clip drag, section resize).
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 11:32] Task: Redraw canvas on section item duration resize
- Tóm tắt thay đổi: Thêm
setCanvasRedrawCount(n => n + 1)trong resize mousemove handler để WaveformLane redraw khi section/MIDI item duration thay đổi. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 17:22] Task: Piano roll toolbar - transport, ARM/INPUT, AI bar range
- Tóm tắt thay đổi: Thêm transport (play/stop/back/next/record), ARM+INPUT select MIDI, AI bar range (bar x-y) vào piano roll toolbar. handleAISend phát hiện piano roll active → MIDI prompt. Thêm selectedMidiInputId + handleMidiInputSelect.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-25 22:05] Task: Fix piano roll selection, playback, marquee drag/resize/move
- Tóm tắt thay đổi: (1) Fix playback duration — min 16 beats when no notes exist. (2) Selection marquee: green transparent (#22c55e), full-height from top to CC lane. (3) Document-level mousemove/mouseup for marquee drag (không dừng khi ra khỏi grid). (4) Edge resize handles (left/right) cho marquee. (5) Body drag (move) cho marquee. Hiển thị marquee trên cả grid canvas và CC canvas.
- Các file ảnh hưởng:
app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
node --check app/static/js/app.precompiled.js— syntax OK.
[2026-07-25 22:18] Task: Fix marquee mouseleave, loop reschedule, tempo drag
- Tóm tắt thay đổi: (1) Separate onMouseLeave from onMouseUp for grid canvas — preserve marquee when mouse leaves canvas. (2) Add MIDI note rescheduling on piano roll loop restart — 2nd+ loop iteration now plays sound. (3) Tempo bar drag already uses document-level listeners — works across columns.
- Các file ảnh hưởng:
app/static/js/app.precompiled.js
[2026-07-26 10:24] Task: Fix scroll speed match mouse + CC flicker useLayoutEffect
- Tóm tắt thay đổi: (1) Brush auto-scroll: thêm direct scroll target — mỗi mousemove tính pitchPixel, nếu gần rìa visible area (2 note margin) thì scroll trực tiếp tới target để tốc độ cuộn = tốc độ chuột. Giữ interval 16ms fallback cho trường hợp chuột ở rìa viewport. (2) CC flicker: đổi
React.useEffect→React.useLayoutEffectcho CC canvas rendering effect — canvas paint trước browser paint, loại bỏ flash khi state thay đổi. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass. Direct scroll target:container.scrollTop = targettrên mỗi mousemove khi pitch ở gần rìa.
[2026-07-26 10:28] Task: Fix velocity flicker - grid canvas useLayoutEffect
- Tóm tắt thay đổi: Đổi grid canvas effect từ
React.useEffect→React.useLayoutEffect. Nguyên nhân flicker: khi velocity thay đổi, grid canvas (useEffect) vẽ async sau paint → hiển thị velocity fill cũ 1 frame, trong khi CC canvas (useLayoutEffect) đã vẽ sync trước paint → hiển thị stem mới. Sự mismatch giữa 2 canvas tạo flicker. Fix: cả grid + CC canvas đều dùng useLayoutEffect, đồng bộ trước paint. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 10:39] Task: Fix brush shape draw + remove transport + loop repeat sound
- Tóm tắt thay đổi: (1) Brush drawing: thay vì redistribute notes evenly, append từng note mới tại beat position thực tế (
getSnapBeat(beat, snapVal)) — notes theo đúng hình dạng brush stroke. (2) Xóa transport controls (back/play/stop/record/forward) khỏi MIDI tab toolbar — chỉ dùng global transport. (3) Loop repeat: thêmschedulePianoRollMidihelper — reschedule MIDI notes qua SonicSF mỗi khi loop repeat, fix lỗi notes trong vùng chọn không có âm thanh khi play lặp lại. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 10:44] Task: Fix brush drawing smooth - track pitch + beat delta
- Tóm tắt thay đổi: Brush drawing: theo dõi cả
lastDrawBeatvàvisitedPitches. Tạo note mới khi pitch thay đổi HOẶC beat di chuyển >= minBeatStep (25% của grid duration). Dùng rawbeatthay vìgetSnapBeatđể note theo sát vị trí chuột. Khởi tạolastDrawBeat: starttrong draggedNote. Fix lỗi draw bị ngắt quãng do chỉ tạo note khi pitch thay đổi. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 10:47] Task: Fix brush draw only pitch change + CC velocity flicker skip
- Tóm tắt thay đổi: (1) Brush draw: chỉ tạo note mới khi
pitchChanged(pitch chưa visited). Nếu cùng pitch, extendduration_beatscủa note cuối cùng trongbrushIds— không tạo note mới trùng pitch. (2) CC velocity: thêm checkMath.abs(currentVal - val) > 0.001trước mỗisetNotes— skip render khi velocity/pan không thay đổi, loại bỏ flicker cho cả selected + non-selected mode. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass. currentVal tính: velocity = note.velocity || 0.8; pan = (note.pan || 0) / 2 + 0.5.
[2026-07-26 10:49] Task: Fix brush draw per-note duration + noteStartBeats tracking
- Tóm tắt thay đổi: (1) Thêm
noteStartBeatsarray trongdraggedNote— track start beat của từng note. Khi tạo note mới (pitchChanged), fix duration của note trước đó thànhbeat - prevNoteBeat. Khi extend (cùng pitch), chỉ extend note cuối vớibeat - lastNoteBeat(không extend toàn bộ stroke). (2) Khởi tạonoteStartBeats: [start]trong setDraggedNote. Fix lỗi note cuối có duration dài bằng toàn bộ brush stroke. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 10:55] Task: Fix brush draw pitch revisit by comparing lastDrawnPitch
- Tóm tắt thay đổi: Thay
!visited.includes(snappedPitch)bằngsnappedPitch !== lastDrawnPitch. Bug cũ: khi vẽ C→D→C, visited.includes(C) = true → không tạo note mới → note D cũ bị extend. Fix: so sánh với pitch của note cuối cùng, cho phép vẽ tiếp khi pitch thay đổi dù đã visit trước đó. BỏvisitedPitches(không còn dùng). - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 10:58] Task: Fix CC note detection with Y-aware stem matching
- Tóm tắt thay đổi: Thêm
findCCNoteIndex(beat, mouseY, ccH)helper — tìm note trong CC lane dựa trên cả X (beat) và Y (vị trí stem top). Khi nhiều note cùng beat (chord), so sánhmouseYvới stem top của từng note, chọn note có stem gần nhất. Dùng helper này thay thế tất cảnotes.findIndexvà nearest-fallback tronghandleCCMouseDownvàhandleCCMouseMove. Fix lỗi không detect được note khi velocity gần nhau hoặc cùng beat. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:03] Task: Remove MIDI tab loop button + Ctrl+click clear range
- Tóm tắt thay đổi: (1) Xóa nút loop (repeat) khỏi MIDI tab toolbar — dùng global transport loop button thay thế. (2) Ctrl/Meta+Click trên ruler (tempo track lane) xóa loop range: set loopStartBeat/loopEndBeat = null, selectionStart/selectionEnd = null.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:06] Task: Fix MIDI tab real-time instrument change - restart playback
- Tóm tắt thay đổi:
setTrackInstrumentWithProgram: khi đổi instrument, kiểm tra nếu có PIANO_ROLL sub-tab với trackId đang playing → gọistopAllPlayback()+setTimeout50ms đểschedulePianoRollMidi+startSubTabPlaybackvới instrument mới. Fix lỗi MIDI tab không cập nhật realtime khi thay đổi Synth. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:07] Task: Fix instrument change breaks loop playback
- Tóm tắt thay đổi: Khi đổi instrument trong lúc MIDI tab đang play loop, restart code giờ cũng reset
startBufferOffsetRefvà gọirequestAnimationFrame(updatePlayhead)để tiếp tục vòng lặp animation. Fix lỗi loop bị ngắt sau khi change instrument. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:17] Task: Real-time velocity, ruler play, export MIDI button
- Tóm tắt thay đổi: (1) Thêm
onRescheduleMidiprop — khi edit velocity trong CC lane lúc đang play, gọiwindow.SonicSF.stopAll()+schedulePianoRollMidivới velocity mới. (2) Click ruler: setcurrentTime+ nếu đang play thìonStop+setTimeoutonPlayPauseđể play từ vị trí click. (3) Thêm nút Export cạnh Save — inline MIDI writer, xuất.midfile từ notes array. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:27] Task: Fix realtime velocity stale notes + ruler click restart
- Tóm tắt thay đổi: (1) Compute
updatedNotessynchronously trước khi gọisetNotes, pass intoonRescheduleMidi(updatedNotes).schedulePianoRollMidinhận tham sốnotesOverride— dùng notes mới thay vìst.notes(stale). (2) Ruler click: thayonStop()(reset currentTime) bằngstopAllPlayback()trực tiếp +schedulePianoRollMidi+startSubTabPlayback+ RAF từclickTime. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:32] Task: Fix range auto-loop + gain fade-out crackling
- Tóm tắt thay đổi: (1) Xóa
setIsLooping(true)vàisLooping: truekhỏi ruler drag handler — range selection không còn tự động loop, chỉ loop khi nút LOOP global được kích hoạt. (2) Thaywindow.SonicSF.stopAll()abrupt bằng gain fade-out:linearRampToValueAtTime(0.001, +40ms)→ delay 50-60ms →stopAll+ reschedule →linearRampToValueAtTime(volLinear, +15ms). Áp dụng cho cảonRescheduleMidivàsetTrackInstrumentWithProgram. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:35] Task: Fix ruler click playhead - use onStop+onPlayPause props
- Tóm tắt thay đổi: Sửa ruler click handler — xóa calls tới
stopAllPlayback/schedulePianoRollMidi/updatePlayhead(không có trong scope PianoRollTabEditor). Dùng props:setSubTabs({currentTime, isPlaying: false})→onStop()→ timeout 40ms →setSubTabs({currentTime})→onPlayPause(). Playhead di chuyển và play tiếp từ vị trí click. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:40] Task: Fix ruler cursor+range drag + SonicSF smooth stopAll
- Tóm tắt thay đổi: (1) Ruler: cursor default, click trong range → drag move (hand cursor → move), click ngoài range → seek/drag tạo selection mới. (2)
soundfontPlayer.js:activeOscillatorslưu{osc, gain}thay vìosc;stopAllramp gain về 0 trong 8ms trước khiosc.stop(10ms)— loại bỏ crackling khi realtime update. Cũng xóasetIsLooping(true)trong ruler drag cũ. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 11:56] Task: Fix Piano Roll playhead seek + sound cracking
- Tóm tắt thay đổi: (1) Click ruler khi Piano Roll đang play → seek đến vị trí mới và tiếp tục play (không dừng). (2) ADSR envelope trong
soundfontPlayer.jsdùnglinearRampToValueAtTimethaysetValueAtTime/exponentialRampToValueAtTimeđể loại bỏ gain jump gây crackling.stopAllramp gain về 0 trong 20ms trước khi stop oscillator. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/services/soundfontPlayer.js,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 12:20] Task: Fix Piano Roll seek-to-start during playback
- Tóm tắt thay đổi: Thêm hàm
seekPlaybackTo(time)xử lý seek khi đang play cho cả sub-tab và main timeline. Nút "Quay lại đầu" (skip-back) và "Đầu vùng chọn" (step-back) giờ restart playback từ vị trí mới: stop → update refs → reschedule MIDI → restart. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 12:13] Task: Fix Piano Roll loop/stop behavior
- Tóm tắt thay đổi: (1) Sub-tab loop selection chỉ active khi nút Loop (st.isLooping) bật — không còn bị ảnh hưởng bởi global isLoopingSelection. (2) Stop dừng ngay lập tức:
SonicSF.stopAll()set gain 0 và stop oscillator tại ctx.currentTime, không ramp. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/services/soundfontPlayer.js,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass.
[2026-07-26 12:31] Task: VST/SoundFont inspection engine + AI schema
- Summary: New
SoundFontInspector(sf2utils) scans .sf2 files, generates full/condensed catalog. Added/soundfonts/catalogAPI. Updated AI tool schema withsoundfont_id/bank/program. Condensed catalog auto-injected into system prompt. Server render now passes bank/program + MIDI channel routing (drums → ch9). AddedDecentSamplerManager+ Pianobook render path. ClientSonicSF:applyAITrackInstrument,controllerChange,programChange. Catalog cache invalidated on upload. - Files:
requirements.txt,app/core/soundfont_inspector.py(new),app/api/v1/plugins.py,app/core/vst_engine.py,app/core/render_engine.py,app/static/js/services/aiGateway.js,app/static/js/services/api.js,app/static/js/services/soundfontPlayer.js,app/static/js/app.jsx,Dockerfile,.gitignore,vst_plugins/(new),samples/pianobook/(new) - Tests:
python3 -m pytest tests/ -v→ 61 passed, 1 pre-existing failure. Python syntax check OK on all modified files.
[2026-07-26 16:11] Task: Mount daw_assets host dirs into Docker
- Summary: Created
/home/locpham/daw_assets/{vst3,soundfonts,pianobook}on host. Mounted them intoweb,worker, andbeatcontainers at/opt/daw_engine/vst3,/opt/daw_engine/soundfonts,/opt/daw_engine/samples/pianobook. - Files:
docker-compose.yml - Tests: N/A
[2026-07-26 16:21] Task: Implement Synth button with VST3 selection
- Summary: Fixed Synth button to properly select and use system-installed VST3 plugins. Fixed field name mismatch (
instrument_idvsinstrument) between UI and render engine. Track auto-switches to MIDI type when VST assigned. SoundFont preset selection now passessoundfont_bank+soundfont_programto server. Serialize/deserialize includes all instrument fields. - Files:
app/static/js/app.jsx,app/static/js/app.precompiled.js,app/core/render_engine.py - Tests:
pytest- 61 passed, 1 pre-existing failure.
[2026-07-26 20:45] Task: Fix timeline play — schedule noteOn via setTimeout
- Tóm tắt thay đổi: SpessaSynth noteOn(ch, pitch, vel, enableReverb) không nhận exactAudioTime. Timeline truyền startTime vào vị trí thứ 4 → SpessaSynth ép kiểu thành enableReverb=true → tất cả notes play ở T=0. Fix: tính delay = startTime - currentTime, dùng setTimeout để schedule noteOn đúng thời điểm.
- Các file ảnh hưởng: app/static/js/services/soundfontPlayer.js, app/static/js/app.precompiled.js
- Ghi chú/Test (nếu có): MIDI keyboard play ngay. Timeline play các note đúng thời điểm.
[2026-07-26 21:00] Task: Sync loop button + auto-derive loop boundaries
- Tóm tắt thay đổi: Nút Loop trên transport controls giờ đồng bộ với sub-tab đang active. Khi bật loop: tự động tính loopEnd từ MIDI notes, section clips, hoặc audio duration. Gán selectionStart=0, selectionEnd=loopEndTime. Main timeline cũng tự derive loop end từ tracks/items/clips.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Bật Loop → playhead loop từ đầu đến cuối item/clip và ngược lại.
[2026-07-26 21:15] Task: Fix stop + loop for all tab types
- Tóm tắt thay đổi:
stopAllgiờ huỷ tất cảsetTimeoutđã schedule cho SpessaSynth notes (cả noteOn lẫn noteOff), gọiallNotesOff(0-15). Loop hoạt động trên MAIN SESSION (quaisLoopingSelection+selLeft/selRight), SECTION-TAB và PIANO_ROLL (quast.isLooping+st.selectionStart/End). - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/static/js/app.jsx - Ghi chú/Test (nếu có): Stop = dừng ngay. Loop = phát từ đầu đến cuối item/clip rồi lặp lại.
[2026-07-26 21:30] Task: Sticky ruler + tempo lane on track scroll
- Tóm tắt thay đổi: Wrap TimelineRuler và TempoTrackLane trong
sticky top-0 z-30 bg-[#1a1a1a]. Khi cuộn tracks lên, ruler và tempo lane vẫn hiển thị dưới toolbar. Áp dụng cho cả MAIN SESSION và sub-tab views. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Cuộn tracks lên → ruler + tempo lane dính ở top.
[2026-07-26 22:05] Task: Fix multi-track bank loading (no delete, graceful 404)
- Tóm tắt thay đổi: Xoá
deleteSoundBank— SpessaSynth giữ nhiều banks đồng thời. Kiểm trasoundBankListtrước khi tải lại. 404 từ server không làm mất bank đã load trước đó. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có): 3 tracks với 3 SF khác nhau → SpessaSynth giữ cả 3 → noteOn dùng đúng preset từ bank tương ứng.
[2026-07-27 07:55] Task: Fix item duration when BPM changes
- Tóm tắt thay đổi: Thêm
length_barsvào midiItems/sections.useEffecttrên BPM recalcduration = length_bars * currentSecondsPerBar. Fix section hardcoded 4s →4 * secondsPerBar. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Tạo 8 bars item ở BPM 80 → đổi BPM 120 → item vẫn 8 bars.
[2026-07-27 08:20] Task: Fix TempoTrackLane bar spacing on BPM change
- Tóm tắt thay đổi: DOM traversal tìm scrollLeft bị gãy do sticky wrapper (tìm nhầm div có
position: stickyvới scrollLeft=0). Xoá DOM lookup, dùngscrollLeftprop trực tiếp. Fix cho cả TimelineRuler và TempoTrackLane. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Thay đổi BPM → bars trên tempo track lane vẽ đúng khoảng cách.
[2026-07-27 08:25] Task: Fix bar width drift for non-integer BPM
- Tóm tắt thay đổi: Vòng lặp
t += beatDurationtích luỹ sai số dấu phẩy động (đặc biệt BPM lẻ như 77, 81). Chuyển sangt = bn * beatDurationvới bn là số beat nguyên — mỗi bar line tính độc lập, không sai số tích luỹ. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): BPM 77 → tất cả bars đều rộng bằng nhau.
[2026-07-27 08:40] Task: TimelineRuler draws bar markers aligned with TempoTrackLane
- Tóm tắt thay đổi: Thêm bar markers vào TimelineRuler dùng cùng
bn * barDurationvới TempoTrackLane. Số bar hiển thị ở giữa ruler. Time markers vẫn giữ phía dưới. Cùng BPM prop → luôn đồng bộ với tempo track. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Thay đổi BPM → TimeRuler bars + TempoTrackLane bars luôn đồng nhất.
[2026-07-27 08:48] Fix: equal bar widths + selection tempo/time mode
- WaveformLane: Integer beat loop thay vì
t += beatDuration(fix bar width drift) - Selection mode: Nút toggle ♪T (theo tempo - đổi BPM → selection thay đổi) / ⏱T (cố định thời gian)
[2026-07-27 09:25] Feat: eager soundfont scan at startup
- App load: fetch full catalog with all instrument presets
- Merge: gán presets vào từng soundfont trong instrumentSelectorData
- Synth button: hiển thị presets từ cache, không cần API call per-SF
[2026-07-27 09:55] Feat: Synth button shows flat instrument list from all SFs
- openInstrumentSelector: flatten presets from all SFs + soundfont name tag
- Modal: search bar + flat instrument list + soundfont name prefix
- Fix:
2ad2922had extra closing paren in PluginManagerModal (build fix) - Arch: loại bỏ hai-bước, click Synth → chọn instrument trực tiếp
[2026-07-27 11:05] Feat: MIDI keyboard NoteOff sustain + CC/pitch bend realtime
- NoteOff: durationMs=60000, NoteOff gọi stopNote(ch,pitch) → note tắt ngay
- MIDI channel: truyền msg.data[0]&0x0F vào playNote/stopNote
- CC64 sustain/CC1 modulation: forward qua SonicSF.controllerChange
- Pitch Bend: forward qua SonicSF.pitchBend (14-bit)
[2026-07-27 11:20] Fix: All Notes Off (CC123) before MIDI NoteOn
- Nguyên nhân: SoundFonts như saxophone có release envelope dài, noteOff không tắt âm ngay
- Fix: Gửi
controllerChange(ch, 123, 0)= All Notes Off trước mỗi NoteOn từ MIDI keyboard - Kết quả: Note trước tắt ngay khi play note mới, không còn đè âm
[2026-07-27 11:45] Fix: lazyInit in playNote + 3 root cause fixes
- Root cause 1: NoteOn with velocity=0 treated as NoteOff ✅ (cmd === 0x8 || (cmd === 0x9 && vel === 0))
- Root cause 2: Same channel extraction for NoteOn+NoteOff ✅ (msg.data[0] & 0x0F)
- Root cause 3: pitch/channel always integers from msg.data[] ✅
- Added: _lazyInit() called from playNote when _initialized false
- Oscillator fallback: 2000ms instead of 60000ms (quick preview before init)
[2026-07-27 12:05] Task: Fix Tremolo String continuous sustain after MIDI note-off
- Tóm tắt thay đổi: Root cause:
stopNote()used CC 123 (All Notes Off) which merely enters SoundFont's release envelope. Sustained instruments like Tremolo Strings (GM#44) have 2-5s release tails. Fix: replaced CC 123 with CC 120 (All Sound Off) instopNote()andstopAll(). CC 120 setsvoice.isActive = falseimmediately, bypassing the release envelope entirely, so the note stops instantly on key release. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có): Test với MIDI keyboard + Tremolo Strings instrument: note tắt ngay khi release key. CC 120 tác dụng lên toàn bộ channel (kill all voices) — phù hợp với keyboard preview use-case.
[2026-07-27 12:45] Task: Migrate SpessaSynth → FluidSynth WASM engine
- Tóm tắt thay đổi: Thay thế SpessaSynth (JS/AudioWorklet) bằng FluidSynth WASM (C++ compiled via Emscripten). CDN
@enikey87/fluidsynth-emscripten@0.1.1cho dev (localhost), self-host/static/js/vendor/cho production. Giải quyết triệt để stuck notes (Tremolo/Saxophone) nhờ FluidSynth xử lý Gen 44 loop mode chuẩn SF2.04. Thêmfluidsynth-bridge.jsAudioWorklet nhận PCM từ main-thread FluidSynth render loop. Giữ nguyênwindow.SonicSFAPI surface (0 thay đổi ở app.jsx). - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/templates/index.html,app/static/js/services/fluidsynthLoader.js(NEW),app/static/js/worklets/fluidsynth-bridge.js(NEW),app/static/js/vendor/libfluidsynth-2.3.0-sf3.js(NEW),app/static/js/vendor/libfluidsynth-2.3.0-sf3.wasm(NEW) - Ghi chú/Test (nếu có): Server render (pyfluidsynth) + client preview (FluidSynth WASM) = cùng C++ core → 100% audio parity. Cần clear browser cache. Babel 8 syntax error pre-existing (JSX
{ if(...) { } }trong app.jsx), không rebuild được app.precompiled.js.
[2026-07-27 15:59] Fix: _activeNotes array — multi-track ARM cùng pitch
- Tóm tắt thay đổi: Khi 2 track ARM play cùng MIDI pitch,
_activeNotes['0:60']bị overwrite (track2 ghi đè track1).stopNotechỉ stop 1 track. Fix:_activeNotes[key] = [ch1, ch2, ...]array.stopNoteiterate all channels. - Các file ảnh hưởng:
soundfontPlayer.js - Ghi chú/Test (nếu có): ARM 2 track, play note → thả phím → cả 2 track stop.
[2026-07-27 15:47] Fix: Multi-track ARM + activeNotes cho stopNote
- Tóm tắt thay đổi: MIDI handler
find(t=>isArmed)→filter(...)loop all armed tracks. Thêm_activeNotes[ch:pitch]map đểstopNotelookup đúng channel sau khi đổi SoundFont (channel allocation thay đổi). - Các file ảnh hưởng:
soundfontPlayer.js,app.precompiled.js - Ghi chú/Test (nếu có): ARM 2 track cùng SF khác instrument → cả 2 play. Đổi SF → noteOff vẫn tìm đúng voice.
[2026-07-27 16:05] Fix: program_select thay bank_select+program_change (SF preset search order)
- Tóm tắt thay đổi:
bank_select+program_changedùng search order qua tất cả SF đã load. Khi track 1 load SF2, preset lookup trên channel 0 tìm thấy preset của SF2 thay vì SF1 (SF2 là last-loaded → priority). Fix:fluid_synth_program_select(synth, ch, sfHandle, bank, prog)chỉ định rõ SoundFont ID, không phụ thuộc search order. Track 2 giữ nguyên instrument của SF1. - Các file ảnh hưởng:
soundfontPlayer.js - Ghi chú/Test (nếu có): Track 1 SF1→SF2, track 2 SF1 → track 2 không bị ảnh hưởng.
[2026-07-27 12:15] Task: Fix SpessaSynth loop voice not releasing (layer 2)
- Tóm tắt thay đổi: CC 120 vẫn không đủ vì SpessaSynth 4.3.1 AudioWorklet có bug: looped voices trong MIDI message pipeline xử lý CC 120 sai (
processMessage). Fix: thêmnoteOn(ch, pitch, 0)(MIDI noteOff alternate path) +_synthInstance.post({channelNumber:ch, type:"stopAll", data:1})gửi lệnh trực tiếp đến worklet quahandleMessage— bypass hoàn toàn MIDI pipeline. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/templates/index.html - Ghi chú/Test (nếu có): Cần clear cache browser (index.html cache-bust param updated).
[2026-07-27 17:07] Task: MIDI Ghost Notes + Dropdown Item Switcher + Session Sync Mode
- Tóm tắt thay đổi: Thêm ghost notes cho Piano Roll tab: tất cả MIDI items không được chọn thành ghost notes (25% opacity, dashed border). Dropdown thay thế tab title để chuyển nhanh MIDI item đang edit. Hai chế độ xem: Session Sync (ghost visible, bar labels aligned với session) và Isolated (bar 0, no ghost).
- Các file ảnh hưởng:
app/static/js/services/ghostNoteExtractor.js,app/static/js/app.jsx,app/static/js/app.precompiled.js,app/templates/index.html - Ghi chú/Test (nếu có): Kiểm tra dropdown list đúng tất cả MIDI items. Switch item → ghost notes của item cũ hiện ra. Nút 🌐 Session/📋 Isolated chuyển chế độ. 👻 Ghost toggle chỉ hoạt động ở Session mode.
[2026-07-28 07:03] Task: Auto-scan soundfont cho Synth button
- Tóm tắt thay đổi: Thêm
SoundFontAutoScanner— background daemon thread quét thư mục/opt/daw_engine/soundfontsvàapp/storage/soundfontsmỗi 30s. Dùngsf_scan_state.jsonđể track file đã quét (theo size+mtime), chỉ inspect file mới/thay đổi. Catalog dùng scanner cache thay vì regenerate từ đầu mỗi request./soundfonts/catalogAPI dùng scanner catalog. Upload/delete trigger scan tức thì. Thêm.gitignorecho*.dbvàsf_scan_state.json. - Các file ảnh hưởng:
app/core/soundfont_scanner.py(NEW),app/api/v1/plugins.py,app/core/soundfont_inspector.py,app/main.py,.gitignore - Ghi chú/Test (nếu có):
python3 -c "from app.core.soundfont_scanner import SoundFontAutoScanner; s=SoundFontAutoScanner(); print(s.scan_once())"— scan hoạt động. Condensed catalog dùng scanner's full_catalog tránh duplicate scan.
- Tóm tắt thay đổi: Verify 4 rules: (1) MIDI item nhận instrument từ track cha qua
handleSwitchMidiItemcopyinstrumentProgramtừ track → sub-tab; (2) Section item KHÔNG nhận instrument (reset về null khi clone tracks tronghandleEditSectionInTab); (3) Track khác không bị ảnh hưởng dosetTrackInstrumentWithProgramfilter bytrackId; (4) Đổi instrument trong PianoRoll tab → áp dụng ngược lại cho track quaopenInstrumentSelector→setTrackInstrumentWithProgram. Tất cả đều OK, không bug. - Các file ảnh hưởng:
app/static/js/app.jsx(verify lines 4642-4667, 5905, 6763-6807, 8422-8424),app/core/render_engine.py(verify lines 95-115) - Ghi chú/Test (nếu có): No code changes needed. All 4 rules confirmed working.
[2026-07-27 20:37] Task: Fix cross-track instrument interference (missed channel param)
- Tóm tắt thay đổi: Sửa 6 vị trí gọi
SonicSF.playNote()thiếu tham sốchannel+synthEngine, khiến FluidSynth mặc địnhch=0cho mọi track →program_changetrên channel 0 đè lên nhau giữa các track. Fix: thêm channel computation (từtrack.midiChannelhoặc track index) + truyềnsynth_enginevàoplayNotetạistartTrackPlayback, section sub-track MIDI,playMidiPreviewNote, piano roll Alt+scroll preview, note click preview, keybed onMouseDown/onMouseEnter.setTrackInstrumentWithProgramđã filter đúngtrackId— lỗi không nằm ở logic set instrument mà ở playback. - Các file ảnh hưởng:
app/static/js/app.jsx(lines 4846, 5121, 5661-5671, 10117-10124, 10179-10201, 10285-10307) - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: main session play 2 tracks MIDI khác instrument → mỗi track giữ instrument riêng. Piano Roll preview note → không ảnh hưởng track khác.
[2026-07-27 20:44] Task: Apply md/41_INSTRUMENT.md spec to codebase
- Tóm tắt thay đổi: Implement spec: (1) Tạo
pianoRollTabService.jsvớigetParentTrackByItemId,buildActiveScope,getTrackMidiChannel; (2) Thêmparent_track_idvào tất cả MIDI items creation (deserialize, recording, AI, API); (3)handleSwitchMidiItemdùngbuildActiveScopepattern — thêmactive_scopevào sub-tab state; (4) Toolbar Piano Roll hiển thị(Belongs to: Track X)vàSynth (Track X): Instrument; (5) Đồng bộ channel assignment vềtrackIndex % 16(spec §4); (6) Toàn bộplayNotecalls dùngSonicPianoRoll.getTrackMidiChannelthay for-loop channel compute. - Các file ảnh hưởng:
app/static/js/services/pianoRollTabService.js(NEW),app/static/js/app.jsx,app/static/js/app.precompiled.js,app/templates/index.html - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: multi-item piano roll switch item → scope cập nhật đúng. Parent track label xuất hiện trên toolbar.
[2026-07-27 21:25] Task: Fix _playNoteFluid else branch leaking cross-track SF
- Tóm tắt thay đổi: Khi track chưa set instrument,
_playNoteFluidgọiprogram_change(ch, 0). FluidSynth search program 0 trong ALL loaded SoundFonts → nếu chỉ có SoundFont của Track 2 được load, program 0 của SF đó gán cho channel của Track 1. Fix: bỏprogram_change, dùng oscillator fallback trực tiếp. Track không instrument → sine wave, không leak instrument từ track khác. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: Track 2 set instrument, Track 1 không set → Track 1 play sine wave (không phải SF program 0 của Track 2).
[2026-07-27 21:29] Task: Fix multi-track ARM only first track produces sound
- Tóm tắt thay đổi: ONMIDIMESSAGE handler (
app.jsx:6954-6998) dùng raw MIDI hardware channel (msg.data[0] & 0x0F) thay vì dedicated channel của track, vàfind(t => t.isArmed)chỉ lấy track ARM đầu tiên. Fix: dùngfilter(t => t.isArmed)+getTrackMidiChannelcho từng track — MIDI Note On/Off, CC, Pitch Bend đều route tới ALL armed tracks trên dedicated channel của mỗi track. - Các file ảnh hưởng:
app/static/js/app.jsx(lines 6960-7040),app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: ARM cả 2 track, bấm phím MIDI → cả 2 track đều phát ra instrument riêng. NoteOff gửi đúng channel.
[2026-07-27 21:37] Task: Ctrl+drag on ruler for global selection
- Tóm tắt thay đổi:
handleRulerMouseDowncũ khie.ctrlKeychỉ clear selection rồi return. Fix: Ctrl+click trên ruler khởi tạoselectionMode='global', setrulerDragStartRefvàisDraggingRulerRef=true→ document mousemove handler (đã có sẵn) cập nhật selection range, selection overlay (đã render sẵn) hiển thị vùng chọn. - Các file ảnh hưởng:
app/static/js/app.jsx(line 11133-11142),app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: Ctrl+drag trên ruler → selection overlay xuất hiện. Shift+click vẫn extend selection.
[2026-07-27 21:47] Task: Ctrl+drag sweep select + multi-item drag/copy
- Tóm tắt thay đổi: (1) Ctrl+drag trên empty space (track lane) → sweep select (vùng chọn màu amber), mouseup → chọn tất cả items chạm vùng sweep. (2) Selected items highlight (viền vàng dày). (3) Ctrl+drag trên section/MIDI item → duplicate item. (4) Drag trên selected item → move cả nhóm items đã chọn. (5) Ctrl+drag trên selected item → copy cả nhóm.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Cần test: Ctrl+drag empty space → sweep overlay + selected items highlight. Click+drag selected item → cả nhóm move. Ctrl+drag selected item → copy nhóm.
[2026-07-28 08:09] Task: Fix Ctrl+Click+Drag không chọn item trong SECTION-TAB
- Tóm tắt thay đổi: Xoá toggle selection (select/deselect) khỏi Ctrl+Click handler cho section/MIDI items. Trước đây Ctrl+Click vừa chọn item vừa set pending drag → item bị khoá cứng (selected), không thể kéo copy. Fix: Ctrl+Click chỉ set pending drag để copy, không chạm selection state.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Ctrl+Click+Drag section/MIDI item → copy đến vị trí mới, item không bị selected.
[2026-07-28 11:17] Task: AI Copilot Suggestion Chips + Typeahead (spec 44_AI_SUGGEST_PROMT.md)
- Tóm tắt thay đổi: Thêm suggestion chips bar trên Copilot Prompt: context-aware (MIDI selected → amber rearrange chips, no selection → indigo creation chips). Typeahead dropdown với keyword matching khi gõ ≥2 ký tự, Tab để chọn.
getSelectedMidiItemInfohelper. Backend ai_presets.py thêm 4 rearrange presets.promptTemplateManager.jsthêmgetContextualSuggestions(hasSelectedItem). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/services/promptTemplateManager.js,app/api/v1/ai_presets.py,tests/test_ai_presets.py - Ghi chú/Test (nếu có):
npx babelcompile pass.pytest tests/test_ai_presets.py -v— 7/7 pass.
[2026-07-28 11:02] Task: AI Rearrange Scenario Detection (spec 45_SCENARIAO_AI.md)
- Tóm tắt thay đổi: Thêm 9 rearrange scenarios vào
aiGateway.js(arpeggio, harmonies, syncopation, walking bass, jazz swing, synthwave, cinematic, simplify, passing tones) với keyword mapping →detectRearrangeScenario().buildRearrangeMessagetự động inject specific technique rules dựa trên scenario detect được. Thêm 9 rearrange presets vàopromptTemplateManager.jsvới category "Rearrange / Variation". - Các file ảnh hưởng:
app/static/js/services/aiGateway.js,app/static/js/services/promptTemplateManager.js - Ghi chú/Test (nếu có):
node --checkpass.npx babelcompile pass. Preset Manager hiển thị 12 presets (3 cũ + 9 rearrange). Gõ "arpeggio" → AI nhận dạng scenario → inject technique "Subdivide sustained chord notes into 0.25-beat arpeggiated steps".
[2026-07-28 10:52] Task: MIDI Rearrange Feature (spec 41_AIMIDI_FIX.md)
- Tóm tắt thay đổi: Tạo
midiExtractor.js—extractSelectedMIDIContexttrích xuất notes + note names từ selected MIDI item. ThêmREARRANGE_TOOL_SPEC+buildRearrangeMessagetrongaiGateway.js.handleAISendphát hiện selected MIDI item → dùng rearrange flow riêng (source notes → AI →rearrange_midi_melody→ A/B track bên dưới source). HandlerrearrangeMidiMelodytạo track[AI Rearrange] <title>+ MIDI item ngay dưới source track. - Các file ảnh hưởng:
app/static/js/services/midiExtractor.js(NEW),app/static/js/services/aiGateway.js,app/static/js/services/dawCommandDispatcher.js,app/static/js/app.jsx,app/templates/index.html - Ghi chú/Test (nếu có):
node --checkpass.npx babelcompile pass. Cần chọn MIDI item → gõ prompt rearrange → track A/B mới xuất hiện.
[2026-07-28 10:39] Task: AI MIDI Prompt Template & Preset Engine (spec 33_MIDI_AI.md)
- Tóm tắt thay đổi: Tạo
PromptTemplateManagerservice (keyword matching với scoring, CRUD, star/fav toggle). Backendai_presets.pyCRUD router (JSON file storage, auth isolation). RefactorAIPresetModaldùng service + star toggle + backend API. Piano Roll AI wiring. 7 tests cho matching + backend CRUD + auth isolation. - Các file ảnh hưởng:
app/static/js/services/promptTemplateManager.js(NEW),app/api/v1/ai_presets.py(NEW),tests/test_ai_presets.py(NEW),app/main.py,app/static/js/services/api.js,app/static/js/app.jsx,app/templates/index.html - Ghi chú/Test (nếu có):
python3 -m pytest tests/test_ai_presets.py -v— 7 tests pass. Pre-existing 1 test fail (test_api_user_ai_configpermission error, unrelated).
[2026-07-28 10:15] Task: Dropdown CC lane — Velocity, Sustain, Modulation, Pitch Bend, Pan
- Tóm tắt thay đổi: Thay button group (velocity/pan) bằng
<select>dropdown với 5 options. Hiển thị mặc định là Velocity. Button toggle CC lane hiển thị short label (Vel/Sus/Mod/Bend/Pan/CC). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. CC lane dropdown có 5 options, default Velocity.
[2026-07-28 10:10] Task: Ctrl+Click vùng trống không sweep select, chỉ deselect
- Tóm tắt thay đổi: Thay
onSweepSelectStartbằngonClearSelectiontrong Ctrl+Click vùng không có items. Trước đây gọihandleSweepSelectStart→ yellow cursor + mousemove chọn items. Fix:onClearSelection = () => setSelectedItemIds(new Set())— chỉ clear selection, không sweep. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Ctrl+click vùng trống → deselect hết, không yellow cursor.
[2026-07-28 10:06] Task: Ctrl+S trong SECTION-TAB chỉ lưu section, không export SF
- Tóm tắt thay đổi: Di chuyển SECTION-TAB save check từ handler thứ 2 (dead code, shadowed bởi export handler) lên handler đầu tiên. Line 8066: nếu
activeTab.startsWith('session_')→handleSaveSectionTab, else →handleExportSFS. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. SECTION-TAB Ctrl+S → lưu section. MAIN Ctrl+S → export SF.
[2026-07-28 09:58] Task: Ctrl+S lưu section, Ctrl+A chọn tất cả items
- Tóm tắt thay đổi: Thêm
activeTab.startsWith('session_')branch trong Ctrl+S handler → gọihandleSaveSectionTab. Thêm Ctrl+A handler: collect tất cả sections/midiItems/clips từactiveTracksRef.currentvàoselectedItemIds. Hoạt động trên MAIN SESSION và SECTION-TAB. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. SECTION-TAB Ctrl+S → lưu section. Ctrl+A → chọn tất cả items.
[2026-07-28 09:55] Task: Remove Insert Section từ context menu trong SECTION-TAB
- Tóm tắt thay đổi: Thêm
!sessionTabs.some(s => s.id === activeTab)cho "Insert Section" button trong JSX context menu. Section không thể chứa section item. Data-driven menu (line 14905) đã có check này. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Right-click trong SECTION-TAB → không thấy "Insert Section".
[2026-07-28 09:50] Task: Program change ở note time, không phải call time
- Tóm tắt thay đổi: Di chuyển
program_change/program_select/bank_selecttừ synchronous (call time) vào trongdoNote(note time via setTimeout) trong_playNoteFluid. Trước đây tất cả program_changes xảy ra ngay khistartTrackPlaybackchạy → MIDI items processed trước sections → section's program_change override cuối cùng → ALL notes (section + MIDI) play với section instrument. Fix: mỗi note có program_change tại đúng thời điểm của nó. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có): load. TRACK 1: | SECTION ITEM (inst B) | MIDI ITEM (inst A) | → section plays inst B, MIDI item plays inst A.
[2026-07-28 09:30] Task: Thêm _isSectionClone flag — tắt âm thanh mặc định trong SECTION-TAB
- Tóm tắt thay đổi: Thêm
_isSectionClone: truevào section cloned tracks. Line 10283 kiểm tra flag: nếu section clone và không instrument →undefined(silent); nếu MAIN track và không instrument →0(GM Piano). Trước đây section cloned tracks đi qua line 10283 vớiinstrumentProgram: undefined→ default 0 → play GM Piano. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. SECTION-TAB không instrument → im lặng. MAIN track không instrument → GM Piano.
[2026-07-28 09:27] Task: Silent fallback — không nghe âm thanh mặc định
- Tóm tắt thay đổi: Thay oscillator fallback bằng silent return trong
_playNoteFluid(soundfontPlayer.js:395). Khi không có instrument data, không play bất kỳ âm thanh nào (cả oscillator lẫn FluidSynth). - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js - Ghi chú/Test (nếu có): Section sub-track MIDI items không instrument → im lặng. MAIN track MIDI items không instrument → GM Piano (program 0).
[2026-07-28 09:25] Task: Restore program default 0 cho MAIN track MIDI playback
- Tóm tắt thay đổi: Restore line 10283
instrumentProgram !== undefined ? instrumentProgram : 0— MAIN track MIDI items cần program 0 để play (GM Piano) khi không load instrument. Chỉ section sub-track (line 10393) giữsubTrack.instrumentProgram(undefined → oscillator fallback) để tránh load MAIN SoundFont. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. MAIN track MIDI items play GM Piano khi không instrument. Section sub-track MIDI items dùng oscillator fallback.
[2026-07-28 09:21] Task: Revert fix playhead — gây lỗi không play được
- Tóm tắt thay đổi: Revert 2 changes từ
5d31563: (1) xoárequestAnimationFrame(updatePlayhead)thừa tronghandlePlayPause(effect đã xử lý). (2) xoásetCurrentTime(0)tronghandleEditSectionInTab(gây reset currentTime global). Giữ nguyên các fix instrument isolation + program default. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần kiểm tra Play trên MAIN và SECTION-TAB.
[2026-07-28 09:40] Task: Cả main + section MIDI playback không default program 0
- Tóm tắt thay đổi: Thay đổi line 10283 giống line 10393:
instrumentProgram !== undefined ? ... : 0→instrumentProgram. Trước đây main playback cũng default program 0 khiinstrumentProgramundefined →_playNoteFluidgọiprogram_change(ch,0)trên FluidSynth → load program 0 của MAIN SoundFont. Fix: undefined → oscillator fallback. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. MIDI items không instrument → oscillator fallback (không load SoundFont). MIDI items có instrument → FluidSynth play đúng program.
[2026-07-28 09:37] Task: Section sub-track MIDI playback không default program 0
- Tóm tắt thay đổi: Line 10393
subTrack.instrumentProgram !== undefined ? subTrack.instrumentProgram : 0→subTrack.instrumentProgram. Trước đây khiinstrumentProgramundefined (section clone), program default 0 →_playNoteFluidgọiprogram_change(ch, 0)trên FluidSynth → load program 0 của MAIN session's SoundFont. Fix: undefined program →_playNoteFluiddùng oscillator fallback (không load SoundFont). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Play MAIN session → section MIDI items không load SoundFont. User load instrument trong section tab → program set → FluidSynth play đúng.
[2026-07-28 08:49] Task: Revert Synth button ẩn — track có thể chứa cả section + MIDI
- Tóm tắt thay đổi: Revert conditional Synth button (
815a6fa). Track có section item con VÀ midi item con → Synth vẫn hiển thị. Instrument chỉ áp dụng cho MIDI item, không ảnh hưởng section item (được xử lý bởi data isolation fix trước đó: section clone null hết soundfont fields). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Track sections + midiItems → Synth hiển thị, instrument chỉ tác động midiItems.
[2026-07-28 08:36] Task: Synth chỉ hiển thị cho MIDI track, không cho section/audio track
- Tóm tắt thay đổi: Synth button trong TCP giờ conditional: chỉ hiển thị khi track có midiItems (>0) và KHÔNG có sections. FX button giữ nguyên cho mọi track type. Logic đúng: Synth áp dụng cho MIDI item, FX áp dụng cho section/MIDI/audio clip item.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Track có sections → ẩn Synth. Track MIDI → hiện Synth. Track audio → ẩn Synth. FX hiện ở mọi track.
[2026-07-28 08:31] Task: Section track auto-load soundfont 1/prog 0 từ MAIN qua ...t spread
- Tóm tắt thay đổi:
...ttrong fallback branch spreadsoundfont_id,soundfont_bank,soundfont_program,instrument_sourcetừ parent MAIN track. Dù đã nullinstrumentProgramvàsynth_engine, các fieldsoundfont_*vẫn khiến FluidSynth load soundfont khi play. Fix: override tất cả soundfont fields thành null/undefined. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. MAIN loaded SF1/prog5 → mở section tab → section tracks không có SF.
[2026-07-28 08:27] Task: KHÔNG auto-load instrument MAIN vào SECTION-TAB track
- Tóm tắt thay đổi: Revert kế thừa instrument từ parent MAIN track. Section tracks giữ
instrumentId: null, instrumentProgram: undefined, instrumentName: null, synth_engine: undefined. Trước đây kế thừa instrument (f2ac70e) → SECTION-TAB auto-load program 0 của SoundFont từ MAIN. User tự chọn instrument trong SECTION-TAB qua Synth button (đã hỗ trợ quaupdateActiveTracksrouting). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Load instrument MAIN → mở section tab → section tracks KHÔNG có instrument.
[2026-07-28 08:12] Task: Sync instrument load methods từ MAIN vào SECTION-TAB PIANO_ROLL
- Tóm tắt thay đổi: Fix
updateActiveTracksRef— khiactiveTablà PIANO_ROLL sub-tab cóparent_tab_idtrỏ đến SECTION-TAB, routing instrument update vàosetSessionTabsthay vìsetTracks(main). Trước đây đổi instrument trong PIANO_ROLL của SECTION-TAB ghi vào main tracks (vô hiệu). Cùng logic vớiactiveTracksuseMemo fix trước đó. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Mở SECTION-TAB → PIANO_ROLL → Synth → chọn instrument → verify instrument được áp dụng cho section track (không phải main track).
[2026-07-28 18:17] Task: Keyboard shortcuts Ctrl+A select all + Del delete selected items/groups
- Tóm tắt thay đổi: (1) Ctrl+A đã tồn tại để chọn tất cả items trên timeline. (2) Thêm xử lý phím Delete/Backspace cho
selectedItemIds: xóa tất cả section, MIDI item, và audio clip đang được chọn khỏi track, xóa selection, hiện toast thông báo. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Build có lỗi parse pre-existing (Babel 8) không liên quan đến thay đổi này.
[2026-07-29 09:40] Task: Reset to 33cf804 — clean slate for mixer reimplementation
- Tóm tắt thay đổi: Hard reset về commit
33cf804, xóa toàn bộ mixer code (MasterChannelStrip, TrackChannelStrip, MixerConsole, AudioMixerGraphManager, setOutputNode) và MIDI keyboard note sustain. Giữ lại TimelineRuler (tempo bar/timebar/lead-in/0-index). Backup mixer panel code lưu trongmixer_panel_backup_*.patch. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/services/soundfontPlayer.js,app/static/js/services/audioMixerGraphManager.js - Ghi chú/Test (nếu có):
npm run build— build passes. Sẵn sàng cài đặt lại mixer panel display-only.
[2026-07-29 09:46] Task: Install Mixer Panel display-only with F7 toggle
- Tóm tắt thay đổi: Thêm MixerStrip component (display-only, không audio routing). Mixer panel bottom dock với drag handle resize (80-400px). Track strip hiển thị index, M/S buttons, fader slider, dB readout, track name. Master strip với "MAIN OUT" label. F7 toggle + status bar button + View menu item. onUpdateTrack thay đổi mute/solo/volumeDb trực tiếp.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 09:55] Task: Mixer panel — VU meter, fader scaling, track name color
- Tóm tắt thay đổi: Master strip thêm fader placeholder + VU meter. MixerStrip fader container
items-end→items-stretchđể fill chiều cao. Thêm VU meter (volume-dependent color: green/amber/red, height theo volumeDb). Track name dùngtrack.colorthay vì fixed text-zinc-400. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:02] Task: Fix stuck MIDI notes when ARM toggled off mid-note
- Tóm tắt thay đổi: Xóa
if (!st.isArmed) return;trong note-off handler. Note-off giờ stopNote trên ALL tracks thay vì chỉ armed tracks — FluidSynth stopNote trên channel không có note đang play là no-op. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:08] Task: Fix AI Suggestion list changing on item select/deselect
- Tóm tắt thay đổi: Xóa filter
hasSelItem ? ...khỏisuggestions— luôn hiển thị toàn bộ preset list thay vì chỉ rearrange/favorites khi có item được chọn. Tránh tự động thêm/xóa suggestion khi click chọn/bỏ chọn item. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:12] Task: Restore AI suggestion filter by selection + fix stale instance
- Tóm tắt thay đổi: Restore filter: khi chọn MIDI item chỉ hiện Rearrange/favorites, không chọn thì hiện tất cả. Fix gốc: cache PromptTemplateManager qua
aiPromptMgrRefthay vìnewmỗi render — tránh thay đổi array reference gây re-render suggestion list. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:17] Task: Fix filter including wrong presets in AI suggestion list
- Tóm tắt thay đổi: Xóa
|| p.is_favoritekhỏi filter khi có MIDI item selected — chỉ lọc theocategory === 'Rearrange / Variation', tránh preset favorite bị lọt vào list không đúng. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:19] Task: Show all presets always, no filter by selection
- Tóm tắt thay đổi: Luôn hiển thị
promptMgr.presetsđầy đủ, không filter theo selection state — tránh preset đầu tiên nhảy vào list khi bỏ chọn item. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:42] Task: Fix multi-item drag merging all items into target track
- Tóm tắt thay đổi: Khi cross-track drag, chỉ item đang được kéo (
drag.itemId) được chuyển track — các item được chọn khác giữ nguyên track cũ, chỉ cập nhật vị trí ngang (delta). FixisDraggedItemcheck trong multiIds loop. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:48] Task: Fix group drag — all items move, clamp to track 0, auto-add track
- Tóm tắt thay đổi: Restore group drag: tất cả selected items di chuyển cùng nhau. Thêm clamp targetTrackId về track đầu tiên (0). Auto-add track mới khi kéo xuống dưới track cuối cùng.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:50] Task: Fix group drag merging items — keep items on original tracks
- Tóm tắt thay đổi: Xóa toàn bộ cross-track logic (currentTrackMap, crossTracks). MultiIds drag chỉ cập nhật time position (delta) trên từng track — items luôn giữ nguyên track cũ, không gộp.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:55] Task: Fix group drag cross-track — each item keeps relative track offset
- Tóm tắt thay đổi: Khi drag group cross-track, mỗi item tính target track riêng dựa trên
srcIdx + crossOffset. Item trên track 1 kéo xuống 2 track → đến track 3. Item trên track 2 kéo xuống 2 track → đến track 4. Không gộp vào 1 track. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 10:58] Task: Fix group drag cross-track stale track index bug
- Tóm tắt thay đổi: Sửa
trkIdsdùngprev.map(tr => tr.id)(từ live state trong callback) thay vìactiveTracksRef.current. Sửa insert section dùngforloop tìm srcItem đúng thay vìflatMapsai. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:00] Task: Fix group drag — use ORIGINAL trackId from drag start
- Tóm tắt thay đổi: Lưu
trackId: t.idvàomultiIdslúc bắt đầu drag. Dùnginf.trackId(original) để tínhorigIdx + crossOffset— không dùngcurrentTrackMap(live state có thể stale). Item track1 drag n track → track1+n, item track2+n track → track2+n. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:04] Task: Fix single-item cross-track drag
- Tóm tắt thay đổi: Sửa single-item drag path: mỗi track filter
drag.itemIdriêng, chỉ insert vàotargetTrackId. Xóa duplicatesetDraggedSectionItem. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:06] Task: Fix cross-track drag — compute target track from mouse Y
- Tóm tắt thay đổi: Thay
hoveredTrackIdRefbằng tính toán target track từ mouse Y position + track heights (prev[ti].height). Không phụ thuộconMouseEnterevent có thể không fire khi drag. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:07] Task: Auto-add tracks when drag exceeds existing track count
- Tóm tắt thay đổi: Tính
maxTrgtừ tất cả items trong multiIds. Nếuprev.length <= maxTrg, tự động thêm track mới vàoprevtrước khi xử lí. Item track2 cần xuống track3 → auto-add track3. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:10] Task: Fix targetTrackId undefined outside closure
- Tóm tắt thay đổi: Chuyển target track computation ra ngoài
updateActiveTrackscallback đểsetDraggedSectionItemthấytargetTrackId. Compute dùngactiveTracksRefcho track heights. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:12] Task: Fix drag cross-track — use ORIGINAL baseTrackId, never update
- Tóm tắt thay đổi: Dùng
dragBaseTrackIdtừmultiIds[drag.itemId].trackId(không đổi) thay vìdrag.trackId(bị update mỗi mousemove). XóasetDraggedSectionItemupdate trackId — không cần thiết. crossOffset ổn định xuyên suốt drag. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:16] Task: Rewrite drag handler — separate multi/single paths, pre-compute crossOffset
- Tóm tắt thay đổi: Tách multiIds path và single path. Pre-compute crossOffset + outTracks ngoài
updateActiveTracks. outTracks merge vào prev bằng filter id unique. Mỗi item compute targetTid từ origIdx+crossOffset, filter + upsert trên target track. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:27] Task: Fix seed_admin() resetting admin password on every restart
- Tóm tắt thay đổi: Xóa
elsebranch trongseed_admin()— từng kiểm traif not verify_password(default_pwd, ...)và reset password admin vềadmin123+must_change_password=1mỗi lần server restart nếu password đã bị đổi. Giờ chỉ create admin khi chưa tồn tại. - Các file ảnh hưởng:
app/core/auth.py
[2026-07-29 11:34] Task: Fix instrument list not loading on new machine after login
- Tóm tắt thay đổi: Thêm
listPlugins()+getSoundfontCatalog()fetch tronghandleAuthSuccess— re-fetch instrument data sau khi login vì useEffect mount chạy trước khi token được set, fail silent. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:45] Task: AI SUGGEST auto-update after saving preset in AI Preset Manager
- Tóm tắt thay đổi: Thêm
aiPresetVersioncounter, increment khi AIPresetModal đóng. Trong AI panel, so sánh version vớiwindow.__aiPresetVersion, nếu khác thì gọiaiPromptMgrRef.current.loadPresets()để reload từ localStorage. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:53] Task: Synth button shows track dropdown for quick instrument assign
- Tóm tắt thay đổi: Synth button TCP giờ mở dropdown list tất cả tracks thay vì mở modal trực tiếp. Chọn track trong dropdown → modal instrument selector mở cho track đó. Không cần đóng modal để click Synth từng track.
- Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 11:57] Task: Fix Synth button — track selector inside instrument modal
- Tóm tắt thay đổi: Xóa dropdown track riêng (synthTrackDropdownId). Thêm
<select>track list trong modal Select Instrument, giữa title và textbox tìm kiếm. Chọn track → mở instrument cho track đó trong cùng modal. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 12:00] Task: Fix missing aiTypeaheadRef definition
- Tóm tắt thay đổi: Thêm
aiTypeaheadRef = useRef(null)— ref được dùng trong typeahead dropdown nhưng chưa được khai báo, gây ReferenceError. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 12:03] Task: Select Instrument no longer auto-closes on instrument pick
- Tóm tắt thay đổi: Xóa
setInstrumentSelectorTrackId(null)khỏisetTrackInstrumentWithProgram— modal không tự đóng khi chọn instrument. Người dùng đổi track trong dropdown, chọn instrument, modal giữ nguyên. Đóng bằng nút ✕ hoặc click outside. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-29 15:39] Task: Implement full Ctrl-Z/Ctrl-Y undo/redo for MAIN SESSION and SECTION-TAB
- Tóm tắt thay đổi: (1) Tạo
UndoRedoEngineservice (app/static/js/services/undoRedoEngine.js) — global stack hỗ trợ cross-cutting state changes (BPM, playhead, selection, AI prompt, instrument). (2) ExtendhandleUndo/handleRedotrongapp.jsxđể ưu tiênUndoRedoEngine, fallback về track-level undo stack cũ. (3) Áp dụng undo/redo cho: Delete/Create items (section, midi, clip, track), text AI Prompt (native textarea undo stack + engine integration), instrument assignment, playhead position, item drag position, selection changes, BPM/tempo changes. (4) Update undo/redo buttons disabled state + keyboard shortcut indicators. (5) Global Ctrl+Z/Y handler skip INPUT/TEXTAREA focus để text input native undo hoạt động đúng. - Các file ảnh hưởng:
app/static/js/services/undoRedoEngine.js(NEW),app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-30 12:37] Task: Install Master Strip Console in mixer panel
- Tóm tắt thay đổi: (1) Thêm CSS styles cho Master Strip Console (
strip-bg,fader-track,btn-daw,btn-teal-active,fader-slider) vàoapp/templates/index.html. (2) Tạo componentMasterStripConsoletrongapp/static/js/app.jsx— thay thế MASTER strip cũ (fader đơn giản) bằng console theo specmd/47_MASTER_STRIP_CONSOLE.md: dual stereo VU meter canvas với dB scales L/R, vertical fader -60~+12dB, button stack (Mono/M/S/Route/FX/Power/Trim/Info), pan knob, dB readout, RMS footer. (3) Tích hợp test signal engine (Play/Stop test audio vào masterBus.input), FX button mở MasteringModal, Power button toggle mastering active/bypass. (4) VU meter animation loop sử dụngmasterBus.leftAnalyser/rightAnalyserthay vì synthetic data. - Các file ảnh hưởng:
app/templates/index.html,app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes (Babel note: deoptimised styling for file >500KB, không phải lỗi).
[2026-07-30 12:53] Task: Install Master Strip + Track Strip Console in mixer panel
- Tóm tắt thay đổi: (1) Refine
MasterStripConsoletheo specmd/47: top button "MASTERING PANEL", pan knob pointer-drag (L/R/center label), mute/mono buttons toggle.btn-mute-active/.btn-mono-active, VU meter dùng synthetic post-fader simulation khi muted. (2) TạoTrackStripConsoletừ specmd/48_TRACK_STRIP_COMPONENT.md:w-[145px]với top color accent bar, pan rotary dial, peak dB fader rail + LED VU canvas, button stack (M/S/Route/FX/Power/Auto/Phase Ø), record arm row, track name, color footer index. (3) CSS mới:.strip-bg-track,.fader-track-bg,.btn-solo-active,.btn-arm-active,@keyframes pulse-red, cập nhật.btn-daw:active/fader-sliderthumb từ spec. (4) ReplaceMixerStrip→TrackStripConsoletrong mixer panel. - Các file ảnh hưởng:
app/templates/index.html,app/static/js/app.jsx - Ghi chú/Test (nếu có):
npm run build— build passes.
[2026-07-30 21:15] Task: PIANO ROLL draw notes snapped to grid
- Tóm tắt thay đổi: Note
start_beat+duration_beatsđược snap bằnggetSnapBeattrước khi vẽ lên canvas. Áp dụng cho active notes, ghost notes, recording notes. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Note hiển thị thẳng hàng với grid lines theo snap value.
[2026-07-30 21:07] Task: Fix PIANO ROLL snap grid — snap values inverted in 3 places
- Tóm tắt thay đổi: snapVal mapping bị đảo ngược: snap='1' (1 beat) cho q=4.0 (4 beats) → grid vẽ mỗi 4 beat, note snap mỗi 4 beat. Fix:
getSnapBeat,getSnapDuration, grid drawing: snap='1'→1.0, '1/2'→0.5, '1/4'→0.25, '1/8'→0.125, '1/16'→0.0625, '1/32'→0.03125, '4'→4.0. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Piano roll snap=1 → grid mỗi 1 beat, note snap đúng vị trí.
[2026-07-30 21:02] Task: Fix audio clips lost — draw clip rect even without buffer + store duration in deserialize
- Tóm tắt thay đổi: WaveformLane từng
if (!clip.buffer) return;skip toàn bộ clip (kể cả rect/label). Fix: draw rect/label luôn, chỉ skip waveform khi null buffer. Thêm label "(audio chưa được tải)". Deserialize giờ restoredurationtrên clip từduration_bars. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Clip không có buffer vẫn hiện rect + label + duration đúng.
[2026-07-30 20:58] Task: Fix snap default + Power button mastering bypass
- Tóm tắt thay đổi: (1) snapValue mặc định
'1'(thay'free'), snapVal PIANO_ROLL mặc định'1'(thay'1/16'); (2) Power button trong MasterStripConsole: bỏisFxActivelocal state dùng chung sai với FX button, đọcmasteringSettings.masterConnectedtrực tiếp → togglemasterConnected+ setisBypassedtương ứng. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Load project → snap = 1. Mixer → Power button → mastering bật/tắt đúng.
[2026-07-30 20:52] Task: Fix audio clip items lost after reload
- Tóm tắt thay đổi: 3 root causes: (1)
serverFileIdkhông được lưu vào clip/track khi upload audio —insertSoundClipAtCursorgọiuploadToServernhưng chỉ lưu vào globalserverFileIdMap, không sett.serverFileId; (2) Serialize dùngt.serverFileId(null) →audio_file_urlrỗng → Deserialize không lấy được fileId →loadAudioBuffersForTrackskhông load được buffer; (3) WaveformLane canvasclip.buffer.numberOfChannelscrash khi buffer null → clip items không vẽ được. Fix: setserverFileIdtrên clip + track khi upload; serialize/deserializeserver_file_idtrongsource_data; guardif (!clip.buffer) returntrong WaveformLane. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Chèn audio clip → lưu project → reload → clip hiển thị + waveform load lại.
[2026-07-30 20:46] Task: Auto-save 5min + Backup 30min + Backup management UI
- Tóm tắt thay đổi: (1) DB: thêm bảng
project_backups+ migration cộtis_backup/original_id; (2) Backend:POST/POST /cloud/{id}/backup,GET /cloud/{id}/backups,DELETE /cloud/backups/{id},POST /cloud/backups/cleanup— tự động giới hạn retention (mặc định 10, cấu hình 5-20); (3) Frontend API: 4 methods backup; (4) App:setInterval5 phút auto-save + 30 phút backup; (5) ProfileModal: nút Backup (hiện số lượng) mở rộng danh sách, xóa từng bản, thanh cấu hình retention slider 5-20, nút dọn dẹp. - Các file ảnh hưởng:
app/models/user.py,app/api/v1/projects.py,app/static/js/services/api.js,app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Cần test: mở dự án Cloud → đợi 30p → backup tự động tạo; slider 5-20 lưu vào localStorage; nút 🧹 DỌN BACKUP giữ lại đúng số lượng.
[2026-07-30 20:36] Task: Fix project load crash — loadAudioBuffersForTracks scoped inside ProfileModal
- Tóm tắt thay đổi:
loadAudioBuffersForTracksdefined insideProfileModal, butrestoreLastSessionProjectin App component called it → ReferenceError on startup + silent catch → project not restored. Fix: move function to App component, pass as prop to ProfileModal. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Mở lại trang → dự án cũ được khôi phục.
[2026-07-30 20:32] Task: Add "Tạo preset với cấu trúc" button in AI Preset Manager
- Tóm tắt thay đổi: Thêm
handleNewStructured— tạo preset mới với template có cấu trúc (mục: thể loại, bố cục, nhạc cụ, hòa âm, hiệu ứng, MIDI). Button xanh lục "Tạo preset với cấu trúc" bên trái "Quay lại" trong form edit. - Các file ảnh hưởng:
app/static/js/app.jsx - Ghi chú/Test (nếu có): Nhấn "Tạo mới" → form edit hiện button xanh "Tạo preset với cấu trúc" → click → pre-fill template.
[2026-07-30 22:48] Task: Fix audio clip mất âm thanh sau khi mở lại dự án
- Tóm tắt thay đổi: Sửa 3 lỗi khiến clip audio không tải được buffer sau khi save/reload project: (1)
deserializeTracksListthiếu fallback từaudio_file_urlkhiserver_file_idkhông có trong source_data (2)serializeTracksListkhông lưuserver_file_idở track level (3) legacy upgrade function trong Python không copyserver_file_idvào source_data. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js,app/api/v1/projects.py - Ghi chú/Test (nếu có): Mở lại dự án có clip audio → waveform hiển thị + playback có âm thanh.
[2026-07-31 05:42] Task: Cải thiện AI Preset Manager - Category dropdown + Generator modal
- Tóm tắt thay đổi: (1) Phần DANH MỤC trong form tạo preset đổi từ input text sang dropdown
<select>có đầy đủ danh mục từ presets + danh mục đã lưu trong localStorage, cho phép người dùng chọn danh mục có sẵn hoặc nhấn "+ Nhập danh mục mới..." để thêm; danh mục mới được lưu vàomidi_prompt_categoriestrong localStorage để load lần sau. (2) Nút "Tạo preset với cấu trúc" mở modal mới chứa iframe/ai-prompt-generatorphục vụ nội dung HTML từmd/49_AI_PROMPT_GENERATOR.md, cho phép người dùng dùng giao diện đầy đủ để tạo preset; đóng modal sẽ tự động refresh danh sách preset từ localStorage. - Các file ảnh hưởng:
app/main.py,app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Backend route/ai-prompt-generatortrả về HTML từmd/49_AI_PROMPT_GENERATOR.md. Test: mở AI Preset Manager → Tạo mới → dropdown DANH MỤC hiện danh sách → thêm danh mục mới → lưu → reload modal vẫn giữ danh mục mới. Click "Tạo preset với cấu trúc" → mở iframe generator.
[2026-07-31 06:15] Task: Fix preview layout in AI Prompt Generator modal
- Tóm tắt thay đổi: Sửa layout right column của generator page: header thêm
shrink-0để không bị co lại; container preview đổi thànhflex-1 flex flex-col min-h-0thay vìoverflow-y-auto;#promptOutputTextđổi từh-fullsangflex-1+overflow-y-autođể bottom luôn neo với cạnh modal và nội dung scroll trong element. - Các file ảnh hưởng:
md/49_AI_PROMPT_GENERATOR.md - Ghi chú/Test (nếu có): Refresh trang generator → right column fill full height → content scroll bên trong #promptOutputText.
[2026-07-31 06:35] Task: Apply generator-to-preset-manager transfer spec
- Tóm tắt thay đổi: Áp dụng spec từ md/50_APPLY_GENERATOR.md vào md/49_AI_PROMPT_GENERATOR.md: (1) Category input chuyển thành
<select id="presetCategorySelect">; (2) Thêm nút "Tạo preset có cấu trúc" (xanh lá) bên trái action bar; (3) autoFillPresetModal cập nhật logic infer category từ role/keywords; (4) Thêm handlerbtnCreateStructuredPresetthay static number bằng template variable; (5) Save handler dùngpresetCategorySelect.value, dispatchAI_PRESET_SAVEDCustomEvent. - Các file ảnh hưởng:
md/49_AI_PROMPT_GENERATOR.md - Ghi chú/Test (nếu có): Refresh generator → Lưu thành Preset → Category tự chọn dropdown → Tạo preset có cấu trúc → số bars/beats biến thành {{bars}}/{{total_beats}}.
[2026-07-31 06:42] Task: Transfer generator data to parent AI PRESET MANAGER form
- Tóm tắt thay đổi: Thay vì mở modal standalone trong iframe generator, "Lưu thành Preset" giờ postMessage
GENERATOR_PRESET_DATAlên parent window. React AIPresetModal thêmmessagelistener nhận data, auto-populate form (name, category, keywords, bars, bpm, scale, template), set editingPreset='new' và đóng generator modal. Nút "Tạo preset có cấu trúc" trong React form vẫn hoạt động độc lập. - Các file ảnh hưởng:
md/49_AI_PROMPT_GENERATOR.md,app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có):
npm run buildpass. Flow: generator iframe → Lưu thành Preset → form React tự điền → generator modal đóng.
[2026-07-31 07:37] Task: Fix 3 bugs - panText, cloud project load, auto-restore
- Tóm tắt thay đổi:
- Thêm
const [panText, setPanText] = React.useState('center')vào MasterStripConsole để fixUncaught ReferenceError: panText is not defined. - Fix
restoreLastSessionProject: xóa bỏlocalStorage.removeItem('sonic_project_id')trong catch block để không xóa project ID khi API lỗi tạm thời → auto-load trên page reload hoạt động lại. - Thêm
.catch()vàoloadAudioBuffersForTrackstronghandleOpenProjectvàrestoreLastSessionProjectđể tránh unhandled promise rejection gây gián đoạn load project.
- Thêm
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Cần reload page và test Open Project + Mixer Panel sau fix.
[2026-07-31 08:01] Task: Fix MIDI note creation duration & add Ctrl+Shift+Click split
- Tóm tắt thay đổi:
- Thêm
lastNoteDurationRefđể ghi nhớ duration của note vừa tạo → click tạo note mới dùng duration đó thay vì snap duration mặc định. - Ctrl+Shift+Click vào giữa MIDI note → tách note đó thành 2 notes tại vị trí click. Ctrl+Shift+Click vào vùng trống → nhân bản các note đang chọn (giữ hành vi cũ).
- Thêm
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Kiểm tra: 1) tạo note → thay đổi duration → tạo note mới → duration được kế thừa. 2) Ctrl+Shift+Click giữa note → note bị cắt thành 2.
[2026-07-31 08:09] Task: Bump precompiled cache version for piano roll split fix
- Tóm tắt thay đổi: Cập nhật cache-busting parameter
app.precompiled.js?v=202607310809để trình duyệt load bản JS mới nhất có fix Ctrl+Shift+Click split MIDI note. - Các file ảnh hưởng:
app/templates/index.html - Ghi chú/Test (nếu có): Hard reload (Ctrl+F5) để lấy bản JS mới.
[2026-07-31 08:40] Task: Click track name triggers realtime MIDI playback
- Tóm tắt thay đổi: Click vào track name ở cột trái → tự động play ngay MIDI notes của track đó (nếu có piano roll tab). Dùng
schedulePianoRollMidi(prTab, 0)+startSubTabPlayback(prTab, 0)+stopAllPlayback(). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload để load JS mới. Click track có MIDI notes → nghe thấy phát ngay.
[2026-07-31 08:50] Task: Piano roll instrument load + multitrack play select
- Tóm tắt thay đổi:
handleEditMidiInTab: khi mở piano roll tab bằng double-click MIDI item → lưuinstrumentId,synth_enginevào tab, fallbackinstrumentProgramtừsynth_engine.soundfont_program, gọiSonicSF.selectInstrumentđể load nhạc cụ của track.- Đổi
activePlayTrackIdstừ single value sang mảng → click nhiều nút tên track ở cột trái piano roll để active nhiều track cùng lúc play multitrack.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Test: double-click MIDI item có nhạc cụ → play đúng nhạc cụ. Click nhiều track name → play multitrack.
[2026-07-31 09:20] Task: Multitrack play select plays immediately
- Tóm tắt thay đổi: Thêm
handlePianoRollRealtimePlay(trackIds)ở App: khi click nút track name trong piano roll → tính ngay ghost layers cho các track được chọn →stopAllPlayback()+SonicSF.stopAll()→schedulePianoRollMidi(playTab, offset)→ play ngay realtime không cần bấm play. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Click track button → nghe ngay. Click thêm/bớt track → re-schedule lại tức thì.
[2026-07-31 09:32] Task: Drag-drop MIDI file creates new tracks
- Tóm tắt thay đổi: Thêm
handleDropMidiToNewTracks(file)— drop file .mid/.midi vào vùng track container → parse MIDI → tạo track mới cho từng MIDI track và append MIDI item. Container cóonDropxử lý MIDI (hoặc audio → load vào selected track); lane drop cũng route MIDI sang hàm mới. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Kéo file .mid vào vùng track → track mới xuất hiện với MIDI.
[2026-07-31 09:48] Task: Piano roll shows all same-track MIDI items notes as active
- Tóm tắt thay đổi:
ghostNoteExtractor.js: bỏ filter time-overlap cho các MIDI items cùng track (isSameTrack) → tất cả notes của mọi MIDI item trong cùng track đều được gộp vào ghost layer. Render: same-track layers vẽ nổi bật (amber, alpha 0.65 + border) = trạng thái active. Effect sync: same-track layers luôn được đưa vàoghostPlayLayers→ play đủ cả track; cross-track vẫn theoactivePlayTrackIds. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js,app/static/js/services/ghostNoteExtractor.js - Ghi chú/Test (nếu có): Hard reload. Mở MIDI item của track → thấy tất cả notes của các MIDI item cùng track hiển thị amber nổi bật; play → nghe đủ track.
[2026-07-31 09:52] Task: Opened MIDI item notes darker than same-track siblings
- Tóm tắt thay đổi: Render piano roll: notes của MIDI item đang mở (main layer) đậm hơn — fill
rgba(234,179,8,0.5), viền#f59e0b(chọn: xanhrgba(59,130,246,0.5)). Ghost notes của các MIDI items cùng track nhạt hơn (alpha 0.35, fill amber 0.35) → phân biệt item đang active vs tham chiếu. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Mở MIDI item → notes item đó đậm, notes item khác cùng track mờ hơn.
[2026-07-31 09:55] Task: Focused MIDI item notes brighter
- Tóm tắt thay đổi: Notes của MIDI item được focus (đang mở / được chọn / đang play) sáng hơn: fill
rgba(253,224,71,0.6), viền#fde047; khi đang play sáng hơn nữargba(254,240,138,0.75)viền#fef08a; chọn: xanhrgba(96,165,250,0.6). Ghost siblings cùng track vẫn mờ (alpha 0.35). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Mở MIDI item → notes sáng; bấm play → sáng hơn; item khác cùng track mờ hơn.
[2026-07-31 10:02] Task: Per-MIDI-item focus brightness in piano roll
- Tóm tắt thay đổi: Mỗi ghost note có
item_id(ghostNoteExtractor.js). Render piano roll tínhfocusedItemId: selection → item đang mở; đang play → item chứa playhead (st.currentTimetrong khoảngstartTime..startTime+duration). Notes của item được focus vẽ sáng (fill vàng0.6/0.7+ viền#fde047, đang play0.75), notes của các item khác cùng track mờ (alpha 0.3, fill tối#7a6a10). Cross-track giữ ghost 0.25. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js,app/static/js/services/ghostNoteExtractor.js - Ghi chú/Test (nếu có): Hard reload. Mở track 3 MIDI items → play: playhead chạy đến item nào item đó sáng, các item khác mờ.
[2026-07-31 10:05] Task: Click note focuses its MIDI item
- Tóm tắt thay đổi: Thêm state
focusItemIdtrong PianoRollTabEditor. Click vào note chính → focus item đang mở (st.target_id). Click vào ghost note (note của MIDI item khác cùng track) → focus chuyển sang item đó (không vẽ note mới). Reset focus khi đổist.target_id. Ưu tiên focus: đang play → item theo playhead; có note chọn → item đang mở; còn lại →focusItemId(theo click). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Click note của item 2 (ghost) → item 2 sáng, item 1&3 mờ.
[2026-07-31 10:10] Task: All same-track MIDI items selectable to focus
- Tóm tắt thay đổi: Cột trái PIANO ROLL: dưới nút tên track (đang mở) hiển thị các nút con cho từng MIDI item trong track đó. Click nút MIDI item →
setFocusItemId(im.id)→ item đó sáng, các item khác cùng track mờ. Nút item đang focus: vàng đậm; item đang mở: viền vàng; còn lại: xám. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Click nút MIDI item bất kỳ trong track → focus chuyển sang item đó.
[2026-07-31 10:10] Task: Revert left-column MIDI item buttons
- Tóm tắt thay đổi: Bỏ các nút MIDI items con ở cột trái (không cần). Giữ cơ chế hiện có: các MIDI items cùng track hiển thị sáng/ít sáng theo focus; click vào note mờ (ít sáng) của MIDI item khác → focus chuyển sang item đó.
- Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload.
[2026-07-31 10:15] Task: Ctrl+click dim note selects + focuses item
- Tóm tắt thay đổi: Trong handleGridMouseDown (ctrl+click, không bấm vào note chính): hit-test trên same-track ghost notes → nếu trúng note mờ → toggle chọn note đó (thêm vào
selectedNoteIds, vẽ xanh sáng + viền) vàsetFocusItemId(item_id)→ active MIDI item chứa note. Empty space vẫn mở marquee như cũ. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Ctrl+click note mờ của MIDI item khác → note chuyển xanh + item đó sáng lên.
[2026-07-31 10:13] Task: Selected dim note's MIDI item wins focus
- Tóm tắt thay đổi: Fix focus resolution khi có selection: duyệt
selectedNoteIdstừ cuối (note chọn gần nhất) — main note → item đang mở; dim note (same-track) →item_idcủa nó. Ưu tiên: selection → playhead →focusItemId. GHOST NOTE chỉ là note của MIDI items track khác (alpha 0.25). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Ctrl+click note mờ item 2 → item 2 sáng dù item 1 đang mở.
[2026-07-31 10:19] Task: Click wait-to-active note switches to editing that item
- Tóm tắt thay đổi: Click thường vào note mờ (wait-to-active) của MIDI item khác cùng track →
setFocusItemId+handleSwitchMidiItem(item_id)→ item đó trở thành target đang mở (editable), notes load vào editor, các item khác thành mờ. Ctrl+click vẫn giữ select+focus (không switch). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Click note mờ item 2 → item 2 mở ra để chỉnh sửa notes.
[2026-07-31 10:22] Task: Piano roll dropdown lists tracks only
- Tóm tắt thay đổi: Dropdown trên toolbar PIANO ROLL: thay vì liệt kê tất cả MIDI items (
allMidiItems), chỉ liệt kê các track có MIDI items (dedupe theo track id). Chọn track →handleSwitchMidiItemsang MIDI item đầu tiên của track đó.value=st.trackId. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Dropdown chỉ hiện tên các track.
[2026-07-31 10:26] Task: Fix master strip PWR mastering bypass
- Tóm tắt thay đổi: Nút PWR (Bật/Tắt MASTERING PANEL Bypass) ở Master strip: khi bật ON trước đây set
isBypassed: true→toggleMasteringOnMaster(true, true)giữ routing bypass → âm thanh vẫn bypass. Fix: toggle luôn setisBypassed: false+ flipmasterConnected→ bật lên là mastering active (EQ→Imager→Maximizer), tắt là disconnect. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Bật PWR → âm thanh qua mastering (không còn bypass); tắt → bypass/disconnect.
[2026-07-31 10:32] Task: Track VU meter idle when not audible
- Tóm tắt thay đổi: Track VU meter loop (line ~18446): tính
isAudibletheo solo/mute (anySolo ? track.solo : !track.muted) — nếu track không phát ra âm thanh thực (muted hoặc bị solo out) thì gánaudioPeak=0và bỏ quamidiVuActivityRefflash → VU meter đứng yên thay vì animation giả khi solo track khác. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Solo track 1 → track 2 (MIDI) VU không còn animation.
[2026-07-31 10:35] Task: Realtime solo toggle during playback
- Tóm tắt thay đổi:
toggleTrackSoloEvaluate: khi toggle solo trong lúc play → stop rồi restart đúng: main timeline gọistartTrackPlayback(currentTime)(đã đổi sang đọcactiveTracksRef.currentđể nhận solo state mới); piano roll subtab đang play →schedulePianoRollMidi+startSubTabPlayback. Trước đây restart thiếu lệnh start nên solo không áp dụng realtime. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Bấm SOLO track trong lúc play → chỉ track đó còn nghe, track khác dừng ngay.
[2026-07-31 10:44] Task: Drop MIDI on existing track loads into that track
- Tóm tắt thay đổi: Lane onDrop: drop MIDI/audio lên track có sẵn →
loadFileOnTrack(track.id, f)(MIDI mở ở track đó; multi-track MIDI: track đầu vào track drop, còn lại tạo track mới). Drop ở vùng trống container vẫn tạo track mới (handleDropMidiToNewTracks). - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js - Ghi chú/Test (nếu có): Hard reload. Kéo file .mid lên track hiện có → MIDI nạp vào track đó.
[2026-07-31 10:47] Task: Fix MIDI playback crackling
- Tóm tắt thay đổi:
soundfontPlayer.js: (1) skip program change nếu channel đã có đúng instrument (_channels[ch]cache) — trước đây mỗi note đều reload soundfont → glitch/crackle; (2) bỏconsole.logper-note; (3) render loop: worklet gửiCONSUMEDfeedback đểqueueDepthchính xác,QUEUE_TARGET4→8, interval nhanh hơn → hết underrun (silence gap); (4)stopAllchỉ dùngall_notes_off(release) thay vìall_sounds_off(kill tức thì) → hết pop khi stop.fluidsynth-bridge.js: postCONSUMEDsau splice. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/static/js/worklets/fluidsynth-bridge.js - Ghi chú/Test (nếu có): Hard reload. Play MIDI note → nghe mượt, hết lụp bụp.
[2026-07-31 10:50] Task: Fix MIDI crackling v2
- Tóm tắt thay đổi:
soundfontPlayer.js: (1) render loop dùng wall-clock time-based queue accounting thay vì async CONSUMED message (hết race gây underrun); (2)QUEUE_TARGET8→16 (~170ms buffer chống jank main thread); (3) bỏ 400ms release hack →noteoffđúng duration (FluidSynth tự release mượt, hết chồng voice); (4)synth.verbose=0+synth.gain=1.0.fluidsynth-bridge.js: bỏ post CONSUMED không còn dùng. - Các file ảnh hưởng:
app/static/js/services/soundfontPlayer.js,app/static/js/worklets/fluidsynth-bridge.js - Ghi chú/Test (nếu có): Hard reload. Play MIDI với instrument → hết lụp bụp.
[2026-07-31 11:25] Task: Media Explorer panel (F6) + mutual exclusion with Mixer (F7)
- Tóm tắt thay đổi: Tạo
MediaExplorerPanelcomponent theo specmd/51_MEDIA_EXPLORER.md(REAPER-style: toolbar, directory tree + file list, transport/preview với canvas visualizer + metadata + status bar), render ở hàng dưới trên status bar (giống Mixer panel, resize được, lưustudio_media_explorer_height). Gán F6 toggle panel vớipreventDefault+stopPropagationở cả keydown capture của App (MAIN SESSION/SECTION-TAB) vàPianoRollTabEditor. F6/F7 mutual exclusion: bật panel này tự tắt panel kia (window.__toggleMediaExplorerRef/__toggleMixerRef). Bỏ dock placeholdermedia_explorercũ. Thêm button status bar "Media Explorer Panel (F6)". Thêm CSS.me-fader-slider+.file-row-selectedvàoindex.html, bump version precompiled. - Các file ảnh hưởng:
app/static/js/app.jsx,app/static/js/app.precompiled.js,app/templates/index.html - Ghi chú/Test (nếu có):
npm run build+node --checkOK; smoke test jsdom: F6 hiện Media Explorer & tắt Mixer, F7 ngược lại. Hard reload để test thủ công.
[2026-07-31 13:05] Task: My Computer tree truy cập filesystem thật
- Tóm tắt thay đổi: Backend mới
app/api/v1/media.py(+ mount trongapp/main.py):GET /api/v1/media/computerliệt kê ổ đĩa/mount point (Windows drive letters / Linux/proc/mounts),GET /api/v1/media/browse?path=liệt kê thư mục con + file audio/MIDI,GET /api/v1/media/file?path=serve file cục bộ để preview (chặn ext không phải media). FrontendMediaExplorerPanel: node "My Computer" click → fetch roots, expand/collapse tree (2 cấp con), click thư mục → liệt kê file vào list, preview audio local qua/media/file, nút Back/Up/Refresh + address bar hiểufolder === 'computer'; metadata hiển thị Local Audio/Local MIDI. - Các file ảnh hưởng:
app/api/v1/media.py,app/main.py,app/static/js/app.jsx,app/static/js/app.precompiled.js,app/templates/index.html - Ghi chú/Test (nếu có): Test trực tiếp: roots 8 mounts, browse
/home/locpham22 dirs; serve wav → content-type audio/wav; chặn/etc/shadow. Smoke jsdom: click My Computer → drive root hiển thị → click drive → listbeat.wav/loop.mid, address hiện path. Hard reload để test thủ công.