V45.16: freeze playhead khi seek master FX active (het playhead chay truoc am ~128ms) v=202608311143

This commit is contained in:
2026-08-31 11:43:21 +07:00
parent 39f00c3c0b
commit 80b8c3e034
3 changed files with 37 additions and 10 deletions
+25 -3
View File
@@ -309,6 +309,8 @@ let _dbgFirstAudiblePending = false;
let _fxRtPlayheadSnapPending = false;
let _fxRtFreezeTimer = null;
let _fxRtFreezeTries = 0;
let _fxRtSeekFreeze = false;
let _fxRtSeekFreezeTimer = null;
const _nativeTrackUrls = {};
const _nativeTrackPending = {};
// ── MIDI overlap cleanup + polyphony gain (spec midi_polyphony_loudness) ─
@@ -21004,6 +21006,17 @@ const App = () => {
if (fxRt && fxRt.active) {
_fxRtPlayheadSnapPending = false;
fxRtAnchorProcessed();
// V45.16: seek/restart FX active — giữ playhead tại startOffset cho tới khi
// worklet phát block đầu (fx_play_first). Trước đây playhead chạy theo
// processedSamples trong lúc re-buffer FILL 24 (~128ms) → playhead trước
// âm (Bug 1b). Fail-safe 1500ms: engine không phát (session fail) → thả.
_fxRtSeekFreeze = true;
if (_fxRtSeekFreezeTimer) { clearTimeout(_fxRtSeekFreezeTimer); }
_fxRtSeekFreezeTimer = setTimeout(function () {
_fxRtSeekFreeze = false;
_fxRtSeekFreezeTimer = null;
fxRtAnchorProcessed();
}, 1500);
return;
}
// V41: gate theo CHAIN (không chỉ active) — session FX warm (stop giữ warm,
@@ -21034,6 +21047,13 @@ const App = () => {
}, _estMs);
};
window.__fxRtSnapPlayhead = function () {
// V45.16: hết seek-freeze — playhead bắt đầu từ startOffset tại mẫu nghe được.
if (_fxRtSeekFreeze) {
_fxRtSeekFreeze = false;
if (_fxRtSeekFreezeTimer) { clearTimeout(_fxRtSeekFreezeTimer); _fxRtSeekFreezeTimer = null; }
fxRtAnchorProcessed();
return;
}
if (fxRt && fxRt.active && _fxRtPlayheadSnapPending) {
_fxRtPlayheadSnapPending = false;
startAudioTimeRef.current = getAudioContext().currentTime;
@@ -24573,9 +24593,11 @@ const App = () => {
const updatedTime = startOffsetTimeRef.current + elapsed;
// G3: playhead display engine-derived khi FX active (wrap/loop trigger giu
// wall-clock — transport truth).
const _playheadSec = (fxRt && fxRt.active && typeof fxRt.processedSamples === 'number' && fxRt.processedAnchor != null)
? Math.max(startOffsetTimeRef.current, startOffsetTimeRef.current + (fxRt.processedSamples - fxRt.processedAnchor - (fxRt.latencyTotal || 0)) / (context.sampleRate || 44100))
: updatedTime;
const _playheadSec = (_fxRtSeekFreeze && fxRt && fxRt.active)
? startOffsetTimeRef.current
: ((fxRt && fxRt.active && typeof fxRt.processedSamples === 'number' && fxRt.processedAnchor != null)
? Math.max(startOffsetTimeRef.current, startOffsetTimeRef.current + (fxRt.processedSamples - fxRt.processedAnchor - (fxRt.latencyTotal || 0)) / (context.sampleRate || 44100))
: updatedTime);
// Selection Loop - LOOP_MAKER.md + LOOP_EDITOR_2.md §4.2
// If selection cleared by user, play linearly (don't loop)