2 Commits

4 changed files with 100 additions and 17 deletions
+39 -6
View File
@@ -14500,6 +14500,11 @@ const App = () => {
if (window.triggerMidiVuActivity) { if (window.triggerMidiVuActivity) {
window.triggerMidiVuActivity(as.trackId, scaledVel); window.triggerMidiVuActivity(as.trackId, scaledVel);
} }
// Gi VU theo trưng đ: tăng counter note đang gi
// (tick gi peak cho ti khi note-off)
try {
heldMidiNotesRef.current[as.trackId] = (heldMidiNotesRef.current[as.trackId] || 0) + 1;
} catch (err) { }
window.SonicSF.playNote(pitch, scaledVel, 60000, undefined, asProg, null, asCh, asSe); window.SonicSF.playNote(pitch, scaledVel, 60000, undefined, asProg, null, asCh, asSe);
}); });
} }
@@ -14512,6 +14517,10 @@ const App = () => {
if (window.triggerMidiVuActivity) { if (window.triggerMidiVuActivity) {
window.triggerMidiVuActivity(at.id, scaledVel); window.triggerMidiVuActivity(at.id, scaledVel);
} }
// Gi VU theo trưng đ: tăng counter note đang gi
try {
heldMidiNotesRef.current[at.id] = (heldMidiNotesRef.current[at.id] || 0) + 1;
} catch (err) { }
window.SonicSF.playNote(pitch, scaledVel, 60000, undefined, atProg, atDest, atCh, atSe); window.SonicSF.playNote(pitch, scaledVel, 60000, undefined, atProg, atDest, atCh, atSe);
}); });
} }
@@ -14535,6 +14544,16 @@ const App = () => {
if (!st.synth_engine && st.midiChannel === undefined) return; if (!st.synth_engine && st.midiChannel === undefined) return;
var stCh = assignTrackMidiChannel(st, stopTracks); var stCh = assignTrackMidiChannel(st, stopTracks);
window.SonicSF.stopNote(stCh, pitch); window.SonicSF.stopNote(stCh, pitch);
// Gim counter note đang gi hết note VU đưc phép decay/tt
// (tick s thy counter = 0 và không còn gi peak na)
try {
if (heldMidiNotesRef.current[st.id] !== undefined) {
heldMidiNotesRef.current[st.id] = Math.max(0, heldMidiNotesRef.current[st.id] - 1);
if (heldMidiNotesRef.current[st.id] === 0) {
delete heldMidiNotesRef.current[st.id];
}
}
} catch (err) { }
}); });
} }
} }
@@ -15494,6 +15513,11 @@ const App = () => {
}, [isPlaying]); }, [isPlaying]);
const midiVuActivityRef = useRef({}); const midiVuActivityRef = useRef({});
// Đếm s note MIDI đang GI per-track (ARM + MIDI keyboard live input).
// VU tick dùng ref này: còn note gi gi peak (không decay) VU animate
// ĐÚNG trưng đ âm thanh (user bug: nhn phím gi âm còn kêu nhưng VU tt
// sau ~0.5s vì decay 0.75/frame). Hết note (note-off) mi decay/tt.
const heldMidiNotesRef = useRef({});
const triggerMidiVuActivity = (trackId, velocity) => { const triggerMidiVuActivity = (trackId, velocity) => {
if (!trackId) return; if (!trackId) return;
const velFactor = typeof velocity === 'number' ? (velocity > 1 ? velocity / 127 : velocity) : 0.8; const velFactor = typeof velocity === 'number' ? (velocity > 1 ? velocity / 127 : velocity) : 0.8;
@@ -20595,6 +20619,9 @@ const App = () => {
// âm cũ user 06:45: tt âm MAIN items khi vào SECTION-TAB VU main // âm cũ user 06:45: tt âm MAIN items khi vào SECTION-TAB VU main
// items phi tt theo, không "din" tiếp). // items phi tt theo, không "din" tiếp).
try { midiVuActivityRef.current = {}; } catch (e) { } try { midiVuActivityRef.current = {}; } catch (e) { }
// Clear luôn counter note đang gi nếu không, sau stop (âm đã dng)
// tick vn thy heldCnt > 0 gi peak VU dính mãi (user bug 08:08).
try { heldMidiNotesRef.current = {}; } catch (e) { }
stopMidiCapture(); stopMidiCapture();
if (window.SonicSF) { if (window.SonicSF) {
try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); } try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); }
@@ -25856,12 +25883,18 @@ STRICT CONSTRAINTS:
// velocity thp (âm SF nh < 0.03) VU không nhy (user bug 07:10). // velocity thp (âm SF nh < 0.03) VU không nhy (user bug 07:10).
let midiPeak = isAudible ? (midiVuActivityRef.current[vuKey] || 0) : 0; let midiPeak = isAudible ? (midiVuActivityRef.current[vuKey] || 0) : 0;
if (midiPeak > 0) { if (midiPeak > 0) {
// Decay 0.75 (~0.5s) cân bng: note đơn/velocity thp hin th rõ // ARM + MIDI keyboard: còn note đang GI gi nguyên peak, KHÔNG
// NHƯNG tt nhanh sau hết note (decay 0.85 ~1s quá lâu user: // decay VU animate đúng trưng đ âm thanh (user bug 08:08: âm còn
// "vn din animation" sau khi âm hết bug 07:15). // kêu nhưng VU tt sau ~0.5s vì decay 0.75/frame). Hết note decay
midiVuActivityRef.current[vuKey] = midiPeak * 0.75; // 0.75 (~0.5s) tt nhanh như trưc.
if (midiVuActivityRef.current[vuKey] < 0.01) { const heldCnt = heldMidiNotesRef.current[vuKey] || 0;
midiVuActivityRef.current[vuKey] = 0; if (heldCnt > 0) {
// Gi nguyên peak trong khi âm đang kêu (không decay)
} else {
midiVuActivityRef.current[vuKey] = midiPeak * 0.75;
if (midiVuActivityRef.current[vuKey] < 0.01) {
midiVuActivityRef.current[vuKey] = 0;
}
} }
} }
// KHÔNG gp sfAudioPeak vào audioPeak: SF là 1 output CHUNG cho mi // KHÔNG gp sfAudioPeak vào audioPeak: SF là 1 output CHUNG cho mi
File diff suppressed because one or more lines are too long
+21 -2
View File
@@ -3,8 +3,27 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>SonicForge Studio - Professional DAW Editor</title> <title>SonicForge Studio - Professional DAW Editor</title>
<script>
// ── Chặn BROWSER ZOOM toàn trang ──
// UI dùng px cố định cho item/button/label — browser zoom (Ctrl+wheel,
// Ctrl+plus/minus/0, pinch) scale TOÀN BỘ làm control phóng to theo.
// Chặn ở capture phase bằng preventDefault() (KHÔNG stopPropagation —
// các vùng zoom chuyên dụng: timeline, piano roll, canvas, EQ vẫn nhận
// event và tự xử lý zoom nội dung của chúng).
document.addEventListener('wheel', function (e) {
if (e.ctrlKey || e.metaKey) e.preventDefault();
}, { capture: true, passive: false });
document.addEventListener('keydown', function (e) {
if ((e.ctrlKey || e.metaKey) && ['+', '-', '=', '_', '0'].indexOf(e.key) !== -1) {
e.preventDefault();
}
}, { capture: true });
// Pinch zoom (Safari/WebKit gesture events)
document.addEventListener('gesturestart', function (e) { e.preventDefault(); }, { passive: false });
document.addEventListener('gesturechange', function (e) { e.preventDefault(); }, { passive: false });
</script>
<link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/favicon.svg">
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script> <script src="https://unpkg.com/lucide@latest"></script>
@@ -24,7 +43,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script> <script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script> <script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script> <script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608081200" defer></script> <script src="/static/js/app.precompiled.js?v=202608081400" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016"> <link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style> <style>
:root { :root {
+18
View File
@@ -1,3 +1,21 @@
### [2026-08-08] FIX: VU meter giữ animation đúng TRƯỜNG ĐỘ âm thanh khi ARM + nhấn/giữ phím MIDI keyboard
- **Tóm tắt thay đổi:** User yêu cầu: bật ARM + nhấn phím MIDI keyboard preview — khi âm CÒN PLAY thì VU meter phải còn animate cho đúng trường độ âm thanh. Trước đây: note-on → `triggerMidiVuActivity` set peak → VU tick decay 0.75/frame → VU tắt sau ~0.5s DÙ âm còn kêu (note dài 60000ms, chỉ tắt khi note-off). Root cause: VU tick decay theo thời gian, không biết note đang giữ. Fix:
1. **`heldMidiNotesRef`** (mới): đếm số note MIDI đang GIỮ per-track (ARM + keyboard live). Note-on → tăng counter (cả nhánh armed sub-tab PIANO_ROLL lẫn armed main track); note-off → giảm, về 0 thì xóa.
2. **VU tick**: `heldCnt = heldMidiNotesRef.current[vuKey]` — còn note giữ (heldCnt > 0) → GIỮ NGUYÊN peak, KHÔNG decay → VU animate suốt trường độ; hết note (note-off) → decay 0.75 (~0.5s) tắt nhanh như cũ (giữ hành vi user bug 07:15 "hết âm → VU tắt ngay").
3. **stopAllPlayback**: clear luôn `heldMidiNotesRef.current = {}` cùng với `midiVuActivityRef` — tránh sau STOP (âm đã dừng) tick vẫn thấy counter > 0 → giữ peak → VU dính mãi.
- **Các file ảnh hưởng:** `app/static/js/app.jsx` (heldMidiNotesRef + note-on/note-off counters + VU tick giữ peak), `app/static/js/app.precompiled.js` (rebuild), `app/templates/index.html` (bump v=202608081400)
- **Ghi chú/Test (nếu có):** BUILD OK 1109174 bytes, node --check OK, pytest 86 passed + 5 skipped. Luồng: nhấn giữ phím → VU giữ peak; nhả phím (note-off) → VU decay ~0.5s rồi tắt; STOP → VU tắt ngay. Keybed click (mousedown duration 500ms) giữ nguyên hành vi cũ (không qua heldMidiNotesRef).
---
### [2026-08-08] FIX: Chặn browser zoom toàn trang — item/button/label giữ nguyên kích thước khi phóng to UI
- **Tóm tắt thay đổi:** User yêu cầu: khi phóng to giao diện, các item/button/label PHẢI GIỮ NGUYÊN kích thước hiện tại — chỉ các vùng flexible co giãn + cho phép user tự kéo resize (resizer TCP/sidebar/media-explorer/track-height ĐÃ có sẵn). Root cause: layout vốn dùng px cố định (không scale theo window), nhưng **browser zoom toàn trang** (Ctrl+wheel, Ctrl+plus/minus/0, pinch gesture) scale TOÀN BỘ UI → control phóng to theo. WebView2 (Tauri) mặc định bật zoom control. Fix trong `app/templates/index.html`:
1. Meta viewport thêm `maximum-scale=1.0, user-scalable=no`.
2. Inline script chặn zoom ở **capture phase** bằng `preventDefault()` — KHÔNG `stopPropagation()` nên các vùng zoom CHUYÊN DỤNG vẫn nhận event và hoạt động bình thường: timeline zoom (Ctrl+wheel, 18999), piano-roll grid zoom (7351), waveform canvas zoom (12189), EQ canvas wheel (10144).
3. Chặn keydown Ctrl+'+'/'-'/'='/'_'/'0' + gesturestart/gesturechange (pinch Safari/WebKit).
- **Các file ảnh hưởng:** `app/templates/index.html` (meta viewport + zoom-guard script inline)
- **Ghi chú/Test (nếu có):** index.html serve OK (uvicorn), script extract + node --check OK. Không đụng app.jsx/app.precompiled.js (index.html no-cache nên không cần bump stamp). Resizer panel giữ nguyên — user vẫn kéo được TCP width (280-600), sidebar (200-600), media-explorer (20-80%), track height (110-300).
---
### [2026-08-08] FIX: Windows runtime — ModuleNotFoundError scipy.stats._ansari_swilk_statistics + ValueError librosa stub (PyInstaller bundle) ### [2026-08-08] FIX: Windows runtime — ModuleNotFoundError scipy.stats._ansari_swilk_statistics + ValueError librosa stub (PyInstaller bundle)
- **Tóm tắt thay đổi:** User chạy bản Windows exe gặp 2 lỗi runtime (hộp thoại error): - **Tóm tắt thay đổi:** User chạy bản Windows exe gặp 2 lỗi runtime (hộp thoại error):
1. `ModuleNotFoundError: No module named 'scipy.stats._ansari_swilk_statistics'` — scipy >= 1.18 tách `scipy.stats` thành nhiều module con import LAZY bên trong hàm (vd `_ansari_swilk_statistics`) → hook scipy của PyInstaller không thấy → thiếu trong bundle. Fix: `engine.spec` thêm `collect_submodules('scipy')` vào hiddenimports (collect toàn bộ stats/signal/ndimage/... — không sót module nào). 1. `ModuleNotFoundError: No module named 'scipy.stats._ansari_swilk_statistics'` — scipy >= 1.18 tách `scipy.stats` thành nhiều module con import LAZY bên trong hàm (vd `_ansari_swilk_statistics`) → hook scipy của PyInstaller không thấy → thiếu trong bundle. Fix: `engine.spec` thêm `collect_submodules('scipy')` vào hiddenimports (collect toàn bộ stats/signal/ndimage/... — không sót module nào).