FIX: Piano roll tab không xuất âm thanh qua mastering chain được recovery nhưng âm thanh rất nhỏ
This commit is contained in:
@@ -742,9 +742,19 @@ const curTracks=activeTracksRef.current||activeTracks;const soloed=curTracks.som
|
||||
// sẽ rebuild loop + panic hủy notes chờ → CÂM TOÀN CỤC → exempt hoàn toàn
|
||||
// (các fix setValueAtTime/NaN guard đã hết "state is bad" — watchdog chỉ
|
||||
// còn là lớp cứu cuối cho main/audio-tab).
|
||||
const _anySubPlaying=subTabsRef.current.some(s=>s.isPlaying);const activeSub=subTabsRef.current.find(s=>s.id===activeTabRef.current);const isPianoRoll=activeSub&&activeSub.type==='PIANO_ROLL';if(!isPianoRoll&&(isPlaying||_anySubPlaying)&&masterBus&&masterBus.analyser&&activeSourcesRef.current.length>0){try{// "Đáng lẽ đang có âm" — quyết định watchdog có được rebuild không:
|
||||
// PIANO_ROLL: watchdog CHỈ rebuild khi output chứa NaN (chain chết —
|
||||
// state-bad). KHÔNG rebuild khi im lặng thường (rests tự nhiên giữa các
|
||||
// note — false-positive = stopAllPlayback + reschedule = glitch).
|
||||
const _anySubPlaying=subTabsRef.current.some(s=>s.isPlaying);const activeSub=subTabsRef.current.find(s=>s.id===activeTabRef.current);const isPianoRoll=activeSub&&activeSub.type==='PIANO_ROLL';if((isPlaying||_anySubPlaying)&&masterBus&&masterBus.analyser&&(isPianoRoll||activeSourcesRef.current.length>0)){try{// "Đáng lẽ đang có âm" — quyết định watchdog có được rebuild không:
|
||||
// - Main / sub-tab audio: source thật đang trong khoảng phát.
|
||||
const ctxNow=getAudioContext().currentTime;let anyPlaying=false;if(_anySubPlaying){const _subs=subTabsRef.current||[];for(let _si=0;_si<_subs.length;_si++){const s=_subs[_si];if(!s.isPlaying)continue;if(s.buffer){if(activeSourcesRef.current.some(src=>typeof src.startTime==='number'&&ctxNow>=src.startTime&&ctxNow<=src.startTime+(src.buffer?src.buffer.duration:0)+0.1)){anyPlaying=true;break;}}}}else{anyPlaying=activeSourcesRef.current.some(s=>typeof s.startTime==='number'&&ctxNow>=s.startTime&&ctxNow<=s.startTime+(s.buffer?s.buffer.duration:0)+0.1);}if(anyPlaying){const d=new Uint8Array(128);masterBus.analyser.getByteTimeDomainData(d);let pk=0;for(let i=0;i<d.length;i++){const v=Math.abs(d[i]-128)/128;if(v>pk)pk=v;}if(pk<0.001){masterSilenceFramesRef.current++;const sinceRebuild=performance.now()-(lastMasterRebuildTimeRef.current||0);if(masterSilenceFramesRef.current>45&&sinceRebuild>3000){masterSilenceFramesRef.current=0;lastMasterRebuildTimeRef.current=performance.now();console.warn('[Recovery] Master silent while sources active — rebuilding audio graph');try{const pt=currentTimeRef.current;stopAllPlayback();Object.keys(activeTrackNodesRef.current).forEach(k=>{const n=activeTrackNodesRef.current[k];try{if(n&&n.gainNode&&n.gainNode.disconnect)n.gainNode.disconnect();}catch(e){}});activeTrackNodesRef.current={};try{initMasterBus(getAudioContext());}catch(e){console.warn('[Recovery] initMasterBus error:',e);}// Resume ĐÚNG chế độ play hiện tại (main hoặc sub-tab piano roll)
|
||||
// - PIANO_ROLL: tab đang play (notes đã schedule) = đáng lẽ có âm.
|
||||
const ctxNow=getAudioContext().currentTime;let anyPlaying=false;if(isPianoRoll){anyPlaying=_anySubPlaying;}else if(_anySubPlaying){const _subs=subTabsRef.current||[];for(let _si=0;_si<_subs.length;_si++){const s=_subs[_si];if(!s.isPlaying)continue;if(s.buffer){if(activeSourcesRef.current.some(src=>typeof src.startTime==='number'&&ctxNow>=src.startTime&&ctxNow<=src.startTime+(src.buffer?src.buffer.duration:0)+0.1)){anyPlaying=true;break;}}}}else{anyPlaying=activeSourcesRef.current.some(s=>typeof s.startTime==='number'&&ctxNow>=s.startTime&&ctxNow<=s.startTime+(s.buffer?s.buffer.duration:0)+0.1);}if(anyPlaying){const d=new Uint8Array(128);masterBus.analyser.getByteTimeDomainData(d);let pk=0;for(let i=0;i<d.length;i++){const v=Math.abs(d[i]-128)/128;if(v>pk)pk=v;}// PIANO_ROLL: chain chết xuất NaN → getByteTimeDomainData đọc NaN →
|
||||
// byte 0/128 → pk CAO → mù. Check float data (chỉ piano roll — rests
|
||||
// tự nhiên thì KHÔNG rebuild).
|
||||
let nanOut=false;if(isPianoRoll){try{const f=new Float32Array(128);masterBus.analyser.getFloatTimeDomainData(f);for(let i=0;i<f.length;i++){if(!isFinite(f[i])){nanOut=true;break;}}}catch(e){}}if(pk<0.001||isPianoRoll&&nanOut){masterSilenceFramesRef.current++;const sinceRebuild=performance.now()-(lastMasterRebuildTimeRef.current||0);if(masterSilenceFramesRef.current>45&&sinceRebuild>3000){masterSilenceFramesRef.current=0;lastMasterRebuildTimeRef.current=performance.now();console.warn('[Recovery] Master silent while sources active — rebuilding audio graph');try{const pt=currentTimeRef.current;stopAllPlayback();Object.keys(activeTrackNodesRef.current).forEach(k=>{const n=activeTrackNodesRef.current[k];try{if(n&&n.gainNode&&n.gainNode.disconnect)n.gainNode.disconnect();}catch(e){}});activeTrackNodesRef.current={};// Tháo chain cũ khỏi destination rồi rebuild THẬT — initMasterBus
|
||||
// early-return khi masterBus còn tồn tại → recovery trước đây là
|
||||
// no-op → chain "state is bad" bị STUCK vĩnh viễn.
|
||||
try{if(masterBus){try{if(masterBus.analyser)masterBus.analyser.disconnect();}catch(e){}try{if(masterBus.output)masterBus.output.disconnect();}catch(e){}try{if(masterBus.dryOutput)masterBus.dryOutput.disconnect();}catch(e){}}masterBus=null;initMasterBus(getAudioContext());}catch(e){console.warn('[Recovery] initMasterBus error:',e);}// Resume ĐÚNG chế độ play hiện tại (main hoặc sub-tab piano roll)
|
||||
const curTab=activeTabRef.current;const subSt=subTabsRef.current.find(s=>s.id===curTab);if(subSt&&(subSt.type==='PIANO_ROLL'||subSt.buffer)){const resumeAt=subSt.currentTime||0;if(subSt.type==='PIANO_ROLL')schedulePianoRollMidi(subSt,resumeAt);startSubTabPlayback(subSt,resumeAt);setSubTabs(prev=>prev.map(s=>s.id===curTab?{...s,buffer:s.buffer||getAudioContext().createBuffer(1,128,getAudioContext().sampleRate),isPlaying:true,currentTime:resumeAt}:s));animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);}else{setIsPlaying(true);startTrackPlayback(pt);}}catch(e){console.warn('[Recovery] rebuild error:',e);}}}else{masterSilenceFramesRef.current=0;}}else{masterSilenceFramesRef.current=0;}}catch(e){masterSilenceFramesRef.current=0;}}else{masterSilenceFramesRef.current=0;}if(recordingStateRef.current==='RECORDING'){const audioCtx=getAudioContext();const lookahead=0.1;// 100ms
|
||||
const secondsPerBeat=60.0/(parseInt(bpmRef.current)||120);// Metronome Click Scheduler
|
||||
while(true){const beatNum=nextMetronomeBeatRef.current;const elapsedBeats=beatNum-recordingStartTimeRef.current/secondsPerBeat;const beatTime=startAudioTimeRef.current+elapsedBeats*secondsPerBeat;if(beatTime<audioCtx.currentTime+lookahead){const isDownbeat=beatNum%4===0;playMetronomeClick(beatTime,isDownbeat);nextMetronomeBeatRef.current++;}else{break;}}// Throttled Live Preview compilation (every 200ms)
|
||||
|
||||
Reference in New Issue
Block a user