From 2ad833fbe7b5ea324db9e5bc39927796b0ce8ed9 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Sun, 23 Aug 2026 16:47:35 +0700 Subject: [PATCH] fix(masterfx): merge captured preset vao realtime chain --- app/static/js/app.jsx | 12 ++++++++++-- app/static/js/app.precompiled.js | 11 +++++++++-- app/templates/index.html | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 165539a..a2e17ad 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -1642,7 +1642,12 @@ function fxRtChain() { const ms = window.currentMasteringSettings; if (!ms || !ms.masterConnected || ms.isBypassed) return []; // Chain hợp nhất (builtin + vst3 theo UI order) — mọi slot active gửi bridge. - return (ms.chain || []).filter(s => s && s.active !== false && (s.type !== 'vst3' || (!ms.vstBypass && s.path && !s.bypass))); + // preset_b64: merge preset GUI mới nhất (_capturedPresetByPath) — nếu chỉ dùng + // s.preset_b64 thì preset vừa chỉnh trong GUI chưa kịp persist → bridge load + // default (passthrough) → power ON nghe như OFF. Merge ở đây làm chainKey đổi + // → fxRtStart tự restart session với preset thật. + return (ms.chain || []).filter(s => s && s.active !== false && (s.type !== 'vst3' || (!ms.vstBypass && s.path && !s.bypass))) + .map(s => s.type === 'vst3' && _capturedPresetByPath[s.path] ? { ...s, preset_b64: _capturedPresetByPath[s.path] } : s); } // Param master chain entry → C++ BuiltinFx key (mirror Python _BUILTIN_PARAM_MAP). const FX_RT_PARAM_MAP = { @@ -1863,7 +1868,10 @@ function fxRtTrackChain(track) { if (!chain.some(s => s && s.type === 'vst3') && !track.vstFxBypass) { chain = chain.concat(activeVstChain(track.vstFxChain || [])); } - return chain; + // preset_b64: merge preset GUI mới nhất (giống fxRtChain master) — nếu chỉ + // dùng s.preset_b64 thì preset chỉnh trong GUI chưa kịp persist → bridge load + // default (passthrough). chainKey đổi → fxRtTrackStart tự restart session. + return chain.map(s => s.type === 'vst3' && _capturedPresetByPath[s.path] ? { ...s, preset_b64: _capturedPresetByPath[s.path] } : s); } function fxRtTrackChainKey(chain) { return (chain || []).map(s => (s.type || '') + ':' + (s.type === 'vst3' ? (s.path || '') + '#' + ((s.preset_b64 || '').slice(0, 16)) : (s.type || ''))).join('|'); diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 6730db3..4eed221 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -277,7 +277,11 @@ const fxRt={active:false,sessionId:null,ws:null,node:null,inQueue:[],pumpTimer:n // reorder VST) → restart session với chain mới (fxRtStart early-return khi // active, nếu không so chainKey thì VST mới insert không bao giờ được áp). function fxRtChainKey(chain){return(chain||[]).map(s=>(s.type||'')+':'+(s.type==='vst3'?(s.path||'')+'#'+(s.preset_b64||'').slice(0,16):s.type||'')).join('|');}function fxRtChain(){const ms=window.currentMasteringSettings;if(!ms||!ms.masterConnected||ms.isBypassed)return[];// Chain hợp nhất (builtin + vst3 theo UI order) — mọi slot active gửi bridge. -return(ms.chain||[]).filter(s=>s&&s.active!==false&&(s.type!=='vst3'||!ms.vstBypass&&s.path&&!s.bypass));}// Param master chain entry → C++ BuiltinFx key (mirror Python _BUILTIN_PARAM_MAP). +// preset_b64: merge preset GUI mới nhất (_capturedPresetByPath) — nếu chỉ dùng +// s.preset_b64 thì preset vừa chỉnh trong GUI chưa kịp persist → bridge load +// default (passthrough) → power ON nghe như OFF. Merge ở đây làm chainKey đổi +// → fxRtStart tự restart session với preset thật. +return(ms.chain||[]).filter(s=>s&&s.active!==false&&(s.type!=='vst3'||!ms.vstBypass&&s.path&&!s.bypass)).map(s=>s.type==='vst3'&&_capturedPresetByPath[s.path]?{...s,preset_b64:_capturedPresetByPath[s.path]}:s);}// Param master chain entry → C++ BuiltinFx key (mirror Python _BUILTIN_PARAM_MAP). const FX_RT_PARAM_MAP={eq:[['g1','eqLowGain'],['g2','eqMid1Gain'],['g3','eqMid2Gain'],['g4','eqHighGain']],compressor:[['threshold','compThreshold'],['ratio','compRatio'],['makeup','compMakeup']],limiter:[['ceiling','limThreshold'],['mode','limMode'],['lookahead_ms','limLookahead']],exciter:[['drive','excDrive']],rebalance:[['mid','rebalMid'],['side','rebalSide']],imager:[['w1','w1'],['w2','w2'],['w3','w3'],['w4','w4']],maximizer:[['boost_db','maxGain'],['soft_clip','maxSoftClip'],['upward','maxUpward'],['ceiling_db','ceiling']]};function fxRtEntryParams(entry,ms){const out=[];if(!entry||entry.type==='vst3')return out;const src=ms||window.currentMasteringSettings||{};const push=(k,v)=>{const n=Number(v);if(isFinite(n))out.push({key:k,value:n});};const map=FX_RT_PARAM_MAP[entry.type]||[];map.forEach(([cppKey,jsKey])=>{if(entry.params&&entry.params[cppKey]!==undefined)push(cppKey,entry.params[cppKey]);else if(entry.params&&entry.params[jsKey]!==undefined)push(jsKey,entry.params[jsKey]);else if(src[jsKey]!==undefined)push(cppKey,src[jsKey]);});if(entry.type==='eqpro'){if(entry.params&&entry.params.amount!==undefined)push('amount',entry.params.amount);const bands=entry.params&&entry.params.bands||[];bands.forEach((b,i)=>{['freq','gain','q'].forEach(k=>{if(b&&b[k]!==undefined)push('band'+i+'_'+k,b[k]);});});}return out;}function fxRtPushAllParams(){if(!fxRt||!fxRt.ws||fxRt.ws.readyState!==1)return;const chain=fxRtChain();chain.forEach((s,i)=>{fxRtEntryParams(s,null).forEach(p=>{try{fxRt.ws.send(JSON.stringify({cmd:'set_param',slot:i,key:p.key,value:p.value}));}catch(e){}});});}async function fxRtStart(){const chain=fxRtChain();const ck=fxRtChainKey(chain);if(fxRt.active){// masterBus bị rebuild (recovery/export) → node thuộc graph cũ → restart. if(!fxRt.node||fxRt.node.context!==getAudioContext())fxRtStop(true);// Chain VST thay đổi (insert/remove/bypass/reorder trong MASTER FX CHAIN) // → stop + start lại để bridge load đúng chuỗi FX mới. @@ -314,7 +318,10 @@ const fxRtTracks={};// 'trackId:audio|sf' → { sessionId, ws, node, ready, path // url → trackId|null (null = master GUI). The GUI process peeks the session's // input ring so its meters move with real audio; push on every start/stop. const _fxGuiEmbedSources={};function _fxGuiRegisterSource(url,trackId){if(!url)return;_fxGuiEmbedSources[url]=trackId===undefined?null:trackId;}function _fxGuiUnregisterSource(url){if(url)delete _fxGuiEmbedSources[url];}function fxRtTrackShmName(trackId){const key=trackId?Object.keys(fxRtTracks).find(k=>k.startsWith(trackId+':')):'';return key?fxRtTracks[key].sessionId||'':'';}function fxRtGuiPushShm(){Object.keys(_fxGuiEmbedSources).forEach(url=>{const trackId=_fxGuiEmbedSources[url];const name=trackId===null?fxRt.sessionId||'':fxRtTrackShmName(trackId);try{fetch(url+'/shm',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({shm:name})}).catch(()=>{});}catch(e){}});}function fxRtTrackChain(track){if(!track)return[];let chain=(track.fxChain||[]).filter(s=>s&&s.active!==false&&(s.type!=='vst3'||!track.vstFxBypass&&s.path&&!s.bypass));// Legacy: project cũ chỉ có vstFxChain riêng — append sau builtin. -if(!chain.some(s=>s&&s.type==='vst3')&&!track.vstFxBypass){chain=chain.concat(activeVstChain(track.vstFxChain||[]));}return chain;}function fxRtTrackChainKey(chain){return(chain||[]).map(s=>(s.type||'')+':'+(s.type==='vst3'?(s.path||'')+'#'+(s.preset_b64||'').slice(0,16):s.type||'')).join('|');}function fxRtTrackPaths(track){const paths=[];if(track&&(track.clips&&track.clips.length>0||!!track.buffer||track.sections&&track.sections.length>0))paths.push('audio');if(track&&(track.midiItems&&track.midiItems.length>0||track.type==='MIDI'||!!track.instrumentId))paths.push('sf');return paths;}// Track-EQ preset library (unified_fx_rack_panel.md §III.1) — band gains only. +if(!chain.some(s=>s&&s.type==='vst3')&&!track.vstFxBypass){chain=chain.concat(activeVstChain(track.vstFxChain||[]));}// preset_b64: merge preset GUI mới nhất (giống fxRtChain master) — nếu chỉ +// dùng s.preset_b64 thì preset chỉnh trong GUI chưa kịp persist → bridge load +// default (passthrough). chainKey đổi → fxRtTrackStart tự restart session. +return chain.map(s=>s.type==='vst3'&&_capturedPresetByPath[s.path]?{...s,preset_b64:_capturedPresetByPath[s.path]}:s);}function fxRtTrackChainKey(chain){return(chain||[]).map(s=>(s.type||'')+':'+(s.type==='vst3'?(s.path||'')+'#'+(s.preset_b64||'').slice(0,16):s.type||'')).join('|');}function fxRtTrackPaths(track){const paths=[];if(track&&(track.clips&&track.clips.length>0||!!track.buffer||track.sections&&track.sections.length>0))paths.push('audio');if(track&&(track.midiItems&&track.midiItems.length>0||track.type==='MIDI'||!!track.instrumentId))paths.push('sf');return paths;}// Track-EQ preset library (unified_fx_rack_panel.md §III.1) — band gains only. const TRACK_EQ_PRESETS={flat:{name:'Flat / Reset',g:[0,0,0,0]},vocal_clarity:{name:'Vocal Unmask & Clarity',g:[-2.5,-1.8,3.2,2.0]},bass_punch:{name:'EDM Low-End Punch',g:[4.0,-3.0,1.5,1.0]},warm_tape:{name:'Warm Vintage Analog',g:[2.0,1.0,-2.0,-3.0]},guitar_edge:{name:'Guitar Cut & Presence',g:[-3.0,-2.0,3.5,1.5]}};// Dynamic signal-chain reconstruction (mastering_expand.md §II.3): // disconnect every module boundary, then wire the ACTIVE modules in series // between inputAnalyser (chain input) and outputAnalyser (chain output). diff --git a/app/templates/index.html b/app/templates/index.html index 0ea6e6a..a761490 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -51,7 +51,7 @@ - +