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:
+41
-1
@@ -17877,6 +17877,16 @@ const App = () => {
|
||||
// D9: playhead sample counter cho native bridge (A11/A13 dùng để đồng bộ
|
||||
// timeline sample-accurate); cập nhật trong updatePlayhead khi bridge active.
|
||||
const bridgePlayheadSampleRef = useRef(0);
|
||||
// V10: timer của transport STOP#2 (150ms sau STOP#1, V8 bug 3 guard) — loop
|
||||
// restart gọi stopAllPlayback() + startTrackPlayback() cùng lúc, STOP#2 trễ
|
||||
// đến sau PLAY → C++ transportStopped=true vĩnh viễn → note-on loop tiếp bị
|
||||
// drop → VSTi chỉ play 1 vòng. PLAY hủy timer này.
|
||||
const bridgeStopRetryTimerRef = useRef(null);
|
||||
// V11: epoch của lần play hiện tại — stopAllPlayback tăng epoch → mọi note
|
||||
// timer (NOTE_ON/NOTE_OFF setTimeout) của vòng cũ bị hủy khi loop restart;
|
||||
// nếu không NOTE_OFF trễ của vòng cũ bắn vào note ĐANG kêu vòng mới → cắt
|
||||
// đột ngột (click) + chồng voice → mx>1.0 clip → cracking khi loop.
|
||||
const bridgeNoteEpochRef = useRef(0);
|
||||
// Resume main/session play khi rời PIANO ROLL tab: mở piano roll lúc main
|
||||
// đang play → bấm Space (play piano roll) → stopAllPlayback dừng main →
|
||||
// quay lại 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 — hủy 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 vừa schedule STOP#2 (150ms); hủy
|
||||
// ngay khi PLAY, nếu không STOP#2 đến sau PLAY → bridge drop mọi 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 phải
|
||||
@@ -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 gọi stopAllPlayback() trước (STOP#1 →
|
||||
// bridge transportStopped=true) → mọi note-on bị drop nếu không PLAY lại.
|
||||
// Hủy STOP#2 (150ms) + báo PLAY để bridge nhận note mới (loop liên tục).
|
||||
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: 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).
|
||||
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) hủy 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
Reference in New Issue
Block a user