fix: bật AudioWorklet hết deprecation ScriptProcessor + signature guard applyMasteringSettings hết BiquadFilterNode state is bad

This commit is contained in:
2026-08-03 12:11:11 +07:00
parent 71278f2aba
commit 36edc9daca
5 changed files with 30 additions and 11 deletions
+6 -2
View File
@@ -10,7 +10,11 @@ const trackMidiChannelsRef={current:{}};const ensureTrackMidiChannel=(track,trac
(function handleSfsDeepLink(){try{const params=new URLSearchParams(window.location.search);const sfsParam=params.get('sfs');if(!sfsParam)return;const decoded=JSON.parse(decodeURIComponent(sfsParam));window.__pendingSfsProject=decoded;// consumed after auth in App
if(window.history.replaceState){window.history.replaceState({},document.title,window.location.pathname);}}catch(e){window.__pendingSfsProject=null;}})();// Storage for server-side file IDs mapped to track IDs
let serverFileIdMap={};let audioCtx;let masterBus=null;// { input, compressor, analyser, output, masteringActive }
function makeDistortionCurve(k){const n_samples=44100;const curve=new Float32Array(n_samples);for(let i=0;i<n_samples;++i){const x=i*2/n_samples-1;curve[i]=Math.atan(x*k)/(Math.atan(k)||1);}return curve;}function applyMasteringSettings(s){if(!masterBus||!audioCtx||!s)return;const now=audioCtx.currentTime;// Clamp every parameter so a stale/incomplete settings object can never push
function makeDistortionCurve(k){const n_samples=44100;const curve=new Float32Array(n_samples);for(let i=0;i<n_samples;++i){const x=i*2/n_samples-1;curve[i]=Math.atan(x*k)/(Math.atan(k)||1);}return curve;}function applyMasteringSettings(s){if(!masterBus||!audioCtx||!s)return;// getAudioContext() invokes this on EVERY call (stopAll, play, VU, …).
// Re-applying identical values in rapid bursts is "fast parameter automation"
// and makes Chromium flag the biquad EQ filters as unstable ("state is bad").
// Only touch the graph when a value actually changed.
const sig=[s.eqActive,s.eqLowGain,s.eqMid1Gain,s.eqMid2Gain,s.eqHighGain,s.imagerActive,s.w1,s.w2,s.w3,s.w4,s.maximizerActive,s.maxGain,s.maxSoftClip,s.maxUpward,s.ceiling].join('|');if(sig===_lastMasteringSig)return;_lastMasteringSig=sig;const now=audioCtx.currentTime;// Clamp every parameter so a stale/incomplete settings object can never push
// NaN or an extreme value into the biquad filters — that puts the master
// chain into a bad state and silences ALL audio (the "mất soundfont" symptom).
const clamp=(v,lo,hi)=>{const n=Number(v);if(!isFinite(n))return 0;// missing/NaN → neutral, never a filter-breaking value
@@ -38,7 +42,7 @@ eqLowFilter.connect(eqMid1Filter);eqMid1Filter.connect(eqMid2Filter);eqMid2Filte
eqHighFilter.connect(imagerInput);// Connect Imager to Maximizer
imagerOutput.connect(maximizerBoostGain);// Setup default non-mastered routing:
// input -> compressor -> inputAnalyser -> outputAnalyser -> output -> analyser -> destination
masterBus.input.connect(masterBus.compressor);masterBus.compressor.connect(masterBus.inputAnalyser);masterBus.inputAnalyser.connect(masterBus.outputAnalyser);masterBus.outputAnalyser.connect(masterBus.output);masterBus.output.connect(masterBus.analyser);masterBus.analyser.connect(ctx.destination);window.masterBus=masterBus;return masterBus;}function setMasterVolume(linear){if(masterBus)masterBus.output.gain.setValueAtTime(linear,audioCtx.currentTime);}let _lastMasteringActive=null;function toggleMasteringOnMaster(activate,isBypassed){if(!masterBus)return;const active=!!(activate&&!isBypassed);// Idempotent: don't disconnect/reconnect the mastering chain on every call
masterBus.input.connect(masterBus.compressor);masterBus.compressor.connect(masterBus.inputAnalyser);masterBus.inputAnalyser.connect(masterBus.outputAnalyser);masterBus.outputAnalyser.connect(masterBus.output);masterBus.output.connect(masterBus.analyser);masterBus.analyser.connect(ctx.destination);window.masterBus=masterBus;return masterBus;}function setMasterVolume(linear){if(masterBus)masterBus.output.gain.setValueAtTime(linear,audioCtx.currentTime);}let _lastMasteringActive=null;let _lastMasteringSig=null;function toggleMasteringOnMaster(activate,isBypassed){if(!masterBus)return;const active=!!(activate&&!isBypassed);// Idempotent: don't disconnect/reconnect the mastering chain on every call
// (getAudioContext invokes this constantly). Rapid re-connection while audio
// flows destabilizes the biquad filters → "BiquadFilterNode: state is bad".
if(_lastMasteringActive===active&&masterBus.masteringActive===active)return;_lastMasteringActive=active;// Disconnect the dynamic junction