FIX: Piano roll tab không xuất âm thanh qua mastering chain

This commit is contained in:
2026-08-05 12:10:50 +07:00
parent cfc114b9d7
commit 55d3464b1e
5 changed files with 64 additions and 13 deletions
+32 -4
View File
@@ -17734,16 +17734,22 @@ const App = () => {
// s rebuild loop + panic hy notes ch CÂM TOÀN CC exempt hoàn toàn
// (các fix setValueAtTime/NaN guard đã hết "state is bad" watchdog ch
// còn là lp cu cui cho main/audio-tab).
// PIANO_ROLL: watchdog CH rebuild khi output cha NaN (chain chết
// state-bad). KHÔNG rebuild khi im lng thưng (rests t nhiên gia 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 (!isPianoRoll && (isPlaying || _anySubPlaying) && masterBus && masterBus.analyser && activeSourcesRef.current.length > 0) {
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 tht đang trong khong phát.
// - PIANO_ROLL: tab đang play (notes đã schedule) = đáng l có âm.
const ctxNow = getAudioContext().currentTime;
let anyPlaying = false;
if (_anySubPlaying) {
if (isPianoRoll) {
anyPlaying = _anySubPlaying;
} else if (_anySubPlaying) {
const _subs = subTabsRef.current || [];
for (let _si = 0; _si < _subs.length; _si++) {
const s = _subs[_si];
@@ -17760,7 +17766,18 @@ const App = () => {
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) {
// PIANO_ROLL: chain chết xut 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) {
@@ -17772,7 +17789,18 @@ const App = () => {
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); }
// Tháo chain cũ khi destination ri rebuild THT initMasterBus
// early-return khi masterBus còn tn ti recovery trưc đây là
// no-op chain "state is bad" b STUCK vĩnh vin.
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 hin ti (main hoc sub-tab piano roll)
const curTab = activeTabRef.current;
const subSt = subTabsRef.current.find(s => s.id === curTab);
File diff suppressed because one or more lines are too long
+16 -2
View File
@@ -191,17 +191,31 @@
var spn = _audioCtx.createScriptProcessor(spBufSz, 0, 2);
var lp = _fluidModule._malloc(spBufSz * 4);
var rp = _fluidModule._malloc(spBufSz * 4);
// Heap WASM có thể realloc khi load SoundFont lớn (SGM-V2.01
// ~300MB) → lp/rp DANGLE → đọc vùng nhớ đã free → NaN/garbage
// → master chain "state is bad" → CÂM + stuck. Theo dõi
// buffer + re-malloc khi đổi.
var _heapBufRef = _fluidModule.HEAPU8.buffer;
spn.onaudioprocess = function (e) {
var left = e.outputBuffer.getChannelData(0);
var right = e.outputBuffer.getChannelData(1);
var sz = left.length;
try {
if (_fluidModule.HEAPU8.buffer !== _heapBufRef) {
try { _fluidModule._free(lp); _fluidModule._free(rp); } catch (er2) {}
lp = _fluidModule._malloc(sz * 4);
rp = _fluidModule._malloc(sz * 4);
_heapBufRef = _fluidModule.HEAPU8.buffer;
}
_fluidModule._fluid_synth_write_float(_synthPtr, sz, lp, 0, 1, rp, 0, 1);
var hf = _fluidModule.HEAPF32;
var lpb = lp >> 2, rpb = rp >> 2;
for (var si = 0; si < sz; si++) {
left[si] = hf[lpb + si];
right[si] = hf[rpb + si];
// NaN sweep: mẫu NaN/Inf → 0 (chain biquad
// KHÔNG BAO GIỜ được nhận NaN → không state-bad).
var L = hf[lpb + si], R = hf[rpb + si];
left[si] = isFinite(L) ? L : 0;
right[si] = isFinite(R) ? R : 0;
}
} catch (er) {}
};