fix: program_change at call time overrides subsequent notes

_playNoteFluid called program_change synchronously at
call time, not at scheduled note time. startTrackPlayback
processes all MIDI items first, then all sections -> each
note's program_change overwritten by the last type processed.
All notes played with section instrument regardless of
actual time position. Moved program_change into doNote
(setTimeout callback) so it fires at correct time.
This commit is contained in:
2026-07-28 09:48:54 +07:00
parent 67551075be
commit 32b61553f9
2 changed files with 30 additions and 19 deletions
+24 -19
View File
@@ -362,41 +362,29 @@
typeof velocity === 'number' ? (velocity > 1 ? velocity : velocity * 127) : 100
)));
var _origChannel = channel;
var ch = channel;
var usedBank = 0, usedProg = 0;
if (synthEngine) {
usedBank = synthEngine.soundfont_bank || 0;
usedProg = synthEngine.soundfont_program || 0;
if (ch === undefined) {
if (channel === undefined) {
var engKey = (synthEngine.soundfont_id || '') + ':' + usedBank + ':' + usedProg;
var mappedCh = _engineChMap[engKey];
if (mappedCh !== undefined) {
ch = mappedCh;
channel = mappedCh;
} else {
ch = usedBank === 128 ? 9 : 0;
channel = usedBank === 128 ? 9 : 0;
}
}
if (ch === undefined) ch = (usedBank === 128 ? 9 : 0);
var sfHandle = synthEngine.soundfont_id ? _sfHandleMap.get(synthEngine.soundfont_id) : undefined;
if (sfHandle !== undefined) {
try {
_fluidModule._fluid_synth_program_select(_synthPtr, ch, sfHandle, usedBank, usedProg);
} catch (e) {}
} else {
try { _fluidModule._fluid_synth_bank_select(_synthPtr, ch, usedBank); } catch (e) {}
try { _fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg); } catch (e) {}
}
if (channel === undefined) channel = (usedBank === 128 ? 9 : 0);
} else if (program !== undefined) {
usedProg = program;
if (ch === undefined) ch = 0;
try {
_fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg);
} catch (e) {}
if (channel === undefined) channel = 0;
} else {
// No instrument configured: silent — no FluidSynth, no oscillator.
return;
}
if (ch === undefined) ch = (usedBank === 128 ? 9 : 0);
if (channel === undefined) channel = (usedBank === 128 ? 9 : 0);
var ch = channel;
var ctx = getCtx();
var now = ctx.currentTime;
var delay = (typeof startTime === 'number' && startTime > now) ? (startTime - now) : 0;
@@ -405,6 +393,23 @@
var self = this;
var doNote = function () {
try {
// Program change at note time, not call time — ensures correct
// instrument for each item regardless of processing order.
if (synthEngine) {
var sfHandle = synthEngine.soundfont_id ? _sfHandleMap.get(synthEngine.soundfont_id) : undefined;
if (sfHandle !== undefined) {
try {
_fluidModule._fluid_synth_program_select(_synthPtr, ch, sfHandle, usedBank, usedProg);
} catch (e) {}
} else {
try { _fluidModule._fluid_synth_bank_select(_synthPtr, ch, usedBank); } catch (e) {}
try { _fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg); } catch (e) {}
}
} else if (program !== undefined) {
try {
_fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg);
} catch (e) {}
}
console.log("[SonicSF] noteOn ch:", ch, "pitch:", midiPitch, "vel:", midiVel);
_fluidModule._fluid_synth_noteon(_synthPtr, ch, midiPitch, midiVel);
var noteMapKey = (_origChannel !== undefined ? _origChannel : 0) + ':' + midiPitch;
+6
View File
@@ -539,6 +539,12 @@
- **Ghi chú/Test (nếu có):** `npm run build` pass. Ctrl+Click+Drag section/MIDI item → copy đến vị trí mới, item không bị selected.
---
### [2026-07-28 09:50] Task: Program change ở note time, không phải call time
- **Tóm tắt thay đổi:** Di chuyển `program_change`/`program_select`/`bank_select` từ synchronous (call time) vào trong `doNote` (note time via setTimeout) trong `_playNoteFluid`. Trước đây tất cả program_changes xảy ra ngay khi `startTrackPlayback` chạy → MIDI items processed trước sections → section's program_change override cuối cùng → ALL notes (section + MIDI) play với section instrument. Fix: mỗi note có program_change tại đúng thời điểm của nó.
- **Các file ảnh hưởng:** `app/static/js/services/soundfontPlayer.js`
- **Ghi chú/Test (nếu có):** load. TRACK 1: | SECTION ITEM (inst B) | MIDI ITEM (inst A) | → section plays inst B, MIDI item plays inst A.
---
### [2026-07-28 09:30] Task: Thêm _isSectionClone flag — tắt âm thanh mặc định trong SECTION-TAB
- **Tóm tắt thay đổi:** Thêm `_isSectionClone: true` vào section cloned tracks. Line 10283 kiểm tra flag: nếu section clone và không instrument → `undefined` (silent); nếu MAIN track và không instrument → `0` (GM Piano). Trước đây section cloned tracks đi qua line 10283 với `instrumentProgram: undefined` → default 0 → play GM Piano.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`