From 90ca8f484b866880056584bf08757ac49373d567 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sun, 26 Jul 2026 11:13:50 +0700 Subject: [PATCH] fix: restore isPlaying after stopAllPlayback on instrument change --- app/static/js/app.jsx | 5 +++++ app/static/js/app.precompiled.js | 2 +- app/storage/sonicforge.db | Bin 118784 -> 118784 bytes 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 56a85da..ce2be62 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -6447,6 +6447,7 @@ const App = () => { })); const playingSub = subTabs.find(s => s.trackId === trackId && s.type === 'PIANO_ROLL' && s.isPlaying); if (playingSub) { + const pid = playingSub.id; stopAllPlayback(); setTimeout(() => { const context = getAudioContext(); @@ -6456,6 +6457,10 @@ const App = () => { 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); }, 50); } diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index f51caed..1ec0ddc 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -170,7 +170,7 @@ const[hoveredTrackId,setHoveredTrackId]=useState(null);const openPanel=id=>{if(i setSynthCategory('soundfont');setSelectedSoundFontId(track.instrumentId);setSfPresets(null);const sfIdParam=track.instrumentId.replace('sf_','');window.SonicAPI.listSoundfontInstruments(sfIdParam).then(data=>setSfPresets(data.presets||[])).catch(()=>setSfPresets([]));}else{// Reset synth state and always reload plugin data setSynthCategory(null);setSelectedSoundFontId(null);window.SonicAPI.listPlugins().then(data=>setInstrumentSelectorData(data)).catch(e=>console.error('listPlugins failed:',e));}};const closeInstrumentSelector=()=>{setInstrumentSelectorTrackId(null);setSynthCategory(null);setSelectedSoundFontId(null);};const[synthCategory,setSynthCategory]=useState(null);// 'vst' | 'soundfont' const[selectedSoundFontId,setSelectedSoundFontId]=useState(null);const[sfPresets,setSfPresets]=useState(null);// presets from SoundFont -const[instrumentDropdownTrackId,setInstrumentDropdownTrackId]=useState(null);const[instrumentDropdownBtnRect,setInstrumentDropdownBtnRect]=useState(null);const[instrumentSearchQuery,setInstrumentSearchQuery]=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(data=>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)=>{updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;return{...t,instrumentId,instrumentProgram:programNumber!==undefined?programNumber:undefined,instrumentName:displayName};}));setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setInstrumentSelectorTrackId(null);setSynthCategory(null);setSelectedSoundFontId(null);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){stopAllPlayback();setTimeout(()=>{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);animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);},50);}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 +const[instrumentDropdownTrackId,setInstrumentDropdownTrackId]=useState(null);const[instrumentDropdownBtnRect,setInstrumentDropdownBtnRect]=useState(null);const[instrumentSearchQuery,setInstrumentSearchQuery]=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(data=>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)=>{updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;return{...t,instrumentId,instrumentProgram:programNumber!==undefined?programNumber:undefined,instrumentName:displayName};}));setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setInstrumentSelectorTrackId(null);setSynthCategory(null);setSelectedSoundFontId(null);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;stopAllPlayback();setTimeout(()=>{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);},50);}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;return{...t,instrumentId,instrumentProgram:undefined,instrumentName:displayName};}));setSelectedSoundFontId(instrumentId);setSynthCategory('soundfont');setInstrumentSelectorTrackId(trackId);setSfPresets(null);// Fetch actual presets from the SoundFont const sfIdParam=instrumentId.replace('sf_','');window.SonicAPI.listSoundfontInstruments(sfIdParam).then(data=>setSfPresets(data.presets||[])).catch(e=>{console.error('listSoundfontInstruments failed:',e);setSfPresets([]);});}else{setTrackInstrumentWithProgram(trackId,instrumentId,undefined,displayName);}};const[activeTool,setActiveTool]=useState('select');// 'select' | 'grab' | 'razor' const[snapValue,setSnapValue]=useState('free');// 'free', '1', '1/2', '1/4', '1/8', '1/16', '1/32' diff --git a/app/storage/sonicforge.db b/app/storage/sonicforge.db index 830b803c98060812de5fc885b5141f38b4942377..81a6f6dd71284cd88cd9240f6eca65f9ce602705 100644 GIT binary patch delta 2974 zcmaJ@ON<;>74;kcB+H)Z@du1&2G2AZf{a~yuim$YSOy~`R!A&ZLUng_b$9i*x~o6c zjYhJ?2C*SdEuN4f;tdiz3k@IJ1Y2U2kPs!SNGuR$iLhq2&7mc1$Fy|(Ln`i(xiaD-roXP@0IuDD|UlA2V|Wo(@kOWLwunTfVfaPe|6 zj)p;_xFf~oVlpfbhJGb14~shxNo9}TJeys;znr1JT%sIP<1a9d$au`hIPwXZ^Ow*0 z69=10^e;>3qP|u}8#cWlQnoAAK8<6axO4961$Q??4~yCO&6OoaH7TR7=K(0|H#+HP z)T#S~&IRyZ2w*$0>XP|R2{%>@Ux+2SX~rsL-uxw)4R#WO zNg}Z7xuc2h*3bn+vYT(8*QEhk)!(e34Kpw4V$uuyp&K_M-_?&oGcOnBWdi^2Dswj? z`%$eG&L(}I>hd^uleRni!#SG|N4k_v2PCWov)E_)@d#Zw3K=|`qCoM&1(<-I<{3DN zaWWguCS9NF@9i2l>bTZTUyae1?fi06A?xi%wO5@mU+7ndrWl%IuD}+XlMgNTQp}5Z zyG2CPm-?ehp1@PD-Pd)ziFe!&5M{e7<6Tz8Q4#Ob#DKgmP7D=+d+S8AfRB2xQ2?1BvIayrFqniZxYQ_QqY} z>(Usl9VODi)@z9>eWrwVtR0}A(#gPUbrWCzBh2k!vv*5qYktdZD_o-5RWIzsUg#6u zspZ!B5*kDffE8 z-nyAx{bCX>M2xBsFRA&Qs8DfSyHe&=dWA_3U#XCqcLfI?4qQwF?y{mOY3=`%l;Pj6 z9|K%c;qATt-gi*8`PRmX97xb;l0Ept50B@6Tc zOi|hEhvPWM$x|lj*)X+VUYaO{z&ZZAQR& zMj;O80+F9|F{Y4>Y13=f>n)%?D+{h84EubJm>BxwT+vZI4x2*^FvV6PAcbc+_3Hhc z=&d2>d|FAPDxey4-jc=<>JVx)-EhXaeAFBSJqeSz+6g!ax>bbi#>1~JQ7?P%{<>}I zib*Uet#_wExxt3nqX!p(pI*CHL`wgB7wzN;A>hDem0$!b&n#6QW)9JX1}Z)!lH5>8 zHH%5RIRe$@xh^n==Zph{a=U~!Y~I{jv2NN@L6AULVToLSZHTt@-~0QzX3ZqVEa*>% zd?tM&tt{vtkJ0&pSdb@?kKdjMq-3N5R%@!Ze!UMlAE#*hb11^7i7@=rkJ!{gC42du z>-zu~fZDWGuT^W+6qLlk131Am0>6k4!Gx@cZY4;i1T``E(4Xw0?ZV>M9)Bk7f+@yR zJPOKU1e#(|q8w^^zcSC~AdClmC^#mgPP5H;XAJl^UlDp*Lfhsu?4Y*>t^yWd*2YOK zmK9il#{n1~385Um(6jk+IA!f9YzArg^g{r7`c{NqT3PggYG9>}ZV1?k!C>4>rb7Ap eM=jj5!x(Wz%9JYJG!gurG!hZpyR$Hb3 delta 4194 zcmaJ^ONd-q8P4hI7>K4HN%v&v?!0QuxGB7@fg{Kc6{JU`WF9|xUzxDYCzbZW1d-w0? z=m&rPraA1R^JjdlJc|8CKl{_G=;-S2FP-I>!taQur1@xoUe66oWr)b9;4jo8+;4Pd zk=Y)hy)|4isEu~b^%~mTQQ}5%T&L=l#Hq#O2Hnj3T*?HLiHSJxR`wxU@1sy3an z-cSoOuvWi|(Pzv%U36|&NVXacTPX=8V%+Wev1GoM<_lveiAP;Y%y(jR84|PA+qBiA zjFMI*P5M>eJZ__dby6a}xnD-tu2D}cf7GhS)nKKlj+4Q-hFe;hucc5th&g};9yHN8 z^UDZr74ievxm9!$HbN%5GwtExjbgtUR~p4zLf$Bj;$Rr-ay5vHw@67+)9;|7*>9o^ zchUMh*Yox!hE=oXb^5JXY0oo1zl-)D2Va;4wHwsXUV)aV`Bwn9zS2P3>mo0CcB=yDsB*cH zVxy<%k||Hn%jSClx>Cr!zi7kdLx|Fhpm=XGB@v{ z>n@4~2Cc2YjMm4a)W!uvYmVKG>wZaP_{gd- zuWfdK(XfK!am;F(d*;a$nz3@AWVk(PsATXUWPYSrwf&4GDXv;-aEV4%5J z@^Avd$^-%Q05{N0mjJ+T9E{ryrka{dJ1cwU9or=bDxQPIQk!5t?#o&*f@ub(c)yMI zkuvY!Ll=*Y^O17KoKe*+D@;a^vU#oJjGqL6Z5W-;Io|+1Nhbbm=qW9*Bb9;zP&=yq zM~pVl(-TxHGTw5hC8K3eg?-VgXyG~HT2se7Z4X~Kd6+NpnUIRsnqi8&vl;N&2bLsN zNm$nx*1eMS%-b_ypE(CwkCF;J8BEK=sie~$%doEZ>EukjDaxsPQwMj;e!skHlGK{7>lUao=xL{7Q(y> zcGeMa4XBOl$r6P~CS@8`Cv=>KT4bHJ)-oeT3kW+5k}1Z7=m|1wXyN~VAi1NtBR8Lt zdpI7{qqsfmYM~rd*MI{*oIum<5WD+1k!iDw`+Yppa+zvRi~`*6XBErmUvkFUDh^@~ zM;bZ=B)DJjN-85{i`PQlVmlI&3*5v>Fd}}U<#Wc`GY?wmh51NGE@d*Tjq72#u7U0? zYn=8-2@A8)K;Lj3;)>BuSd}d4X~{As0uygh`2sseo3j@lp2%oO+k|WQJ@DK98YyLd zKZwx=qUPZg-NZ()}GLKOrluJrH0EB%mK&wTyOS|K6>xmbyf{m zEMbM0b($aXKVI9qbLX>%U;OJ&g4cie^>2f8Tr_`cpdC;!p3TnV5xP7VCMRHAih6T8 zokT1!of*0Ww#RU=`_JyP`%g|jwuUgMs+(5*X`_R+Co?@^C7gwyU2t-Xn~%D4FSwef z9UzGyHt8X_XV6@H7uK)0Aea-H4_Y_EDWA3`p7xH_i)Cc8n^EKHj-V86r^$3QYW$tnXIi%H<&v1fn!n6QoWq7MM>SgQMw(%#)eyJL z=+Ev1yI{Whq~XgzhD}UVMw42#(W07pZc&2^)Lgx{)?hodnOjolpb1_OR7fWpXtqQH zCm%F$!*M;_gQhsmR&6d|J?fWd!$^%Zqi!nA#XA`Xl=