diff --git a/app/static/js/services/soundfontPlayer.js b/app/static/js/services/soundfontPlayer.js index 95543ab..7a110dd 100644 --- a/app/static/js/services/soundfontPlayer.js +++ b/app/static/js/services/soundfontPlayer.js @@ -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; diff --git a/wiki.md b/wiki.md index 599c741..50ba5c1 100644 --- a/wiki.md +++ b/wiki.md @@ -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`