From 0dc95386f2be4501fa4469417162e0e583e933c4 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Mon, 3 Aug 2026 10:33:40 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20ARM=20nhi=E1=BB=81u=20track=20ph=C3=A1t?= =?UTF-8?q?=20sai=20instrument=20-=20synth=5Fengine=20c=E1=BB=A7a=20track?= =?UTF-8?q?=20l=C3=A0=20ngu=E1=BB=93n=20quy=E1=BA=BFt=20=C4=91=E1=BB=8Bnh?= =?UTF-8?q?=20thay=20v=C3=AC=20channel=20state=20(b=E1=BB=8F=20che=20mask?= =?UTF-8?q?=20khi=20channel=20gi=E1=BB=AF=20instrument=20c=C5=A9/tr=C3=B9n?= =?UTF-8?q?g)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/services/soundfontPlayer.js | 15 ++++++--------- app/templates/index.html | 2 +- wiki.md | 5 +++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/static/js/services/soundfontPlayer.js b/app/static/js/services/soundfontPlayer.js index b5b873a..d4c85e6 100644 --- a/app/static/js/services/soundfontPlayer.js +++ b/app/static/js/services/soundfontPlayer.js @@ -424,18 +424,15 @@ var finalProg = usedProg; var finalSfId = synthEngine ? synthEngine.soundfont_id : undefined; var cachedCh = _channels[ch]; - // A channel explicitly configured with a soundfont (via - // selectInstrument) is authoritative. But the default channel - // state (bank 0, program 0, no soundfont) must NOT mask the - // track's synth engine or the note's program — otherwise notes - // on a freshly-picked instrument are silently dropped. - if (cachedCh && cachedCh.sfId !== undefined) { + // The note's own synth engine (track instrument) is + // authoritative. Channel state is only a cache: it must never + // mask the track's instrument, otherwise multi-track ARM or a + // re-picked instrument plays the wrong soundfont. Without an + // engine, fall back to the soundfont configured on the channel. + if (!synthEngine && cachedCh && cachedCh.sfId !== undefined) { finalBank = cachedCh.bank; finalProg = cachedCh.program; finalSfId = cachedCh.sfId; - } else if (!synthEngine && program !== undefined && cachedCh && cachedCh.program !== undefined && cachedCh.program !== 0 && cachedCh.program !== null) { - finalBank = cachedCh.bank; - finalProg = cachedCh.program; } // Ensure the soundfont is actually loaded before the note plays. // Quick instrument pick on a track does not pre-load it, so load diff --git a/app/templates/index.html b/app/templates/index.html index 9817285..097e874 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -16,7 +16,7 @@ - + diff --git a/wiki.md b/wiki.md index 7a3b051..14e9031 100644 --- a/wiki.md +++ b/wiki.md @@ -1199,3 +1199,8 @@ - **Tóm tắt thay đổi:** Sửa `soundfontPlayer.js` để phím MIDI trên track ARM phát đúng instrument: (1) `_playNoteFluid` không còn để state mặc định của channel (bank 0, program 0, không sfId) che mất `synth_engine` của track → `finalSfId` luôn đúng; (2) tự load soundfont lười (lazy) ngay trong đường phát note nếu font chưa vào `_sfHandleMap` (track pick nhanh từ dropdown không gọi `selectInstrument` → trước đây rơi vào `bank_select`/`program_change` trên synth không có soundfont → câm lặng); (3) `selectInstrument` không còn đổi hướng channel khi đã truyền channel tường minh (sửa lỗi 2 track dùng chung instrument bị giật channel). Bump version cache `soundfontPlayer.js` trong `index.html`. - **Các file ảnh hưởng:** `app/static/js/services/soundfontPlayer.js`, `app/templates/index.html` - **Ghi chú/Test (nếu có):** Test logic bằng harness `node /tmp/kilo/test_sonicsf_fix.js` (mô phỏng FluidSynth): S1 lazy-load + program_select + noteon, S2 channel cấu hình sẵn không sfload lại, S3 program-only fallback, S4 no-instrument vẫn silent, S5 2 track cùng instrument giữ channel riêng — ALL PASSED. Cần hard-refresh trình duyệt để nạp `soundfontPlayer.js` bản mới. + +### [2026-08-03 10:30] Task: Fix ARM nhiều track - instrument sai theo track (channel state che mất synth_engine) +- **Tóm tắt thay đổi:** `_playNoteFluid` trước đây ưu tiên state channel (`_channels[ch].sfId`) làm nguồn instrument cho note → khi ARM nhiều track hoặc track đổi instrument qua dropdown nhanh, note bị phát theo instrument cũ/khác đang "dính" trên channel (leftover state hoặc 2 track trùng channel) → sai instrument từng track. Sửa: `synth_engine` của track là nguồn quyết định; channel state chỉ là cache (chỉ dùng khi note không có engine). Khi note mang engine khác với channel đang giữ, tự động `program_select` lại đúng instrument trước `noteon`; vẫn lazy-load soundfont nếu chưa nạp. Bump version cache `soundfontPlayer.js` trong `index.html`. +- **Các file ảnh hưởng:** `app/static/js/services/soundfontPlayer.js`, `app/templates/index.html` +- **Ghi chú/Test (nếu có):** Harness `node /tmp/kilo/test_multi_track.js` (mô phỏng routing ARM như app.jsx): (A) 3 track 3 instrument distinct channel → noteon đúng, không re-select thừa; (B) 3 track quick-pick (không midiChannel, channel đang giữ instrument cũ) → mỗi track re-select đúng Q1/Q2/Q3 trước noteon; (C) 2 track trùng channel 0 → track 2 re-select program 40 trước noteon. Harness `test_sonicsf_fix.js` (single-track S1-S5) vẫn ALL PASSED. Hard refresh trình duyệt để nạp bản mới.