fix: sfload reset_presets=0 giữ nguyên instrument các track khác; preloadTrackInstruments module-level (sửa ReferenceError reload); truyền props cho ProfileModal; lọc warning No preset found channel 9

This commit is contained in:
2026-08-03 11:00:08 +07:00
parent 84ab4ae823
commit 9b6de7f857
5 changed files with 61 additions and 27 deletions
+37 -19
View File
@@ -4483,6 +4483,27 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
)
);
};
// Module-level so both ProfileModal (open project) and App (auto-restore) can
// warm the FluidSynth font cache for each track's instrument. This only loads
// the soundfonts; the per-track channel re-selection happens at note time.
const preloadTrackInstruments = async (tracks) => {
if (!window.SonicSF || !window.SonicSF.selectInstrument) return;
const list = tracks || [];
for (let i = 0; i < list.length; i++) {
const t = list[i];
const se = t.synth_engine;
const sfId = (se && se.soundfont_id) || t.soundfont_id;
if (!sfId) continue;
const bank = se ? (se.soundfont_bank !== undefined ? se.soundfont_bank : 0) : (t.soundfont_bank !== undefined ? t.soundfont_bank : 0);
const prog = se ? (se.soundfont_program !== undefined ? se.soundfont_program : 0) : (t.soundfont_program !== undefined ? t.soundfont_program : 0);
const ch = t.midiChannel !== undefined ? t.midiChannel : (i % 16);
try {
await window.SonicSF.selectInstrument(ch, bank, prog, sfId);
} catch (e) {}
}
};
const ProfileModal = ({
isOpen,
onClose,
@@ -4494,7 +4515,14 @@ const ProfileModal = ({
currentProjectId,
setCurrentProjectId,
showToast,
loadAudioBuffersForTracks
loadAudioBuffersForTracks,
setAppWarningModal,
bpm,
setBpm,
setMasteringSettings,
setSessionTabs,
setSubTabs,
trackMidiChannelsRef
}) => {
if (!isOpen) return null;
const [activeTab, setActiveTab] = useState('account');
@@ -4627,23 +4655,6 @@ const ProfileModal = ({
}
};
const preloadTrackInstruments = async (tracks) => {
// Preload each track's soundfont before the user plays otherwise the first
// MIDI key after reload triggers a lazy font load and the note sounds late.
if (!window.SonicSF || !window.SonicSF.selectInstrument) return;
for (const t of (tracks || [])) {
const se = t.synth_engine;
const sfId = (se && se.soundfont_id) || t.soundfont_id;
if (!sfId) continue;
const bank = se ? (se.soundfont_bank !== undefined ? se.soundfont_bank : 0) : (t.soundfont_bank !== undefined ? t.soundfont_bank : 0);
const prog = se ? (se.soundfont_program !== undefined ? se.soundfont_program : 0) : (t.soundfont_program !== undefined ? t.soundfont_program : 0);
var ch = t && assignTrackMidiChannel(t, tracks);
try {
await window.SonicSF.selectInstrument(ch, bank, prog, sfId);
} catch (e) {}
}
};
const handleOpenProject = async (projectId, projectName) => {
setAppWarningModal({
title: "Mở dự án",
@@ -23816,7 +23827,14 @@ const App = () => {
currentProjectId: currentProjectId,
setCurrentProjectId: setCurrentProjectId,
showToast: showToast,
loadAudioBuffersForTracks: loadAudioBuffersForTracks
loadAudioBuffersForTracks: loadAudioBuffersForTracks,
setAppWarningModal: setAppWarningModal,
bpm: bpm,
setBpm: setBpm,
setMasteringSettings: setMasteringSettings,
setSessionTabs: setSessionTabs,
setSubTabs: setSubTabs,
trackMidiChannelsRef: trackMidiChannelsRef
}), /*#__PURE__*/React.createElement(SaveProjectModal, {
isOpen: saveProjectModalOpen,
onClose: () => setSaveProjectModalOpen(false),
File diff suppressed because one or more lines are too long
+11 -1
View File
@@ -106,6 +106,11 @@
},
TOTAL_MEMORY: TOTAL_MEMORY,
printErr: function (msg) {
// "No preset found on channel" is FluidSynth's
// expected notice when a soundfont simply has no
// preset for a bank (e.g. bank 128 on a melodic-only
// font) — the note is just silent, not an error.
if (msg && msg.indexOf('No preset found on channel') !== -1) return;
console.warn('[FluidSynth:err]', msg);
}
});
@@ -192,7 +197,12 @@
try { _fluidModule.FS.unlink(fname); } catch (e) {}
_fluidModule.FS.writeFile(fname, new Uint8Array(buf));
var cPath = this._allocCStr(fname);
var handle = _fluidModule._fluid_synth_sfload(_synthPtr, cPath, 1);
// reset_presets = 0: loading a NEW soundfont must NOT reset the
// presets already selected on other channels. With 1, FluidSynth
// re-points every channel to the new font's preset 0, so loading a
// second instrument silently changes the first one's sound
// (decay/loop envelope…).
var handle = _fluidModule._fluid_synth_sfload(_synthPtr, cPath, 0);
_fluidModule._free(cPath);
try { _fluidModule.FS.unlink(fname); } catch (e) {}
return handle;
+2 -2
View File
@@ -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=202608031040"></script>
<script src="/static/js/services/soundfontPlayer.js?v=202608031130"></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>
@@ -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=202608031100" defer></script>
<script src="/static/js/app.precompiled.js?v=202608031130" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {
+5
View File
@@ -1214,3 +1214,8 @@
- **Tóm tắt thay đổi:** FluidSynth có 16 channel; nếu 2 track trùng channel thì ARM track này sẽ `program_select` đè instrument của track kia. Thêm cơ chế channel riêng cho từng track trong app.jsx: `trackMidiChannelsRef` + `ensureTrackMidiChannel`/`assignTrackMidiChannel` cấp channel ổn định, duy nhất (0-15, bỏ qua 9 - slot percussion cổ điển), tự sửa khi 2 track trùng channel được lưu. Áp dụng ở mọi nơi tính channel: `setTrackInstrumentWithProgram`, `setTrackInstrument` (dropdown nhanh), `preloadTrackInstruments`, routing ARM note-on (armedTracks + piano-roll sub-tab), note-off/CC/PitchBend (bỏ fallback `index % 16` — tránh dừng nhầm note của track khác). Lưu `midi_channel` khi save project + restore; reset map channel khi mở/khôi phục dự án. Kết hợp doNote cũ (synth_engine quyết định + re-select trước noteon) → mỗi track luôn phát đúng instrument của nó dù ARM nhiều track, chord/hợp âm đồng thời không còn đổi âm sắc nhau.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` (build lại), `app/templates/index.html`
- **Ghi chú/Test (nếu có):** `npm run build` OK, bundle chứa `assignTrackMidiChannel` (8 chỗ). Harness `node /tmp/kilo/test_channels.js`: 3 track → channel 0,1,2 duy nhất; gọi lại ổn định; track mới lấy channel trống; track lưu trùng channel được cấp lại; track lưu channel duy nhất được giữ; percussion giữ 9 + track trống khác lấy channel riêng — ALL PASSED. 3 harness cũ (single/multi/dedup) vẫn PASS. Hard refresh.
### [2026-08-03 11:30] Task: Fix instrument sau làm đổi thông số instrument trước + ReferenceError preloadTrackInstruments + warning channel 9
- **Tóm tắt thay đổi:** (1) **sfload reset_presets 1→0** trong `soundfontPlayer._tryLoadSFL`: trước đây mỗi lần load soundfont mới, FluidSynth reset preset của MỌI channel về preset 0 của font mới → load instrument sau làm đổi âm sắc (decay/loop...) của instrument trước dù channel đã `program_select` riêng; giờ channel giữ nguyên instrument, doNote/selectInstrument tự `program_select` rõ ràng. (2) **ReferenceError `preloadTrackInstruments`**: hàm bị đặt nhầm bên trong `ProfileModal` (không cùng scope với `restoreLastSessionProject` trong `App`) → lỗi khi reload. Chuyển `preloadTrackInstruments` lên module-level (self-contained, chỉ warm font cache). Đồng thời sửa lỗi tiềm ẩn của `handleOpenProject` trong ProfileModal: truyền thêm props App-scoped (`setAppWarningModal`, `bpm`, `setBpm`, `setMasteringSettings`, `setSessionTabs`, `setSubTabs`, `trackMidiChannelsRef`) — trước đây click mở dự án trong modal sẽ ReferenceError. (3) **"No preset found on channel 9 [bank=128 prog=0]"**: lọc warning benign trong `printErr` (font không có preset bank 128 thì note chỉ câm, không phải lỗi) + reset=0 giảm nguồn sinh ra nó.
- **Các file ảnh hưởng:** `app/static/js/services/soundfontPlayer.js`, `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` (build lại), `app/templates/index.html`
- **Ghi chú/Test (nếu có):** `npm run build` OK, bundle syntax OK. Harness `node /tmp/kilo/test_reset.js`: sfload reset=0, load B không re-select channel A, noteon ch0/ch1 không kèm program_select thừa — PASSED. 5 harness còn lại (single/multi/dedup/channels) vẫn PASS. Deprecation ScriptProcessorNode giữ nguyên (đổi sang AudioWorklet có rủi ro crackle, cần test audio thật). Hard refresh.