fix(bridge): loop keeps playing — cancel delayed STOP#2 on PLAY (V10) + epoch-guard stale note timers (V11, crack fix)

This commit is contained in:
2026-08-14 11:24:44 +07:00
parent d42abb2a41
commit dc54a0c3f3
2 changed files with 66 additions and 7 deletions
+41 -1
View File
@@ -17877,6 +17877,16 @@ const App = () => {
// D9: playhead sample counter cho native bridge (A11/A13 dùng đ đng b
// timeline sample-accurate); cp nht trong updatePlayhead khi bridge active.
const bridgePlayheadSampleRef = useRef(0);
// V10: timer ca transport STOP#2 (150ms sau STOP#1, V8 bug 3 guard) loop
// restart gi stopAllPlayback() + startTrackPlayback() cùng lúc, STOP#2 tr
// đến sau PLAY C++ transportStopped=true vĩnh vin note-on loop tiếp b
// drop VSTi ch play 1 vòng. PLAY hy timer này.
const bridgeStopRetryTimerRef = useRef(null);
// V11: epoch ca ln play hin ti stopAllPlayback tăng epoch mi note
// timer (NOTE_ON/NOTE_OFF setTimeout) ca vòng cũ b hy khi loop restart;
// nếu không NOTE_OFF tr ca vòng cũ bn vào note ĐANG kêu vòng mi ct
// đt ngt (click) + chng voice mx>1.0 clip cracking khi loop.
const bridgeNoteEpochRef = useRef(0);
// Resume main/session play khi ri PIANO ROLL tab: m piano roll lúc main
// đang play bm Space (play piano roll) stopAllPlayback dng main
// quay li MAIN/SECTION CÂM. Lưu {offset, audioTime} lúc m tab khi
@@ -21880,11 +21890,15 @@ const App = () => {
if (program !== undefined && program !== null) {
try { if (window.__ensureBridgeProgram) window.__ensureBridgeProgram(trkId, program, synthEngine); } catch (e) {}
}
// V11: epoch guard hy timer cũ khi stop/loop restart (xem ref).
const epoch = bridgeNoteEpochRef.current;
setTimeout(function () {
if (epoch !== bridgeNoteEpochRef.current) return;
if (!guardPlay()) return;
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_ON', channel: trkId, pitch: pitch || 60, velocity: velocity || 0.8, percussion: isPerc }); } catch (e) {}
}, delayMs);
setTimeout(function () {
if (epoch !== bridgeNoteEpochRef.current) return;
if (!guardPlay()) return;
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_OFF', channel: trkId, pitch: pitch || 60, velocity: 0, percussion: isPerc }); } catch (e) {}
}, delayMs + (durMs || 1000) + 30);
@@ -21898,6 +21912,13 @@ const App = () => {
// D2: bridge active -> báo transport PLAY (bridge flush note-off cũ + đng
// b timeline; A13 C++ x lý arg1=playheadSamples sau này).
if (window.NativeBridgeService && window.NativeBridgeService.isBridgeConnected) {
// V10: loop restart stopAllPlayback va schedule STOP#2 (150ms); hy
// ngay khi PLAY, nếu không STOP#2 đến sau PLAY bridge drop mi note-on
// (V8 guard transportStopped) VSTi ch play 1 vòng loop.
if (bridgeStopRetryTimerRef.current) {
clearTimeout(bridgeStopRetryTimerRef.current);
bridgeStopRetryTimerRef.current = null;
}
try { window.NativeBridgeService.transport('play'); } catch (e) {}
}
// FIX: đng b mastering + Carla status NGAY khi play MIDI item phi
@@ -22317,6 +22338,16 @@ const App = () => {
const schedulePianoRollMidi = (st, offsetSeconds, notesOverride) => {
if (st.type !== 'PIANO_ROLL') return;
const context = getAudioContext();
// V10: piano roll loop restart cũng gi stopAllPlayback() trưc (STOP#1
// bridge transportStopped=true) mi note-on b drop nếu không PLAY li.
// Hy STOP#2 (150ms) + báo PLAY đ bridge nhn note mi (loop liên tc).
if (window.NativeBridgeService && window.NativeBridgeService.isBridgeConnected) {
if (bridgeStopRetryTimerRef.current) {
clearTimeout(bridgeStopRetryTimerRef.current);
bridgeStopRetryTimerRef.current = null;
}
try { window.NativeBridgeService.transport('play'); } catch (e) {}
}
try { if (window.__ensureMasteringRouting) window.__ensureMasteringRouting(); } catch (e) {}
refreshCarlaStatus();
const midiNotes = notesOverride || st.notes || [];
@@ -22510,6 +22541,9 @@ const App = () => {
const stopAllPlayback = () => {
try {
isPlayingRef.current = false;
// V11: hy mi note timer ca ln play cũ (stop/seek/loop restart) timer
// cũ bn vào vòng mi = NOTE_OFF ct note đang kêu crack (xem ref).
bridgeNoteEpochRef.current++;
try { if (subTabsRef.current) subTabsRef.current = subTabsRef.current.map(s => ({ ...s, isPlaying: false })); } catch (e) {}
activeSourcesRef.current.forEach(src => {
try {
@@ -22536,7 +22570,13 @@ const App = () => {
try { window.NativeBridgeService.transport('stop'); } catch (e) {}
// V8 bug 3: transport stop thu 2 sau 150ms note-on timer guard sync qua
// React effect (cham) van bay toi bridge sau STOP retrigger am treo.
setTimeout(() => { try { window.NativeBridgeService.transport('stop'); } catch (e) {} }, 150);
// V10: timer lưu ref startTrackPlayback (loop restart) hy khi PLAY,
// tránh STOP#2 giết loop tiếp theo (VSTi ch play 1 vòng).
if (bridgeStopRetryTimerRef.current) { clearTimeout(bridgeStopRetryTimerRef.current); }
bridgeStopRetryTimerRef.current = setTimeout(() => {
bridgeStopRetryTimerRef.current = null;
try { window.NativeBridgeService.transport('stop'); } catch (e) {}
}, 150);
}
if (window.SonicSF) {
try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); }
File diff suppressed because one or more lines are too long