fix(fxrt): giu session realtime khi loop/seek/reschedule - het bypass+reload moi pass loop
- fxRtKeepForRestart flag: nhánh restart/resume (loop selection/project-end, sub-tab loop, reschedule items, seek, solo toggle, piano-roll seek, export rewind, unmute, playhead, tab-switch resume, ghost play) set flag truoc stopAllPlayback -> giu session FX (fxRtStart early-return) -> khong reload VST ~4s moi pass loop. - User stop thật (fade=true), bridge-down, new project, recovery rebuild, export end, offline export: khong set flag -> teardown nhu cu. - Rebuild app.precompiled.js tu source (babel) + giu lai IIFE overlay telemetry. - Bump cache-buster 202608242200 -> 202608242300 (5 ban).
This commit is contained in:
+36
-4
@@ -1648,6 +1648,12 @@ function encodeFloatWavBlob(pcm, sampleRate, channels) {
|
||||
// tự offline export). Track: mỗi track 1 session/worklet splice vào CẢ 2 đường
|
||||
// chain (audio-clips + MIDI/SF) — mọi luồng track qua bridge trước main out.
|
||||
const fxRt = { active: false, starting: false, startToken: 0, sessionId: null, ws: null, node: null, inQueue: [], pumpTimer: null, lastInSent: 0, chainKey: null, latencyTotal: 0, fadedIn: false };
|
||||
// Loop restart/seek/reschedule: stopAllPlayback giữ session FX realtime (không
|
||||
// teardown) → fxRtStart early-return → KHÔNG reload VST ~4s mỗi pass loop
|
||||
// (hết bypass rồi mới load lại). Chỉ set TRƯỚC nhánh restart/resume; user
|
||||
// stop thật (fade=true) / bridge-down / new-project / export-end KHÔNG set →
|
||||
// teardown bình thường.
|
||||
let fxRtKeepForRestart = false;
|
||||
// Pace input realtime: worklet sản xuất đúng realtime, nhưng sau khi main
|
||||
// thread bị block (solo restart, GUI decode, rAF) các frame 'in' dồn lại —
|
||||
// gửi 1 loạt làm SHM ring 4-slot tràn → mất block → ngắt quãng. Gửi tối đa 1
|
||||
@@ -17649,6 +17655,7 @@ const App = () => {
|
||||
tNode.gainNode.gain.linearRampToValueAtTime(0.001, ctx.currentTime + 0.04);
|
||||
}
|
||||
setTimeout(() => {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
if (tNode && tNode.gainNode) {
|
||||
const trackData = activeTracksRef.current ? activeTracksRef.current.find(t => t.id === trackId) : null;
|
||||
@@ -18235,6 +18242,7 @@ const App = () => {
|
||||
// already uses for solo.
|
||||
if (becameAudible && isPlaying && recordingStateRef.current !== 'RECORDING' && activeTracksRef.current && activeTracksRef.current.length) {
|
||||
try {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
const ctx = getAudioContext();
|
||||
const resumeAt = currentTimeRef.current || 0;
|
||||
@@ -18543,6 +18551,7 @@ const App = () => {
|
||||
localSelectionAnchorRef.current = time;
|
||||
if (isPlaying) {
|
||||
setCurrentTime(time);
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
setTimeout(() => {
|
||||
startOffsetTimeRef.current = time;
|
||||
@@ -18967,6 +18976,7 @@ const App = () => {
|
||||
}));
|
||||
}
|
||||
const _pt = Math.max(0, startOffsetTimeRef.current + (_ctx.currentTime - startAudioTimeRef.current) - _secStart);
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = _pt;
|
||||
startAudioTimeRef.current = _ctx.currentTime;
|
||||
@@ -18987,6 +18997,7 @@ const App = () => {
|
||||
const _mainPos = startOffsetTimeRef.current + (_ctx.currentTime - startAudioTimeRef.current);
|
||||
const _itemInfo = getPianoRollItemInfo(_prTabNew);
|
||||
const _prPos = _itemInfo ? Math.max(0, _mainPos - (_itemInfo.startTime || 0)) : (_prTabNew.currentTime || 0);
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = _prPos;
|
||||
startAudioTimeRef.current = _ctx.currentTime;
|
||||
@@ -19040,6 +19051,7 @@ const App = () => {
|
||||
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) + ')');
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = _resumeAt;
|
||||
startAudioTimeRef.current = getAudioContext().currentTime;
|
||||
@@ -23266,6 +23278,7 @@ const App = () => {
|
||||
lastRescheduleTimeRef.current = nowT;
|
||||
scheduledItemsSigRef.current = sig;
|
||||
const pt = currentTimeRef.current;
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
setIsPlaying(true);
|
||||
// Re-schedule theo ĐÚNG chế độ play hiện tại: loop local / solo chỉ
|
||||
@@ -23511,6 +23524,7 @@ const App = () => {
|
||||
const start = Math.min(st.selectionStart, st.selectionEnd);
|
||||
const end = Math.max(st.selectionStart, st.selectionEnd);
|
||||
if (bufferPos >= end) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
setSubTabs(prev => prev.map(s => s.id === activeTabRef.current ? {
|
||||
...s,
|
||||
@@ -23537,6 +23551,7 @@ const App = () => {
|
||||
return Math.max(maxEnd * beatSec, 16 * beatSec * 4) + 1.0;
|
||||
})() : st.buffer.duration;
|
||||
if (bufferPos >= effectiveDuration) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
if (st.isLooping) {
|
||||
setSubTabs(prev => prev.map(s => s.id === activeTabRef.current ? {
|
||||
@@ -23576,6 +23591,7 @@ const App = () => {
|
||||
if (!selectionCleared && isLoopingSelection && selLeft !== null && selRight !== null) {
|
||||
if (selRight > selLeft && updatedTime >= selRight) {
|
||||
if (hasAnySolo || selectionMode === 'local') {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = selLeft;
|
||||
startAudioTimeRef.current = context.currentTime;
|
||||
@@ -23588,6 +23604,7 @@ const App = () => {
|
||||
setCurrentTime(selLeft);
|
||||
setIsPlaying(true);
|
||||
} else {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = selLeft;
|
||||
startAudioTimeRef.current = context.currentTime;
|
||||
@@ -23609,6 +23626,7 @@ const App = () => {
|
||||
return;
|
||||
}
|
||||
if (isLoopingSelection) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
startOffsetTimeRef.current = 0;
|
||||
startAudioTimeRef.current = context.currentTime;
|
||||
@@ -24998,6 +25016,7 @@ const App = () => {
|
||||
} : s));
|
||||
} else {
|
||||
try {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
const startOffset = st.currentTime || 0;
|
||||
if (st.type === 'PIANO_ROLL') {
|
||||
@@ -25049,6 +25068,7 @@ const App = () => {
|
||||
synthEngine: ltrk ? ltrk.synth_engine : undefined
|
||||
});
|
||||
});
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
const prCtx = getAudioContext();
|
||||
const prTNode = activeTrackNodesRef.current[prSt.trackId];
|
||||
@@ -25083,10 +25103,17 @@ const App = () => {
|
||||
isPlayingRef.current = false;
|
||||
// Gap 7: xoa automation schedule — tra gain master ve 1.0 (fader rieng).
|
||||
try { if (masterBus && masterBus.output && masterBus.output.gain) { const _actx = getAudioContext(); masterBus.output.gain.cancelScheduledValues(_actx.currentTime); masterBus.output.gain.setValueAtTime(1, _actx.currentTime); } } catch (e) { }
|
||||
// Realtime VST FX: dừng session + khôi phục output→analyser.
|
||||
try { fxRtStop(); } catch (e) { console.warn('[FX RT] stop error:', e); }
|
||||
// Realtime VST FX per-track: dừng mọi session track + restore chain trực tiếp.
|
||||
try { fxRtTrackStopAll(); } catch (e) { console.warn('[FX RT track] stop error:', e); }
|
||||
// Realtime VST FX: dừng session + khôi phục output→analyser. Nhánh restart
|
||||
// (loop/seek/reschedule/solo...) set fxRtKeepForRestart=true → GIỮ session
|
||||
// (fxRtStart early-return khi active + chain khớp) → hết bypass+reload ~4s
|
||||
// mỗi pass loop. User stop thật (fade=true) → teardown bình thường.
|
||||
const _keepFx = fxRtKeepForRestart;
|
||||
fxRtKeepForRestart = false;
|
||||
if (!_keepFx) {
|
||||
try { fxRtStop(); } catch (e) { console.warn('[FX RT] stop error:', e); }
|
||||
// Realtime VST FX per-track: dừng mọi session track + restore chain trực tiếp.
|
||||
try { fxRtTrackStopAll(); } catch (e) { console.warn('[FX RT track] stop error:', e); }
|
||||
}
|
||||
_nativePlayEpoch++; // hủy mọi render/decode pending của lần play cũ
|
||||
// V11: hủy mọi note timer của lần play cũ (stop/seek/loop restart) — timer
|
||||
// cũ bắn vào vòng mới = NOTE_OFF cắt note đang kêu → crack (xem ref).
|
||||
@@ -25210,6 +25237,7 @@ const App = () => {
|
||||
const st = subTabs.find(s => s.id === activeTab);
|
||||
if (!st) return;
|
||||
if (st.isPlaying) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
window.SonicSF.stopAll();
|
||||
stopAllNativeSfNotes(); stopAllNativeVstNotes();
|
||||
@@ -25234,6 +25262,7 @@ const App = () => {
|
||||
}
|
||||
} else {
|
||||
if (isPlaying) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
setCurrentTime(time);
|
||||
startOffsetTimeRef.current = time;
|
||||
@@ -26919,6 +26948,7 @@ const App = () => {
|
||||
const toggleTrackSoloEvaluate = trackId => {
|
||||
const wasPlaying = isPlaying;
|
||||
const playingSubTab = subTabs.find(s => s.isPlaying && s.type === 'PIANO_ROLL');
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
updateActiveTracks(prev => prev.map(t =>
|
||||
t.id === trackId ? { ...t, solo: !t.solo, muted: false } : t
|
||||
@@ -28086,6 +28116,7 @@ const App = () => {
|
||||
|
||||
// Rewind + play the FULL project from the start (FluidSynth + FX racks +
|
||||
// mastering chain — exactly what you hear).
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
try { if (window.SonicSF && window.SonicSF.stopAllNotes) window.SonicSF.stopAllNotes(); } catch (e) { }
|
||||
startTrackPlayback(0);
|
||||
@@ -33193,6 +33224,7 @@ STRICT CONSTRAINTS:
|
||||
const seekSt = subTabs.find(s => s.id === activeTab && s.type === 'PIANO_ROLL');
|
||||
if (!seekSt) return;
|
||||
if (seekSt.isPlaying) {
|
||||
fxRtKeepForRestart = true; // restart/resume — giữ session FX realtime
|
||||
stopAllPlayback();
|
||||
window.SonicSF.stopAll();
|
||||
stopAllNativeSfNotes(); stopAllNativeVstNotes();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@
|
||||
<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/chordTheory.js?v=202608211300"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608242200" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608242300" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user