fix: Select Instrument stays open after picking instrument

Remove setInstrumentSelectorTrackId(null) from
setTrackInstrumentWithProgram. Modal stays open so user can
switch tracks and assign instruments without reopening.
This commit is contained in:
2026-07-29 11:56:26 +07:00
parent 8051cf3edb
commit a1767e338a
3 changed files with 7 additions and 3 deletions
-2
View File
@@ -6919,9 +6919,7 @@ const App = () => {
}));
setInstrumentDropdownTrackId(null);
setInstrumentDropdownBtnRect(null);
setInstrumentSelectorTrackId(null);
setSynthCategory(null);
setSelectedSoundFontId(null);
// Trigger SpessaSynth load + program change when soundfont instrument selected
if (window.SonicSF && window.SonicSF.selectInstrument && instrumentId && isSfInstrument) {
const sfId = instrumentId.replace('sf_', '');
+1 -1
View File
@@ -185,7 +185,7 @@ const[selectedSoundFontId,setSelectedSoundFontId]=useState(null);const[sfPresets
const[instrumentDropdownTrackId,setInstrumentDropdownTrackId]=useState(null);const[instrumentDropdownBtnRect,setInstrumentDropdownBtnRect]=useState(null);const[instrumentSearchQuery,setInstrumentSearchQuery]=useState('');const[sfPresetSearchQuery,setSfPresetSearchQuery]=useState('');const filteredInstruments=useMemo(()=>{if(!instrumentSelectorData||!instrumentSearchQuery)return{soundfonts:instrumentSelectorData?.soundfonts||[],vst:instrumentSelectorData?.vst_instruments||[]};const q=instrumentSearchQuery.toLowerCase();return{soundfonts:(instrumentSelectorData.soundfonts||[]).filter(sf=>(sf.display||sf.name||sf.id).toLowerCase().includes(q)),vst:(instrumentSelectorData.vst_instruments||[]).filter(v=>(v.name||v.id).toLowerCase().includes(q))};},[instrumentSearchQuery,instrumentSelectorData]);useEffect(()=>{if(!instrumentSelectorData){window.SonicAPI.listPlugins().then(async data=>{// Eagerly fetch full catalog with all instrument presets
try{const catResp=(await window.SonicAPI.getSoundfontCatalog?.())??(await fetch('/api/v1/plugins/soundfonts/catalog').then(r=>r.json()));const catalog=catResp.full_catalog||{};// Merge presets into each soundfont entry
data.soundfonts=(data.soundfonts||[]).map(sf=>{const sfId=sf.id.replace('sf_','');// Catalog keys are lowercased by soundfont_inspector
const catEntry=catalog[sfId.toLowerCase()]||catalog[sfId];if(catEntry&&catEntry.instruments){return{...sf,presets:catEntry.instruments};}return sf;});}catch(e){console.warn('Catalog fetch error:',e);}setInstrumentSelectorData(data);}).catch(()=>{});}},[]);useEffect(()=>{if(!instrumentDropdownTrackId)return;const handler=e=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setInstrumentSearchQuery('');};document.addEventListener('click',handler);return()=>document.removeEventListener('click',handler);},[instrumentDropdownTrackId]);const GM_INSTRUMENTS=["Acoustic Grand Piano","Bright Acoustic Piano","Electric Grand Piano","Honky-tonk Piano","Electric Piano 1","Electric Piano 2","Harpsichord","Clavi","Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer","Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accordion","Harmonica","Tango Accordion","Acoustic Guitar (nylon)","Acoustic Guitar (steel)","Electric Guitar (jazz)","Electric Guitar (clean)","Electric Guitar (muted)","Overdriven Guitar","Distortion Guitar","Guitar harmonics","Acoustic Bass","Electric Bass (finger)","Electric Bass (pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Harp","Timpani","String Ensemble 1","String Ensemble 2","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet","Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Shakuhachi","Whistle","Ocarina","Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass+lead)","Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)","FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag pipe","Fiddle","Shanai","Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal","Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"];const setTrackInstrumentWithProgram=(trackId,instrumentId,programNumber,displayName,bankNumber)=>{const isSfInstrument=instrumentId&&typeof instrumentId==='string'&&instrumentId.startsWith('sf_');const sfBank=bankNumber!==undefined?bankNumber:isSfInstrument?0:undefined;const sfProg=programNumber!==undefined?programNumber:undefined;updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;const hasInstrument=!!instrumentId;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;var mt=activeTracksRef.current||tracks;var midx=0;for(var mi=0;mi<mt.length;mi++){if(mt[mi].id===trackId){midx=mi;break;}}var mch=sfBank===128?9:midx%16;return{...t,midiChannel:mch,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);setInstrumentSelectorTrackId(null);setSynthCategory(null);setSelectedSoundFontId(null);// Trigger SpessaSynth load + program change when soundfont instrument selected
const catEntry=catalog[sfId.toLowerCase()]||catalog[sfId];if(catEntry&&catEntry.instruments){return{...sf,presets:catEntry.instruments};}return sf;});}catch(e){console.warn('Catalog fetch error:',e);}setInstrumentSelectorData(data);}).catch(()=>{});}},[]);useEffect(()=>{if(!instrumentDropdownTrackId)return;const handler=e=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setInstrumentSearchQuery('');};document.addEventListener('click',handler);return()=>document.removeEventListener('click',handler);},[instrumentDropdownTrackId]);const GM_INSTRUMENTS=["Acoustic Grand Piano","Bright Acoustic Piano","Electric Grand Piano","Honky-tonk Piano","Electric Piano 1","Electric Piano 2","Harpsichord","Clavi","Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer","Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accordion","Harmonica","Tango Accordion","Acoustic Guitar (nylon)","Acoustic Guitar (steel)","Electric Guitar (jazz)","Electric Guitar (clean)","Electric Guitar (muted)","Overdriven Guitar","Distortion Guitar","Guitar harmonics","Acoustic Bass","Electric Bass (finger)","Electric Bass (pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Harp","Timpani","String Ensemble 1","String Ensemble 2","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet","Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Shakuhachi","Whistle","Ocarina","Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass+lead)","Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)","FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag pipe","Fiddle","Shanai","Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal","Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"];const setTrackInstrumentWithProgram=(trackId,instrumentId,programNumber,displayName,bankNumber)=>{const isSfInstrument=instrumentId&&typeof instrumentId==='string'&&instrumentId.startsWith('sf_');const sfBank=bankNumber!==undefined?bankNumber:isSfInstrument?0:undefined;const sfProg=programNumber!==undefined?programNumber:undefined;updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;const hasInstrument=!!instrumentId;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;var mt=activeTracksRef.current||tracks;var midx=0;for(var mi=0;mi<mt.length;mi++){if(mt[mi].id===trackId){midx=mi;break;}}var mch=sfBank===128?9:midx%16;return{...t,midiChannel:mch,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);setSynthCategory(null);// Trigger SpessaSynth load + program change when soundfont instrument selected
if(window.SonicSF&&window.SonicSF.selectInstrument&&instrumentId&&isSfInstrument){const sfId=instrumentId.replace('sf_','');var allTracks=activeTracksRef.current||tracks;var tidx=0;for(var i=0;i<allTracks.length;i++){if(allTracks[i].id===trackId){tidx=i;break;}}var ch=sfBank===128?9:tidx%16;// Store channel for consistent per-track instrument playback
if(!allTracks[tidx]||allTracks[tidx].midiChannel===undefined){updateActiveTracks(prev=>prev.map(t=>t.id===trackId?{...t,midiChannel:ch}:t));}window.SonicSF.selectInstrument(ch,sfBank||0,sfProg||0,sfId);}setSubTabs(prev=>prev.map(s=>{if(s.trackId!==trackId)return s;return{...s,instrumentProgram:programNumber!==undefined?programNumber:undefined,instrumentName:displayName,instrumentId};}));const playingSub=subTabs.find(s=>s.trackId===trackId&&s.type==='PIANO_ROLL'&&s.isPlaying);if(playingSub){const pid=playingSub.id;const ctx=getAudioContext();const tNode=activeTrackNodesRef.current[trackId];if(tNode&&tNode.gainNode){tNode.gainNode.gain.setValueAtTime(tNode.gainNode.gain.value||1,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(0.001,ctx.currentTime+0.04);}setTimeout(()=>{stopAllPlayback();if(tNode&&tNode.gainNode){const trackData=activeTracksRef.current?activeTracksRef.current.find(t=>t.id===trackId):null;const volDb=trackData?trackData.volumeDb??0:0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);tNode.gainNode.gain.setValueAtTime(0.001,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(volLinear||0.8,ctx.currentTime+0.015);}const context=getAudioContext();const offset=playingSub.currentTime||0;startOffsetTimeRef.current=offset;startAudioTimeRef.current=context.currentTime;startBufferOffsetRef.current=offset*(playingSub.speed||1.0);schedulePianoRollMidi(playingSub,offset);startSubTabPlayback(playingSub,offset);setSubTabs(prev=>prev.map(s=>s.id===pid?{...s,isPlaying:true}:s));if(subTabsRef.current){subTabsRef.current=subTabsRef.current.map(s=>s.id===pid?{...s,isPlaying:true}:s);}animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);},60);}setTimeout(()=>lucide.createIcons(),50);};const setTrackInstrument=(trackId,instrumentId,displayName)=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);if(instrumentId&&instrumentId.startsWith('sf_')){// Set instrument on track immediately so Synth button shows the name
updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;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');setInstrumentSelectorTrackId(trackId);setSfPresets(null);setSfPresetSearchQuery('');const sfIdParam=instrumentId.replace('sf_','');// Use cached presets from instrumentSelectorData