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,21 +362,40 @@
|
|||||||
typeof velocity === 'number' ? (velocity > 1 ? velocity : velocity * 127) : 100
|
typeof velocity === 'number' ? (velocity > 1 ? velocity : velocity * 127) : 100
|
||||||
)));
|
)));
|
||||||
var _origChannel = channel;
|
var _origChannel = channel;
|
||||||
var ch = channel;
|
|
||||||
var usedBank = 0, usedProg = 0;
|
var usedBank = 0, usedProg = 0;
|
||||||
if (synthEngine) {
|
if (synthEngine) {
|
||||||
usedBank = synthEngine.soundfont_bank || 0;
|
usedBank = synthEngine.soundfont_bank || 0;
|
||||||
usedProg = synthEngine.soundfont_program || 0;
|
usedProg = synthEngine.soundfont_program || 0;
|
||||||
if (ch === undefined) {
|
if (channel === undefined) {
|
||||||
var engKey = (synthEngine.soundfont_id || '') + ':' + usedBank + ':' + usedProg;
|
var engKey = (synthEngine.soundfont_id || '') + ':' + usedBank + ':' + usedProg;
|
||||||
var mappedCh = _engineChMap[engKey];
|
var mappedCh = _engineChMap[engKey];
|
||||||
if (mappedCh !== undefined) {
|
if (mappedCh !== undefined) {
|
||||||
ch = mappedCh;
|
channel = mappedCh;
|
||||||
} else {
|
} else {
|
||||||
ch = usedBank === 128 ? 9 : 0;
|
channel = usedBank === 128 ? 9 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ch === undefined) ch = (usedBank === 128 ? 9 : 0);
|
if (channel === undefined) channel = (usedBank === 128 ? 9 : 0);
|
||||||
|
} else if (program !== undefined) {
|
||||||
|
usedProg = program;
|
||||||
|
if (channel === undefined) channel = 0;
|
||||||
|
} else {
|
||||||
|
// No instrument configured: silent — no FluidSynth, no oscillator.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
var durSec = (durationMs || 500) / 1000;
|
||||||
|
var scheduledNote = { on: null, off: null };
|
||||||
|
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;
|
var sfHandle = synthEngine.soundfont_id ? _sfHandleMap.get(synthEngine.soundfont_id) : undefined;
|
||||||
if (sfHandle !== undefined) {
|
if (sfHandle !== undefined) {
|
||||||
try {
|
try {
|
||||||
@@ -387,24 +406,10 @@
|
|||||||
try { _fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg); } catch (e) {}
|
try { _fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg); } catch (e) {}
|
||||||
}
|
}
|
||||||
} else if (program !== undefined) {
|
} else if (program !== undefined) {
|
||||||
usedProg = program;
|
|
||||||
if (ch === undefined) ch = 0;
|
|
||||||
try {
|
try {
|
||||||
_fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg);
|
_fluidModule._fluid_synth_program_change(_synthPtr, ch, usedProg);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
} else {
|
|
||||||
// No instrument configured: silent — no FluidSynth, no oscillator.
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (ch === undefined) ch = (usedBank === 128 ? 9 : 0);
|
|
||||||
var ctx = getCtx();
|
|
||||||
var now = ctx.currentTime;
|
|
||||||
var delay = (typeof startTime === 'number' && startTime > now) ? (startTime - now) : 0;
|
|
||||||
var durSec = (durationMs || 500) / 1000;
|
|
||||||
var scheduledNote = { on: null, off: null };
|
|
||||||
var self = this;
|
|
||||||
var doNote = function () {
|
|
||||||
try {
|
|
||||||
console.log("[SonicSF] noteOn ch:", ch, "pitch:", midiPitch, "vel:", midiVel);
|
console.log("[SonicSF] noteOn ch:", ch, "pitch:", midiPitch, "vel:", midiVel);
|
||||||
_fluidModule._fluid_synth_noteon(_synthPtr, ch, midiPitch, midiVel);
|
_fluidModule._fluid_synth_noteon(_synthPtr, ch, midiPitch, midiVel);
|
||||||
var noteMapKey = (_origChannel !== undefined ? _origChannel : 0) + ':' + midiPitch;
|
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.
|
- **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
|
### [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.
|
- **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`
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`
|
||||||
|
|||||||
Reference in New Issue
Block a user