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:
@@ -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;
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user