FIX: sửa lỗi đồng bộ playhead vị trí play giữa MAIN SESSION/SECTION-TAB/PIANO ROLL TAB
This commit is contained in:
+79
-25
@@ -14750,6 +14750,31 @@ const App = () => {
|
||||
const sessionTabsRef = useRef(sessionTabs);
|
||||
sessionTabsRef.current = sessionTabs;
|
||||
|
||||
// Vị trí MIDI item (trong context của piano roll) — dùng để đồng bộ playhead
|
||||
// 2 chiều SECTION-TAB ↔ PIANO ROLL: item.startTime (local section / main) +
|
||||
// st.currentTime (vị trí TRONG item). Dùng REFS (activeTracksRef/
|
||||
// sessionTabsRef) — an toàn với closure cũ (updatePlayhead/rAF loop).
|
||||
const getPianoRollItemInfo = st => {
|
||||
try {
|
||||
if (!st) return null;
|
||||
let list = null;
|
||||
if (st.parent_tab_id && st.parent_tab_id.startsWith('session_')) {
|
||||
const _pst = sessionTabsRef.current.find(s => s.id === st.parent_tab_id);
|
||||
list = _pst ? _pst.tracks : null;
|
||||
} else {
|
||||
list = activeTracksRef.current;
|
||||
}
|
||||
if (!list) return null;
|
||||
const _trk = list.find(t => t.id === st.trackId);
|
||||
if (!_trk) return null;
|
||||
const _item = (_trk.midiItems || []).find(m => m.id === st.target_id);
|
||||
if (!_item) return null;
|
||||
return { startTime: _item.startTime || 0, duration: _item.duration || 4 };
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Track mute/solo/volume signature: only re-apply gains when something that
|
||||
// affects audibility actually changed (loads, undo, AI ops, section tabs).
|
||||
const trackMuteSoloSigRef = useRef({});
|
||||
@@ -14851,28 +14876,41 @@ const App = () => {
|
||||
// trước đó mở tab lúc main đang play (mainResumeRef đã set).
|
||||
const _resume = mainResumeRef.current;
|
||||
mainResumeRef.current = null;
|
||||
if (_resume && prevSub.type === 'PIANO_ROLL' && (activeTab === 'main' || (activeTab && activeTab.startsWith('session_')))) {
|
||||
try {
|
||||
const _elapsed = getAudioContext().currentTime - _resume.audioTime;
|
||||
const _resumeAt = _resume.offset + _elapsed;
|
||||
console.log('[Resume] main play từ', _resumeAt.toFixed(2), 's (offset', _resume.offset.toFixed(2), '+ elapsed', _elapsed.toFixed(2), ')');
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = _resumeAt;
|
||||
startAudioTimeRef.current = getAudioContext().currentTime;
|
||||
startTrackPlayback(_resumeAt);
|
||||
if (prevSub.type === 'PIANO_ROLL' && (activeTab === 'main' || (activeTab && activeTab.startsWith('session_')))) {
|
||||
// Resume section/main play tại vị trí TƯƠNG ỨNG với piano roll:
|
||||
// resumeAt = item.startTime + st.currentTime (vị trí TRONG item —
|
||||
// yêu cầu: play piano roll ở vị trí nào → về SECTION play đúng vị trí
|
||||
// đó). Ưu tiên item info; fallback mainResumeRef (offset+elapsed);
|
||||
// fallback cuối: setIsPlaying(true) — playhead chạy theo âm còn kêu.
|
||||
let _resumeAt = null;
|
||||
const _itemInfo = getPianoRollItemInfo(prevSub);
|
||||
if (_itemInfo) {
|
||||
_resumeAt = _itemInfo.startTime + (prevSub.currentTime || 0);
|
||||
// Về MAIN mà item thuộc SECTION: vị trí main = secStart + local
|
||||
if (activeTab === 'main' && prevSub.parent_tab_id && prevSub.parent_tab_id.startsWith('session_')) {
|
||||
const _pst = sessionTabsRef.current.find(s => s.id === prevSub.parent_tab_id);
|
||||
if (_pst && _pst.sectionId) {
|
||||
(activeTracksRef.current || []).forEach(_t => (_t.sections || []).forEach(_s => {
|
||||
if (_s.sectionId === _pst.sectionId || _s.id === _pst.sectionId) _resumeAt += (_s.start || 0);
|
||||
}));
|
||||
}
|
||||
}
|
||||
} else if (_resume) {
|
||||
_resumeAt = _resume.offset + (getAudioContext().currentTime - _resume.audioTime);
|
||||
}
|
||||
if (_resumeAt !== null) {
|
||||
try {
|
||||
console.log('[Resume] play tại', _resumeAt.toFixed(2), 's (item.startTime=' + (_itemInfo ? _itemInfo.startTime.toFixed(2) : '?') + ' + st.currentTime=' + (prevSub.currentTime || 0).toFixed(2) + ')');
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = _resumeAt;
|
||||
startAudioTimeRef.current = getAudioContext().currentTime;
|
||||
startTrackPlayback(_resumeAt);
|
||||
setIsPlaying(true);
|
||||
} catch (e) { console.warn('[Resume] error:', e); }
|
||||
} else {
|
||||
console.log('[Playhead] tiếp tục theo piano roll (không stop âm) — activeTab=' + activeTab);
|
||||
setIsPlaying(true);
|
||||
} catch (e) { console.warn('[Resume] error:', e); }
|
||||
} else if (prevSub.type === 'PIANO_ROLL' && (activeTab === 'main' || (activeTab && activeTab.startsWith('session_')))) {
|
||||
// Play THUẦN piano roll (main chưa từng play — mainResumeRef null):
|
||||
// rời tab → KHÔNG stop âm (0415) — NHƯNG isPlaying=false → nhánh
|
||||
// main updatePlayhead `if (!isPlaying) return` → playhead NGỪNG dù
|
||||
// âm piano roll còn kêu (user: playhead không đồng bộ/ngừng khi
|
||||
// chuyển tab). → setIsPlaying(true): playhead main/section chạy theo
|
||||
// elapsed (startOffsetTimeRef/startAudioTimeRef đang là của piano
|
||||
// roll play — đồng bộ với âm đang phát). Hết bài (projectEnd) →
|
||||
// nhánh main tự stop.
|
||||
console.log('[Playhead] tiếp tục theo piano roll (không stop âm) — activeTab=' + activeTab);
|
||||
setIsPlaying(true);
|
||||
}
|
||||
}
|
||||
} else if (prevSub && prevSub.type === 'PIANO_ROLL') {
|
||||
// Rời PIANO ROLL tab KHÔNG play: main/section play ngầm vẫn chạy (âm
|
||||
@@ -18495,13 +18533,29 @@ const App = () => {
|
||||
if (!st || !st.buffer) return;
|
||||
if (!st.isPlaying) {
|
||||
// PIANO ROLL / sub-tab KHÔNG play: playhead theo MAIN/SECTION nếu đang
|
||||
// play (đồng bộ vị trí âm THẬT — user: di chuyển tab qua lại → quay
|
||||
// lại piano roll tab → playhead phải chạy theo âm đang phát); không
|
||||
// có play nào → đứng yên (chế độ edit). Loop luôn tiếp tục (rAF).
|
||||
// play — ĐỒNG BỘ 2 CHIỀU: vị trí play (section/main) → vị trí TRONG
|
||||
// item (st.currentTime = playPos - item.startTime); playhead vượt ra
|
||||
// ngoài duration item → bắt đầu từ đầu (0). Không có play nào → đứng
|
||||
// yên (chế độ edit). Loop luôn tiếp tục (rAF).
|
||||
if (isPlaying) {
|
||||
const context = getAudioContext();
|
||||
const _ph = startOffsetTimeRef.current + (context.currentTime - startAudioTimeRef.current);
|
||||
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: _ph } : s));
|
||||
let _local = _ph;
|
||||
const _itemInfo = getPianoRollItemInfo(st);
|
||||
if (_itemInfo) {
|
||||
if (_ph < _itemInfo.startTime) _local = 0;
|
||||
else if (_ph >= _itemInfo.startTime + _itemInfo.duration) _local = 0; // vượt duration item → đầu
|
||||
else _local = _ph - _itemInfo.startTime;
|
||||
}
|
||||
// Log chẩn đoán (rate-limited ~2 lần/giây): xác nhận đồng bộ MAIN/
|
||||
// SECTION ↔ PIANO ROLL — ph (vị trí play) → local (trong item).
|
||||
try {
|
||||
const _sf = (window.__syncFrame = (window.__syncFrame || 0) + 1);
|
||||
if (_sf % 45 === 0) {
|
||||
console.log('[Sync] PR', st.id, 'ph=', _ph.toFixed(2), 'item.start=', _itemInfo ? _itemInfo.startTime.toFixed(2) : '-', 'dur=', _itemInfo ? _itemInfo.duration.toFixed(2) : '-', '→ local=', _local.toFixed(2), 'tab=', activeTabRef.current);
|
||||
}
|
||||
} catch (e) { }
|
||||
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: _local } : s));
|
||||
}
|
||||
animationFrameIdRef.current = requestAnimationFrame(updatePlayhead);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user