M22: fix Bug C playhead nhay khi PWR toggle — rebase timeline (off/at + anchor=ps) khi wake warm-session giua play; diag Bug B — mdiag dB khi F7 toggle Mixer Panel
This commit is contained in:
@@ -19630,6 +19630,10 @@ const App = () => {
|
||||
setShowMediaExplorerRef.current = setShowMediaExplorer;
|
||||
// F6/F7 mutual exclusion: enabling one panel disables the other
|
||||
window.__toggleMixerRef = function() {
|
||||
// M22 Bug B diag: F7 mở/đóng MIXER PANEL (user: mở panel → âm lượng giảm
|
||||
// nhỏ; M20 chỉ instrument MasteringModal panel_open — F7 đi đường khác nên
|
||||
// chưa có dB). Chụp dB master OUT + hist + post như panel_open.
|
||||
try { _mdiagSample('mixer_toggle'); _mdiagDump(); _mdiagSchedulePost(); } catch (e) {}
|
||||
setShowMixer(function(p) {
|
||||
const next = !p;
|
||||
if (next) setShowMediaExplorerRef.current(false);
|
||||
@@ -20828,6 +20832,8 @@ const App = () => {
|
||||
|
||||
useEffect(() => {
|
||||
window.currentMasteringSettings = masteringSettings;
|
||||
const _fxPrevAct = !!(fxRt && fxRt.active);
|
||||
const _fxPrevWarm = !!(fxRt && fxRt.warm);
|
||||
if (audioCtx && masterBus) {
|
||||
toggleMasteringOnMaster(masteringSettings.masterConnected, masteringSettings.isBypassed);
|
||||
applyMasteringSettings(masteringSettings);
|
||||
@@ -20914,6 +20920,7 @@ const App = () => {
|
||||
updateSfRouting();
|
||||
} catch (e) {}
|
||||
}
|
||||
try { if (!_fxPrevAct && _fxPrevWarm && fxRt.active && window.__fxRtWakeRebase) window.__fxRtWakeRebase(); } catch (e) {}
|
||||
try { if (_phArmed() && window.__phSnapAll) { window.__phSnapAll('ph_effect'); _dbgLog('ph_effect', 'mc=' + (masteringSettings.masterConnected ? 1 : 0) + ' bp=' + (masteringSettings.isBypassed ? 1 : 0) + ' chain=' + fxRtChain().length + ' gui=' + (masteringSettings.gui_open_path || 'na')); } } catch (e) {}
|
||||
}, [masteringSettings]);
|
||||
|
||||
@@ -21875,6 +21882,26 @@ const App = () => {
|
||||
const mainResumeRef = useRef(null);
|
||||
currentTimeRef.current = currentTime;
|
||||
|
||||
// M22 Bug C fix hook: rebase timeline khi wake warm-session (PWR ON giữa lúc
|
||||
// play) — engine processedSamples bị đóng băng suốt OFF-warm (worklet rời
|
||||
// graph) nên ps−anchor cũ tụt cả khoảng OFF; nếu không rebase playhead
|
||||
// engine-derived nhảy LÙI về vị trí trước OFF (ph_jump d âm lớn). Rebase:
|
||||
// dời startOffsetTimeRef/startAudioTimeRef về wall hiện tại + anchor=ps →
|
||||
// engine clock nối lại đúng chỗ, playhead liên tục (lệch ~latencyTotal).
|
||||
window.__fxRtWakeRebase = function () {
|
||||
try {
|
||||
if (!fxRt || !fxRt.active) return;
|
||||
const _c = getAudioContext();
|
||||
const _wall = (startOffsetTimeRef.current || 0) + (_c.currentTime - (startAudioTimeRef.current || _c.currentTime));
|
||||
const _pa = (typeof fxRt.processedSamples === 'number') ? fxRt.processedSamples : null;
|
||||
startOffsetTimeRef.current = _wall;
|
||||
startAudioTimeRef.current = _c.currentTime;
|
||||
if (_pa !== null) fxRt.processedAnchor = _pa;
|
||||
if (_fxRtSeekFreeze) { _fxRtSeekFreeze = false; if (_fxRtSeekFreezeTimer) { clearTimeout(_fxRtSeekFreezeTimer); _fxRtSeekFreezeTimer = null; } }
|
||||
try { _dbgLog('ph_rebase', 'wall=' + _wall.toFixed(3) + ' at=' + startAudioTimeRef.current.toFixed(3) + ' off=' + startOffsetTimeRef.current.toFixed(3) + ' ps=' + (typeof fxRt.processedSamples === 'number' ? fxRt.processedSamples : 'na') + ' pa=' + (typeof fxRt.processedAnchor === 'number' ? fxRt.processedAnchor : 'na') + ' lat=' + (fxRt.latencyTotal || 0)); } catch (e) {}
|
||||
} catch (e) { try { _dbgLog('ph_rebase', 'err ' + String(e)); } catch (e2) {} }
|
||||
};
|
||||
|
||||
// M21 ph-diag hooks (Bug C — playhead nhay khi PWR master FX toggle)
|
||||
// __phArmPwr: user bam PWR OFF/ON -> arm cua so log 6s + snapshot ngay lap tuc.
|
||||
// __phSnapAll: snapshot refs/fxRt tai diem goi (chi log khi cua so dang mo).
|
||||
|
||||
@@ -417,7 +417,7 @@ if(fxRt.warm)fxRtStop(true);// Token chống race bypass: user bấm PWR/Bypass
|
||||
// node → bypass vô hiệu. Mỗi fxRtStop() bump startToken → mọi checkpoint
|
||||
// dưới đây thấy mismatch → hủy session vừa tạo, không nối node.
|
||||
const token=++fxRt.startToken;fxRt.starting=true;const ctx=getAudioContext();if(!ctx||!ctx.audioWorklet||!masterBus||!masterBus.output||!masterBus.analyser||!masterBus.masterFxGain||!masterBus.masterFxDryGain)return;const chainPayload=chain.map(s=>s.type==='vst3'?{type:'vst3',path:s.path,name:s.name||s.path,preset_b64:s.preset_b64||'',bypass:!!s.bypass||!!(window.currentMasteringSettings||{}).vstBypass}:{type:'builtin',id:s.type,params:s.params&&Object.keys(s.params).length?s.params:{},bypass:!!s.bypass});let st;try{st=await window.SonicAPI.fxRealtimeStart({fx_chain:chainPayload,sample_rate:ctx.sampleRate||44100});}catch(e){console.warn('[FX RT] start fail:',e);showToast('Không khởi động VST FX realtime: '+(e.message||e),'warning');fxRt.starting=false;return;}if(token!==fxRt.startToken){// Bypass/stop giữa lúc engine đang load chain — hủy session vừa tạo.
|
||||
console.warn('[FX RT] start cancelled mid-flight — session stopped');try{window.SonicAPI.fxRealtimeStop({session_id:st.session_id}).catch(()=>{});}catch(e2){}fxRt.starting=false;return;}fxRt.sessionId=st.session_id;fxRtGuiPushShm();try{await ctx.audioWorklet.addModule(`${API_BASE_URL}/static/js/sf-fx-realtime.js?v=202609071004`);if(token!==fxRt.startToken){fxRt.starting=false;return;}fxRt.node=new AudioWorkletNode(ctx,'sf-fx-realtime',{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[2]});const u=new URL(API_BASE_URL);const wsProto=u.protocol==='https:'?'wss://':'ws://';// WS to 'localhost' bi tre ~300ms: Chromium thu ::1 truoc (Windows khong
|
||||
console.warn('[FX RT] start cancelled mid-flight — session stopped');try{window.SonicAPI.fxRealtimeStop({session_id:st.session_id}).catch(()=>{});}catch(e2){}fxRt.starting=false;return;}fxRt.sessionId=st.session_id;fxRtGuiPushShm();try{await ctx.audioWorklet.addModule(`${API_BASE_URL}/static/js/sf-fx-realtime.js?v=202609071038`);if(token!==fxRt.startToken){fxRt.starting=false;return;}fxRt.node=new AudioWorkletNode(ctx,'sf-fx-realtime',{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[2]});const u=new URL(API_BASE_URL);const wsProto=u.protocol==='https:'?'wss://':'ws://';// WS to 'localhost' bi tre ~300ms: Chromium thu ::1 truoc (Windows khong
|
||||
// co listener IPv6 -> SYN hang ~2s) roi moi fallback 127.0.0.1. Dung
|
||||
// thang 127.0.0.1 (loopback) -> WS open ~5ms. Server chap nhan loopback.
|
||||
const wsHost=u.hostname==='localhost'||u.hostname==='::1'?'127.0.0.1'+(u.port?':'+u.port:''):u.host;const tok=localStorage.getItem('sonic_token')||'';const wsUrl=`${wsProto}${wsHost}${st.ws_url}${tok?'?token='+encodeURIComponent(tok):''}`;const ws=new WebSocket(wsUrl);fxRt.ws=ws;ws.binaryType='arraybuffer';ws.onopen=()=>{if(token!==fxRt.startToken){try{ws.close();}catch(e){}fxRt.starting=false;return;}fxRt.active=true;fxRt.flushing=false;fxRt.starting=false;fxRt.inQueue=[];// bỏ audio stale sản xuất trước khi WS open (~4s load VST) — hết lag khởi đầu
|
||||
@@ -1505,7 +1505,10 @@ React.useEffect(()=>{const VKB_MAP={z:0,s:1,x:2,d:3,c:4,v:5,g:6,b:7,h:8,n:9,j:10
|
||||
// element khác, không chặn listener cùng node cùng phase (bug user:
|
||||
// VKB mở, phím V vẫn bật prompt Gain).
|
||||
e.stopImmediatePropagation();const vel=Math.max(1,Math.min(127,parseInt(s.velocity)||100))/127;if(vkbNoteOnRef.current)vkbNoteOnRef.current(pitch,vel);}else{e.preventDefault();e.stopImmediatePropagation();if(vkbNoteOffRef.current)vkbNoteOffRef.current(pitch);}};window.addEventListener('keydown',handler,{capture:true});window.addEventListener('keyup',handler,{capture:true});return()=>{window.removeEventListener('keydown',handler,{capture:true});window.removeEventListener('keyup',handler,{capture:true});};},[]);const[showAIPanel,setShowAIPanel]=useState(true);const[showSelectionPanel,setShowSelectionPanel]=useState(false);const[showPythonToolsPanel,setShowPythonToolsPanel]=useState(false);const[showMediaExplorer,setShowMediaExplorer]=useState(false);const[scrollBufferExtra,setScrollBufferExtra]=useState(0);const scrollBufferExtraRef=useRef(0);scrollBufferExtraRef.current=scrollBufferExtra;const[showFxRack,setShowFxRack]=useState(false);const[showMidiEvents,setShowMidiEvents]=useState(false);const[showMixer,setShowMixer]=useState(false);const setShowMixerRef=useRef(setShowMixer);setShowMixerRef.current=setShowMixer;const setShowMediaExplorerRef=useRef(setShowMediaExplorer);setShowMediaExplorerRef.current=setShowMediaExplorer;// F6/F7 mutual exclusion: enabling one panel disables the other
|
||||
window.__toggleMixerRef=function(){setShowMixer(function(p){const next=!p;if(next)setShowMediaExplorerRef.current(false);return next;});};// Live per-track bypass (split into two buttons per user request):
|
||||
window.__toggleMixerRef=function(){// M22 Bug B diag: F7 mở/đóng MIXER PANEL (user: mở panel → âm lượng giảm
|
||||
// nhỏ; M20 chỉ instrument MasteringModal panel_open — F7 đi đường khác nên
|
||||
// chưa có dB). Chụp dB master OUT + hist + post như panel_open.
|
||||
try{_mdiagSample('mixer_toggle');_mdiagDump();_mdiagSchedulePost();}catch(e){}setShowMixer(function(p){const next=!p;if(next)setShowMediaExplorerRef.current(false);return next;});};// Live per-track bypass (split into two buttons per user request):
|
||||
// - 'audio': bypass FX+mastering for AUDIO items (audio clips + section items)
|
||||
// - 'midi' : bypass FX+mastering for MIDI items (soundfont instrument)
|
||||
window.__setTrackBypass=function(trackId,which,on){if(which==='audio'){trackAudioBypassMap[trackId]=!!on;trackMasteringBypassMap[trackId]=!!on;// Live re-route the main track node AND every section sub-node of this
|
||||
@@ -1679,7 +1682,7 @@ const handlePrefsChange=next=>{setPrefs(next);try{localStorage.setItem('sf_prefs
|
||||
const[fxRackTarget,setFxRackTarget]=useState(null);window.__openFxRack=(trackId,trackName)=>setFxRackTarget({trackId,trackName});// Render offline toàn project qua native_bridge (VST FX masterbus + track) → WAV.
|
||||
// Dùng cho nút "Nghe thử" trong FX Rack / Mastering — preview = âm export.
|
||||
window.__renderProjectPreview=async()=>{const schema=serializeProjectToSchema(currentProjectId||'temp_project',projectName||'Dự án tạm chưa lưu',bpm,tracks,subTabs,sessionTabs,masteringSettings,masterVolume,{volL:masterVolL,volR:masterVolR,link:masterLink,muted:masterMuted});try{const res=await window.SonicAPI.renderProject(schema,'preview_'+Date.now()+'.wav');_lastMasterStats={lufs:res.lufs,true_peak_db:res.true_peak_db};return res;}catch(e){window.showToast&&window.showToast('Render preview lỗi: '+(e.message||e),'error');throw e;}};const[masteringSettings,setMasteringSettings]=useState({masterConnected:true,// ⚠️ FIX: bật ON mặc định — MIDI item/preview PHẢI được xử lí qua mastering FX ngay khi chạy app (không cần bật/tắt power)
|
||||
activeModule:null,imagerActive:false,maximizerActive:false,eqLowGain:1.5,eqMid1Gain:-1.0,eqMid2Gain:2.0,eqHighGain:1.8,w1:0,w2:115,w3:135,w4:150,imagerScale:'v2',maxGain:5.4,maxUpward:2.0,maxSoftClip:15,maxTransient:25,ceiling:-0.1,isBypassed:false,chain:[],compActive:false,compThreshold:-16,compRatio:3,compMakeup:0,limActive:false,limThreshold:-1.0,limMode:0,limLookahead:2,excActive:false,excDrive:40,rebalActive:false,rebalMid:0,rebalSide:0,vstFxChain:[],vstBypass:false});useEffect(()=>{window.currentMasteringSettings=masteringSettings;if(audioCtx&&masterBus){toggleMasteringOnMaster(masteringSettings.masterConnected,masteringSettings.isBypassed);applyMasteringSettings(masteringSettings);// Đồng bộ realtime VST master với PWR/Bypass: WebAudio graph đã đổi theo
|
||||
activeModule:null,imagerActive:false,maximizerActive:false,eqLowGain:1.5,eqMid1Gain:-1.0,eqMid2Gain:2.0,eqHighGain:1.8,w1:0,w2:115,w3:135,w4:150,imagerScale:'v2',maxGain:5.4,maxUpward:2.0,maxSoftClip:15,maxTransient:25,ceiling:-0.1,isBypassed:false,chain:[],compActive:false,compThreshold:-16,compRatio:3,compMakeup:0,limActive:false,limThreshold:-1.0,limMode:0,limLookahead:2,excActive:false,excDrive:40,rebalActive:false,rebalMid:0,rebalSide:0,vstFxChain:[],vstBypass:false});useEffect(()=>{window.currentMasteringSettings=masteringSettings;const _fxPrevAct=!!(fxRt&&fxRt.active);const _fxPrevWarm=!!(fxRt&&fxRt.warm);if(audioCtx&&masterBus){toggleMasteringOnMaster(masteringSettings.masterConnected,masteringSettings.isBypassed);applyMasteringSettings(masteringSettings);// Đồng bộ realtime VST master với PWR/Bypass: WebAudio graph đã đổi theo
|
||||
// masterConnected nhưng fxRt không tự dừng → tắt PWR mà âm không đổi.
|
||||
try{if(fxRtChain().length){fxRtStart();// V45.46: start từ settings-change lúc KHÔNG có playback (mount mở
|
||||
// project có master chain / chỉnh PWR khi đang dừng) → engine từng chạy
|
||||
@@ -1700,7 +1703,7 @@ const _hasVst=!!(masteringSettings.gui_open_path&&(masteringSettings.chain||[]).
|
||||
try{const _list=activeTracksRef.current&&activeTracksRef.current.length?activeTracksRef.current:tracks;_list.forEach(_t=>{const _n=activeTrackNodesRef.current[_t.id];if(_n){if(_n.sfRouteGain&&_n.sfDryGain){const _b=effMidiBypass(_t);_n.sfRouteGain.gain.value=_b?0:1;_n.sfDryGain.gain.value=_b?1:0;}if(_n.route&&_n.route.routeGain&&_n.route.dryGain){const _ab=routeBypassFor(_t);_n.route.routeGain.gain.value=_ab?0:1;_n.route.dryGain.gain.value=_ab?1:0;}}});// Sub-nodes (section tabs, key '<parent>_sub_<id>'): PWR master ON →
|
||||
// ép qua master chain; OFF → khôi phục theo nút A/♪ riêng của section
|
||||
// (route._bypass lưu trạng thái gốc lúc tạo/toggle).
|
||||
Object.keys(activeTrackNodesRef.current).forEach(_k=>{if(!_k.includes('_sub_'))return;const _sn=activeTrackNodesRef.current[_k];if(_sn&&_sn.route&&_sn.route.routeGain&&_sn.route.dryGain){const _sab=masteringChainOn()?false:!!_sn.route._bypass;_sn.route.routeGain.gain.value=_sab?0:1;_sn.route.dryGain.gain.value=_sab?1:0;}});updateSfRouting();}catch(e){}}try{if(_phArmed()&&window.__phSnapAll){window.__phSnapAll('ph_effect');_dbgLog('ph_effect','mc='+(masteringSettings.masterConnected?1:0)+' bp='+(masteringSettings.isBypassed?1:0)+' chain='+fxRtChain().length+' gui='+(masteringSettings.gui_open_path||'na'));}}catch(e){}},[masteringSettings]);const[pluginManagerModalOpen,setPluginManagerModalOpen]=useState(false);const[pluginsData,setPluginsData]=useState(null);// D10: bridge indicator + "Ép dùng WASM" debug toggle.
|
||||
Object.keys(activeTrackNodesRef.current).forEach(_k=>{if(!_k.includes('_sub_'))return;const _sn=activeTrackNodesRef.current[_k];if(_sn&&_sn.route&&_sn.route.routeGain&&_sn.route.dryGain){const _sab=masteringChainOn()?false:!!_sn.route._bypass;_sn.route.routeGain.gain.value=_sab?0:1;_sn.route.dryGain.gain.value=_sab?1:0;}});updateSfRouting();}catch(e){}}try{if(!_fxPrevAct&&_fxPrevWarm&&fxRt.active&&window.__fxRtWakeRebase)window.__fxRtWakeRebase();}catch(e){}try{if(_phArmed()&&window.__phSnapAll){window.__phSnapAll('ph_effect');_dbgLog('ph_effect','mc='+(masteringSettings.masterConnected?1:0)+' bp='+(masteringSettings.isBypassed?1:0)+' chain='+fxRtChain().length+' gui='+(masteringSettings.gui_open_path||'na'));}}catch(e){}},[masteringSettings]);const[pluginManagerModalOpen,setPluginManagerModalOpen]=useState(false);const[pluginsData,setPluginsData]=useState(null);// D10: bridge indicator + "Ép dùng WASM" debug toggle.
|
||||
const[bridgeUi,setBridgeUi]=useState({connected:false,forceWasm:false});React.useEffect(()=>{if(!window.NativeBridgeService)return;setBridgeUi(s=>({...s,connected:!!window.NativeBridgeService.isBridgeConnected}));window.NativeBridgeService.onStatusChange(function(st){// V8 bug 4: reset map NGAY DAU callback (ca connected lan !connected) —
|
||||
// Rust auto-restart KHONG emit bridge-down khi thanh cong → map cu true →
|
||||
// GUI click skip load → attach null → nen trang.
|
||||
@@ -1804,7 +1807,13 @@ const bridgeNoteEpochRef=useRef(0);// Resume main/session play khi rời PIANO R
|
||||
// đ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
|
||||
// rời tab resume startTrackPlayback(offset + elapsed thật).
|
||||
const mainResumeRef=useRef(null);currentTimeRef.current=currentTime;// M21 ph-diag hooks (Bug C — playhead nhay khi PWR master FX toggle)
|
||||
const mainResumeRef=useRef(null);currentTimeRef.current=currentTime;// M22 Bug C fix hook: rebase timeline khi wake warm-session (PWR ON giữa lúc
|
||||
// play) — engine processedSamples bị đóng băng suốt OFF-warm (worklet rời
|
||||
// graph) nên ps−anchor cũ tụt cả khoảng OFF; nếu không rebase playhead
|
||||
// engine-derived nhảy LÙI về vị trí trước OFF (ph_jump d âm lớn). Rebase:
|
||||
// dời startOffsetTimeRef/startAudioTimeRef về wall hiện tại + anchor=ps →
|
||||
// engine clock nối lại đúng chỗ, playhead liên tục (lệch ~latencyTotal).
|
||||
window.__fxRtWakeRebase=function(){try{if(!fxRt||!fxRt.active)return;const _c=getAudioContext();const _wall=(startOffsetTimeRef.current||0)+(_c.currentTime-(startAudioTimeRef.current||_c.currentTime));const _pa=typeof fxRt.processedSamples==='number'?fxRt.processedSamples:null;startOffsetTimeRef.current=_wall;startAudioTimeRef.current=_c.currentTime;if(_pa!==null)fxRt.processedAnchor=_pa;if(_fxRtSeekFreeze){_fxRtSeekFreeze=false;if(_fxRtSeekFreezeTimer){clearTimeout(_fxRtSeekFreezeTimer);_fxRtSeekFreezeTimer=null;}}try{_dbgLog('ph_rebase','wall='+_wall.toFixed(3)+' at='+startAudioTimeRef.current.toFixed(3)+' off='+startOffsetTimeRef.current.toFixed(3)+' ps='+(typeof fxRt.processedSamples==='number'?fxRt.processedSamples:'na')+' pa='+(typeof fxRt.processedAnchor==='number'?fxRt.processedAnchor:'na')+' lat='+(fxRt.latencyTotal||0));}catch(e){}}catch(e){try{_dbgLog('ph_rebase','err '+String(e));}catch(e2){}}};// M21 ph-diag hooks (Bug C — playhead nhay khi PWR master FX toggle)
|
||||
// __phArmPwr: user bam PWR OFF/ON -> arm cua so log 6s + snapshot ngay lap tuc.
|
||||
// __phSnapAll: snapshot refs/fxRt tai diem goi (chi log khi cua so dang mo).
|
||||
window.__phArmPwr=function(dir){try{_phDiagArmedUntil=Date.now()+6000;_dbgLog('ph_pwr','dir='+dir+' cur='+(currentTimeRef.current==null?'na':currentTimeRef.current.toFixed(3))+' off='+(startOffsetTimeRef.current==null?'na':startOffsetTimeRef.current.toFixed(3))+' at='+(startAudioTimeRef.current==null?'na':startAudioTimeRef.current.toFixed(3))+' ctx='+(getAudioContext().currentTime||0).toFixed(3)+' fx='+(fxRt?'act='+(fxRt.active?1:0)+' warm='+(fxRt.warm?1:0)+' start='+(fxRt.starting?1:0)+' ps='+(typeof fxRt.processedSamples==='number'?fxRt.processedSamples:'na')+' pa='+(typeof fxRt.processedAnchor==='number'?fxRt.processedAnchor:'na')+' lat='+(fxRt.latencyTotal||0)+' pdc='+(fxRt.pdcCalibMs||0)+' sid='+(fxRt.sessionId||'na'):'na')+' freeze='+(_fxRtSeekFreeze?1:0)+' snap='+(_fxRtPlayheadSnapPending?1:0)+' wh='+(wrapHoldRef.current?wrapHoldRef.current.end.toFixed(3)+'/'+wrapHoldRef.current.v0.toFixed(3):'na'));}catch(e){try{_dbgLog('ph_pwr','err '+String(e));}catch(e2){}}};window.__phSnapAll=function(tag){try{if(!_phArmed())return;_dbgLog(tag,'cur='+(currentTimeRef.current==null?'na':currentTimeRef.current.toFixed(3))+' off='+(startOffsetTimeRef.current==null?'na':startOffsetTimeRef.current.toFixed(3))+' at='+(startAudioTimeRef.current==null?'na':startAudioTimeRef.current.toFixed(3))+' ctx='+(getAudioContext().currentTime||0).toFixed(3)+' fx='+(fxRt?'act='+(fxRt.active?1:0)+' warm='+(fxRt.warm?1:0)+' start='+(fxRt.starting?1:0)+' ps='+(typeof fxRt.processedSamples==='number'?fxRt.processedSamples:'na')+' pa='+(typeof fxRt.processedAnchor==='number'?fxRt.processedAnchor:'na')+' lat='+(fxRt.latencyTotal||0)+' pdc='+(fxRt.pdcCalibMs||0)+' sid='+(fxRt.sessionId||'na'):'na')+' freeze='+(_fxRtSeekFreeze?1:0)+' snap='+(_fxRtPlayheadSnapPending?1:0)+' wh='+(wrapHoldRef.current?wrapHoldRef.current.end.toFixed(3)+'/'+wrapHoldRef.current.v0.toFixed(3):'na'));}catch(e){try{_dbgLog(tag,'err '+String(e));}catch(e2){}}};// ── Keyboard Shortcuts ──
|
||||
@@ -2190,7 +2199,7 @@ fxRtTrackReconcile(trackId,node,track);}catch(e){console.warn('rebuildTrackFxGra
|
||||
// Cần activeTrackNodesRef/isPlayingRef. Session giống master (fxRtStart):
|
||||
// fetch start → AudioWorkletNode 'sf-fx-realtime' → WS; WS open → splice
|
||||
// worklet vào chain hiện tại (đọc mods MỚI NHẤT — an toàn với rebuild).
|
||||
const fxRtTrackStart=async(trackId,track,path)=>{const key=trackId+':'+path;const chain=fxRtTrackChain(track);if(!chain.length)return;if(fxRtTracks[key]){if(!fxRtTracks[key].node||fxRtTracks[key].node.context!==getAudioContext())fxRtTrackStop(trackId,path,true);else return;}const ctx=getAudioContext();if(!ctx||!ctx.audioWorklet)return;const chainPayload=chain.map(s=>s.type==='vst3'?{type:'vst3',path:s.path,name:s.name||s.path,preset_b64:s.preset_b64||'',bypass:!!s.bypass||!!(track&&track.vstFxBypass)}:{type:'builtin',id:s.type,params:s.params&&Object.keys(s.params).length?s.params:{},bypass:!!s.bypass});let st;try{st=await window.SonicAPI.fxRealtimeStart({fx_chain:chainPayload,sample_rate:ctx.sampleRate||44100});}catch(e){console.warn('[FX RT track] start fail',trackId,path,e);return;}const entry={sessionId:st.session_id,ws:null,node:null,ready:false,path,spliced:null,chainKey:fxRtTrackChainKey(chain)+'|vb:'+(track&&track.vstFxBypass?'1':'0'),paramsBySlot:chain.map(s=>fxRtEntryParams(s,null)),inQueue:[],pumpTimer:null,lastInSent:0,flushing:false,flushTimer:null};fxRtTracks[key]=entry;fxRtGuiPushShm();try{await ctx.audioWorklet.addModule(`${API_BASE_URL}/static/js/sf-fx-realtime.js?v=202609071004`);const node=new AudioWorkletNode(ctx,'sf-fx-realtime',{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[2]});entry.node=node;const u=new URL(API_BASE_URL);const wsProto=u.protocol==='https:'?'wss://':'ws://';// WS to 'localhost' bi tre ~300ms (Chromium thu ::1 truoc roi fallback) - dung 127.0.0.1
|
||||
const fxRtTrackStart=async(trackId,track,path)=>{const key=trackId+':'+path;const chain=fxRtTrackChain(track);if(!chain.length)return;if(fxRtTracks[key]){if(!fxRtTracks[key].node||fxRtTracks[key].node.context!==getAudioContext())fxRtTrackStop(trackId,path,true);else return;}const ctx=getAudioContext();if(!ctx||!ctx.audioWorklet)return;const chainPayload=chain.map(s=>s.type==='vst3'?{type:'vst3',path:s.path,name:s.name||s.path,preset_b64:s.preset_b64||'',bypass:!!s.bypass||!!(track&&track.vstFxBypass)}:{type:'builtin',id:s.type,params:s.params&&Object.keys(s.params).length?s.params:{},bypass:!!s.bypass});let st;try{st=await window.SonicAPI.fxRealtimeStart({fx_chain:chainPayload,sample_rate:ctx.sampleRate||44100});}catch(e){console.warn('[FX RT track] start fail',trackId,path,e);return;}const entry={sessionId:st.session_id,ws:null,node:null,ready:false,path,spliced:null,chainKey:fxRtTrackChainKey(chain)+'|vb:'+(track&&track.vstFxBypass?'1':'0'),paramsBySlot:chain.map(s=>fxRtEntryParams(s,null)),inQueue:[],pumpTimer:null,lastInSent:0,flushing:false,flushTimer:null};fxRtTracks[key]=entry;fxRtGuiPushShm();try{await ctx.audioWorklet.addModule(`${API_BASE_URL}/static/js/sf-fx-realtime.js?v=202609071038`);const node=new AudioWorkletNode(ctx,'sf-fx-realtime',{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[2]});entry.node=node;const u=new URL(API_BASE_URL);const wsProto=u.protocol==='https:'?'wss://':'ws://';// WS to 'localhost' bi tre ~300ms (Chromium thu ::1 truoc roi fallback) - dung 127.0.0.1
|
||||
const wsHost=u.hostname==='localhost'||u.hostname==='::1'?'127.0.0.1'+(u.port?':'+u.port:''):u.host;const tok=localStorage.getItem('sonic_token')||'';const ws=new WebSocket(`${wsProto}${wsHost}${st.ws_url}${tok?'?token='+encodeURIComponent(tok):''}`);entry.ws=ws;ws.binaryType='arraybuffer';ws.onopen=()=>{entry.ready=true;entry.inQueue=[];// bỏ audio stale trước khi WS open — hết lag khởi đầu
|
||||
fxRtTrackSplice(trackId,path);try{(entry.paramsBySlot||[]).forEach((pl,slot)=>pl.forEach(p=>ws.send(JSON.stringify({cmd:'set_param',slot,key:p.key,value:p.value}))));}catch(e){}};ws.onmessage=ev=>{if(typeof ev.data==='string'){try{const d=JSON.parse(ev.data);if(d&&d.cmd==='flush_done'){entry.flushing=false;if(entry.flushTimer){clearTimeout(entry.flushTimer);entry.flushTimer=null;}if(entry.node)entry.node.port.postMessage({type:'flush_done'});}}catch(e){}return;}if(!(ev.data instanceof ArrayBuffer)||!entry.node)return;if(entry.flushing)return;// V24: output stale trong luc flush — drop
|
||||
const f=new Float32Array(ev.data);const half=f.length/2;const l=new Float32Array(half);const r=new Float32Array(half);for(let i=0;i<half;i++){l[i]=f[i*2];r[i]=f[i*2+1];}entry.node.port.postMessage({type:'out',l,r});};ws.onclose=()=>fxRtTrackStop(trackId,path,true);ws.onerror=()=>{try{ws.close();}catch(e){}};entry.node.port.onmessage=ev=>{const d=ev.data;if(!d||!entry.ws)return;if(d.type==='flush'){// V24: nhu master — worklet underrun-resume → flush engine ring
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<script src="/static/js/services/promptTemplateManager.js?v=202608290945"></script>
|
||||
<script src="/static/js/services/undoRedoEngine.js?v=202608290945"></script>
|
||||
<script src="/static/js/services/chordTheory.js?v=202608290945"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202609070903202609070853202609070713202609071004" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202609070903202609070853202609070713202609071004202609071038" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202608290945">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user