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) {}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script src="/static/js/services/audioEngine.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/storage.js?v=202608038200"></script>
|
||||
<script src="/static/js/services/soundfontStorage.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202608042158"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202608052230"></script>
|
||||
<script src="/static/js/services/aiGateway.js?v=202608037200"></script>
|
||||
<script src="/static/js/services/dawCommandDispatcher.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/pianoRollTabService.js?v=202607272044"></script>
|
||||
@@ -24,7 +24,7 @@
|
||||
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
|
||||
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
|
||||
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608042300" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608052230" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
@@ -1854,3 +1854,12 @@
|
||||
(2) **Chống NaN cho chương trình/bank FluidSynth**: Thêm bộ lọc `parseInt` và `isNaN` kiểm tra biến `usedBank` và `usedProg` bên trong hàm phát nốt `doNote` của `soundfontPlayer.js`. Tránh truyền giá trị `NaN` trực tiếp vào hàm WASM `_fluid_synth_program_select` có thể làm rối loạn bộ tổng hợp âm bên trong FluidSynth và kết xuất mẫu âm thanh NaN.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` (build lại), `app/static/js/services/soundfontPlayer.js`, `app/templates/index.html`, `wiki.md`
|
||||
- **Ghi chú/Test (nếu có):** `node build.mjs` thành công.
|
||||
|
||||
### [2026-08-05 22:30] Task: 2 fix quyết định trên baseline — NaN sweep synth (heap dangle) + watchdog piano-roll rebuild THẬT
|
||||
- **Báo cáo user lặp lại (cùng text):** piano-roll play → BACK → play → CÂM, chain STUCK. Sau khi loại toàn bộ compressor (v22:00) mà vẫn lỗi → NaN KHÔNG từ compressor.
|
||||
- **2 lỗ hổng còn lại (baseline cfc114b):**
|
||||
(1) **Synth render KHÔNG sweep NaN + lp/rp dangle:** `_leftBufPtr/_rightBufPtr` malloc 1 lần ở init; load SGM-V2.01 (~300MB) → heap WASM realloc → pointer đọc vùng free → NaN → chain state-bad. FIX soundfontPlayer.js: theo dõi `HEAPU8.buffer` → re-malloc khi đổi + **NaN sweep (isFinite → 0)** ở mọi block — synth output KHÔNG BAO GIỜ chứa NaN.
|
||||
(2) **Watchdog recovery là NO-OP:** `initMasterBus` early-return khi masterBus còn tồn tại → [Recovery] không rebuild gì → chain chết STUCK vĩnh viễn. FIX: teardown (disconnect analyser/output/dryOutput + masterBus=null) TRƯỚC initMasterBus → rebuild biquad THẬT + reschedule.
|
||||
(3) **Watchdog mở cho PIANO_ROLL:** trước đây `!isPianoRoll` (exempt hoàn toàn). Giờ: piano-roll CHỈ rebuild khi output NaN (getFloatTimeDomainData + isFinite — rests tự nhiên KHÔNG trigger, tránh false-positive).
|
||||
- **Các file ảnh hưởng:** soundfontPlayer.js, app.jsx, index.html (?v=202608052230 cho cả 2), wiki.md. Rebuild precompiled.
|
||||
- **Ghi chú/Test:** `npm run build` → hard refresh → play → BACK → play từ đầu → KỲ VỌNG: CÓ ÂM (sweep chặn NaN tại nguồn + watchdog cứu nếu còn chết).
|
||||
|
||||
Reference in New Issue
Block a user