feat(engine): fix SF2 path resolution, add synth_engine struct and fallback chain

- Fix SF2 path: search upload dir, system dir, static dir (backward compat)
- Add synth_engine struct parsing with flat field fallback
- Add 3-level fallback: selected SF -> default SF -> oscillator synth
- Write synth_engine in setTrackInstrument UI setters
- Pass synth_engine context to client SoundFontPlayer and AI gateway
This commit is contained in:
2026-07-26 16:49:40 +07:00
parent 2ab989132b
commit d48262d468
5 changed files with 99 additions and 22 deletions
+16 -3
View File
@@ -6492,7 +6492,17 @@ const App = () => {
if (t.id !== trackId) return t;
const hasInstrument = !!instrumentId;
const isSfInstrument = instrumentId && typeof instrumentId === 'string' && instrumentId.startsWith('sf_');
return { ...t, instrumentId, instrumentProgram: programNumber !== undefined ? programNumber : undefined, instrumentName: displayName, soundfont_bank: bankNumber !== undefined ? bankNumber : (isSfInstrument ? 0 : undefined), soundfont_program: programNumber !== undefined ? programNumber : undefined, type: hasInstrument ? 'MIDI' : (t.type === 'MIDI' ? 'audio' : t.type) };
const sfBank = bankNumber !== undefined ? bankNumber : (isSfInstrument ? 0 : undefined);
const sfProg = programNumber !== undefined ? programNumber : undefined;
const instrType = isSfInstrument ? 'soundfont' : (hasInstrument ? 'vst3' : 'default');
const synthEngine = hasInstrument ? {
type: instrType,
plugin_id: instrumentId,
soundfont_bank: sfBank !== undefined ? sfBank : 0,
soundfont_program: sfProg !== undefined ? sfProg : 0,
soundfont_id: isSfInstrument ? instrumentId.replace('sf_', '') : ''
} : undefined;
return { ...t, instrumentId, instrumentProgram: sfProg, instrumentName: displayName, soundfont_bank: sfBank, soundfont_program: sfProg, synth_engine: synthEngine, type: hasInstrument ? 'MIDI' : (t.type === 'MIDI' ? 'audio' : t.type) };
}));
setInstrumentDropdownTrackId(null);
setInstrumentDropdownBtnRect(null);
@@ -6544,7 +6554,9 @@ const App = () => {
// Set instrument on track immediately so Synth button shows the name
updateActiveTracks(prev => prev.map(t => {
if (t.id !== trackId) return t;
return { ...t, instrumentId, instrumentProgram: undefined, instrumentName: displayName };
const sfClean = instrumentId.replace('sf_', '');
const synthEngine = { type: 'soundfont', plugin_id: instrumentId, soundfont_bank: 0, soundfont_program: 0, soundfont_id: sfClean };
return { ...t, instrumentId, instrumentProgram: undefined, instrumentName: displayName, synth_engine: synthEngine };
}));
setSelectedSoundFontId(instrumentId);
setSynthCategory('soundfont');
@@ -10053,6 +10065,7 @@ const App = () => {
const track = activeTracksRef.current ? activeTracksRef.current.find(t => t.id === st.trackId) : null;
const destNode = getOrCreateTrackNode(track, context);
const instrumentProgram = track ? track.instrumentProgram : undefined;
const synthEngine = track ? track.synth_engine : undefined;
midiNotes.forEach(note => {
const noteOnBeat = note.start_beat || 0;
const noteDurBeat = note.duration_beats || 1;
@@ -10064,7 +10077,7 @@ const App = () => {
const scheduledTime = startWallTime + effectiveStart;
const durMs = effectiveDur * 1000;
if (window.SonicSF) {
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode);
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode, undefined, synthEngine);
}
}
});