fix: crash AudioWorklet khi output mono - xử lý mọi số channel trong fluidsynth-bridge + ép stereo outputChannelCount=2
This commit is contained in:
@@ -141,7 +141,15 @@
|
||||
|
||||
if (!_useScriptNode) {
|
||||
try {
|
||||
_workletNode = new AudioWorkletNode(_audioCtx, 'fluidsynth-bridge');
|
||||
// Force stereo output regardless of the device's
|
||||
// channel count — FluidSynth renders stereo, and a
|
||||
// mono output would crash the worklet (out[1] undefined).
|
||||
_workletNode = new AudioWorkletNode(_audioCtx, 'fluidsynth-bridge', {
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [2],
|
||||
channelCount: 2,
|
||||
channelCountMode: 'explicit'
|
||||
});
|
||||
_workletNode.connect(_gainNode);
|
||||
console.log("[SonicSF] AudioWorklet node connected via gain");
|
||||
_startRenderLoop();
|
||||
|
||||
@@ -15,17 +15,24 @@ class FluidSynthBridge extends AudioWorkletProcessor {
|
||||
|
||||
process(inputs, outputs) {
|
||||
const out = outputs[0];
|
||||
if (!out) return true;
|
||||
if (!out || out.length === 0) return true;
|
||||
this.called++;
|
||||
const numCh = out.length;
|
||||
const len = out[0].length;
|
||||
const qL = this.leftQ;
|
||||
const qR = this.rightQ;
|
||||
let fi = 0;
|
||||
let si = 0;
|
||||
// Handle any output channel count (mono devices produce 1 channel, so
|
||||
// out[1] may be undefined — never write into a missing channel).
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (fi >= qL.length) { out[0][i] = 0; out[1][i] = 0; continue; }
|
||||
out[0][i] = qL[fi][si];
|
||||
out[1][i] = qR[fi][si];
|
||||
if (fi >= qL.length) {
|
||||
for (let c = 0; c < numCh; c++) out[c][i] = 0;
|
||||
continue;
|
||||
}
|
||||
for (let c = 0; c < numCh; c++) {
|
||||
out[c][i] = c % 2 === 0 ? qL[fi][si] : qR[fi][si];
|
||||
}
|
||||
si++;
|
||||
if (si >= qL[fi].length) { fi++; si = 0; }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script src="/static/js/services/audioEngine.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/storage.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/soundfontStorage.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202608031210"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202608031230"></script>
|
||||
<script src="/static/js/services/aiGateway.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/dawCommandDispatcher.js?v=202607271016"></script>
|
||||
<script src="/static/js/services/pianoRollTabService.js?v=202607272044"></script>
|
||||
|
||||
@@ -1229,3 +1229,8 @@
|
||||
- **Tóm tắt thay đổi:** (1) **ReferenceError `assignTrackMidiChannel is not defined`**: allocator channel (trackMidiChannelsRef/ensureTrackMidiChannel/assignTrackMidiChannel) đặt bên trong `App`, nhưng các sub-component piano roll (`PianoRollTabEditor` — canvas preview, click note, draw/paint brush, keybed) là module-level → không thấy hàm → lỗi khi render/play. Chuyển allocator lên **module-level** (đầu file, trước PianoRollTabEditor), App dùng chung bản đó (bỏ khai báo `useRef` cục bộ trong App; reset `trackMidiChannelsRef.current = {}` khi load dự án vẫn dùng chung object). (2) **Deprecation ScriptProcessorNode**: bật **AudioWorklet** `fluidsynth-bridge` làm mặc định (`_useScriptNode = false`), chỉ fallback ScriptProcessor khi `new AudioWorkletNode`/`addModule` thất bại → hết warning deprecation; `_startRenderLoop` (queue 16 block × 512 sample ~186ms headroom, interval 2× frame rate) vốn đã được thiết kế cho worklet.
|
||||
- **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`
|
||||
- **Ghi chú/Test (nếu có):** `npm run build` OK, bundle syntax OK, bundle load smoke test (stub React/ReactDOM/window) OK. Harness `test_sonicsf_fix.js` với mock AudioWorkletNode + performance: `workletCreated=1 scriptProcCreated=0` (worklet được dùng), S1-S5 vẫn PASS; trước đó (thiếu mock performance) xác nhận fallback ScriptProcessor hoạt động. 4 harness còn lại vẫn PASS. Hard refresh.
|
||||
|
||||
### [2026-08-03 12:30] Task: Fix crash AudioWorklet - "Cannot set properties of undefined (setting '0')" khi output mono
|
||||
- **Tóm tắt thay đổi:** `FluidSynthBridge.process` luôn ghi stereo `out[0]`/`out[1]`; khi thiết bị/context xuất mono (chỉ 1 channel), `out[1]` là `undefined` → `out[1][i] = ...` ném TypeError, dừng audio. Fix: (1) `fluidsynth-bridge.js` xử lý mọi số channel output (vòng lặp theo `out.length`, chẵn=lấy L, lẻ=lấy R, trường hợp cạn queue ghi 0 cho từng channel); (2) `soundfontPlayer.js` tạo `AudioWorkletNode` với `outputChannelCount: [2]`, `channelCount: 2`, `channelCountMode: 'explicit'` để ép stereo (FluidSynth render stereo) thay vì để device quyết định.
|
||||
- **Các file ảnh hưởng:** `app/static/js/worklets/fluidsynth-bridge.js`, `app/static/js/services/soundfontPlayer.js`, `app/templates/index.html`
|
||||
- **Ghi chú/Test (nếu có):** Harness `node /tmp/kilo/test_worklet.js` mô phỏng process(): (1) output mono 1 channel → mirror left, không crash; (2) stereo → L/R đúng; (3) queue cạn → silence. Tất cả PASS. 5 harness khác vẫn PASS. Worklet + soundfontPlayer `node --check` OK. Không cần build bundle (2 file served trực tiếp). Hard refresh.
|
||||
|
||||
Reference in New Issue
Block a user