FIX: Piano roll tab không xuất âm thanh qua mastering chain
This commit is contained in:
+32
-4
@@ -17734,16 +17734,22 @@ const App = () => {
|
||||
// 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).
|
||||
// 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 (!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 thật đang trong khoảng 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 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) {
|
||||
@@ -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ũ 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);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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) {}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user