fix(masterfx): merge captured preset vao realtime chain

This commit is contained in:
2026-08-23 16:47:35 +07:00
parent 6176cf3f8e
commit 2ad833fbe7
3 changed files with 20 additions and 5 deletions
+10 -2
View File
@@ -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('|');
+9 -2
View File
@@ -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).
+1 -1
View File
@@ -51,7 +51,7 @@
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/services/chordTheory.js?v=202608211300"></script>
<script src="/static/js/app.precompiled.js?v=202608212401" defer></script>
<script src="/static/js/app.precompiled.js?v=202608212402" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {