diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx
index 79fb854..6c055f2 100644
--- a/app/static/js/app.jsx
+++ b/app/static/js/app.jsx
@@ -23353,6 +23353,14 @@ const App = () => {
activeSourcesRef.current = [];
activeTrackNodesRef.current = {};
const fadedGains = [];
+ // V16: stop NGAY clip source CHƯA phát (playbackState='scheduled') — nếu
+ // chờ tới doHardStop (sau FADE_MS) chúng đã bắt đầu phát trong lúc fade →
+ // nghe như "play lại rồi tắt". Source đang kêu ('playing') giữ lại cho ramp.
+ try {
+ capturedSources.forEach(src => {
+ try { if (src.playbackState === 'scheduled') src.stop(); } catch (e) {}
+ });
+ } catch (e) {}
let bridgeGainNode = null;
if (fade && ctx) {
try {
diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js
index 4470aee..6501343 100644
--- a/app/static/js/app.precompiled.js
+++ b/app/static/js/app.precompiled.js
@@ -1722,7 +1722,10 @@ bridgeNoteEpochRef.current++;try{clearAllPendingNoteOffs();}catch(e){}try{if(sub
// xuống 0 trong FADE_MS rồi mới hard-stop — hết click, âm ngân mượt.
// Loop restart/seek (fade=false) giữ stop tức thì như cũ (không fade).
// Lưu ref trước khi clear để doHardStop trễ không đụng playback mới.
-const ctx=getAudioContext();const stopEpoch=_nativePlayEpoch;const FADE_MS=200;const capturedSources=activeSourcesRef.current.slice();const capturedNodes=Object.values(activeTrackNodesRef.current);activeSourcesRef.current=[];activeTrackNodesRef.current={};const fadedGains=[];let bridgeGainNode=null;if(fade&&ctx){try{const now=ctx.currentTime;capturedNodes.forEach(n=>{if(n&&n.gainNode&&n.gainNode.gain){fadedGains.push({node:n.gainNode,prev:n.gainNode.gain.value});n.gainNode.gain.cancelScheduledValues(now);n.gainNode.gain.setValueAtTime(n.gainNode.gain.value,now);n.gainNode.gain.linearRampToValueAtTime(0.0001,now+FADE_MS/1000);}});if(window.BridgeAudioNode&&window.BridgeAudioNode.getOutputNode){const bg=window.BridgeAudioNode.getOutputNode();if(bg&&bg.gain){bridgeGainNode=bg;bg.gain.cancelScheduledValues(now);bg.gain.setValueAtTime(bg.gain.value,now);bg.gain.linearRampToValueAtTime(0.0001,now+FADE_MS/1000);}}}catch(e){console.warn('[Stop] fade setup error:',e);}}const doHardStop=()=>{try{const stale=_nativePlayEpoch!==stopEpoch;// play mới bắt đầu trong lúc fade
+const ctx=getAudioContext();const stopEpoch=_nativePlayEpoch;const FADE_MS=200;const capturedSources=activeSourcesRef.current.slice();const capturedNodes=Object.values(activeTrackNodesRef.current);activeSourcesRef.current=[];activeTrackNodesRef.current={};const fadedGains=[];// V16: stop NGAY clip source CHƯA phát (playbackState='scheduled') — nếu
+// chờ tới doHardStop (sau FADE_MS) chúng đã bắt đầu phát trong lúc fade →
+// nghe như "play lại rồi tắt". Source đang kêu ('playing') giữ lại cho ramp.
+try{capturedSources.forEach(src=>{try{if(src.playbackState==='scheduled')src.stop();}catch(e){}});}catch(e){}let bridgeGainNode=null;if(fade&&ctx){try{const now=ctx.currentTime;capturedNodes.forEach(n=>{if(n&&n.gainNode&&n.gainNode.gain){fadedGains.push({node:n.gainNode,prev:n.gainNode.gain.value});n.gainNode.gain.cancelScheduledValues(now);n.gainNode.gain.setValueAtTime(n.gainNode.gain.value,now);n.gainNode.gain.linearRampToValueAtTime(0.0001,now+FADE_MS/1000);}});if(window.BridgeAudioNode&&window.BridgeAudioNode.getOutputNode){const bg=window.BridgeAudioNode.getOutputNode();if(bg&&bg.gain){bridgeGainNode=bg;bg.gain.cancelScheduledValues(now);bg.gain.setValueAtTime(bg.gain.value,now);bg.gain.linearRampToValueAtTime(0.0001,now+FADE_MS/1000);}}}catch(e){console.warn('[Stop] fade setup error:',e);}}const doHardStop=()=>{try{const stale=_nativePlayEpoch!==stopEpoch;// play mới bắt đầu trong lúc fade
capturedSources.forEach(src=>{try{src.stop();}catch(e){}});capturedNodes.forEach(n=>{if(n.fxStopFn){try{n.fxStopFn();}catch(e){}}});if(stale)return;// play mới đang chạy — không dừng bridge/SF của nó
stopMidiCapture();stopAllNativeSfNotes();stopAllNativeVstNotes();// D2/D6: bridge active -> transport STOP (bridge flush toàn pitch, hết
// âm ngân tức thì; A13 C++ flush note-off).
diff --git a/app/templates/index.html b/app/templates/index.html
index c1f7b09..3b7cef6 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -50,7 +50,7 @@
-
+