From 2f2f84066c1756024f4d40824475d2415970869a Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 28 Jul 2026 09:15:51 +0700 Subject: [PATCH] fix: main playback default program 0 loads MAIN SoundFont Line 10283 same issue as 10393: when instrumentProgram is undefined (section cloned tracks), program defaults to 0. _playNoteFluid calls program_change(ch,0) on FluidSynth, which loads program 0 from whatever SoundFont is loaded (MAIN session's SF). Fix: pass undefined -> oscillator fallback for all tracks without assigned instrument. --- app/static/js/app.jsx | 2 +- app/static/js/app.precompiled.js | 2 +- wiki.md | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 256cc4a..8345513 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -10280,7 +10280,7 @@ const App = () => { const noteEndSec = noteStartSec + noteDurSec; if (offsetTime < noteEndSec) { const durationMs = noteDurSec * 1000; - const program = track.instrumentProgram !== undefined ? track.instrumentProgram : 0; + const program = track.instrumentProgram; if (offsetTime < noteStartSec) { const delay = noteStartSec - offsetTime; const startTime = context.currentTime + delay; diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 43178a3..512d967 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -299,7 +299,7 @@ const createChorusNode=(context,inputNode,outputNode)=>{const dryGain=context.cr const getOrCreateTrackNode=(track,context)=>{if(!track)return null;let node=activeTrackNodesRef.current[track.id];if(!node){const gainNode=context.createGain();const volDb=track.volumeDb??0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);gainNode.gain.setValueAtTime(volLinear,context.currentTime);const pannerNode=context.createStereoPanner();pannerNode.pan.setValueAtTime((track.pan??0)/100,context.currentTime);pannerNode.connect(context.destination);let fxStopFn;if(track.fxType==='chorus'){const fxInput=context.createGain();gainNode.connect(fxInput);const chorus=createChorusNode(context,fxInput,pannerNode);fxStopFn=chorus.stop;}else if(track.fxType==='reverb'){const fxInput=context.createGain();gainNode.connect(fxInput);createReverbNode(context,fxInput,pannerNode);fxStopFn=null;}else{gainNode.connect(pannerNode);}node={gainNode,pannerNode,fxStopFn};activeTrackNodesRef.current[track.id]=node;}return node.gainNode;};const getOrCreateSubTrackNode=(track,subTrack,context)=>{if(!track||!subTrack)return null;const subKey=track.id+'_sub_'+subTrack.id;let node=activeTrackNodesRef.current[subKey];if(!node){const gainNode=context.createGain();const volDb=subTrack.volumeDb??0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);gainNode.gain.setValueAtTime(volLinear,context.currentTime);const parentNode=getOrCreateTrackNode(track,context);gainNode.connect(parentNode);node={gainNode,pannerNode:null};activeTrackNodesRef.current[subKey]=node;}return node.gainNode;};const playMidiPreviewNote=(pitch,velocity=0.8,durationMs=500)=>{if(!window.SonicSF)return;const context=getAudioContext();const tab=subTabs.find(s=>s.id===activeTabRef.current);const trackId=tab?tab.trackId:selectedTrackId;const track=activeTracks.find(t=>t.id===trackId);const destNode=getOrCreateTrackNode(track,context);const program=track?track.instrumentProgram:undefined;var prevCh=window.SonicPianoRoll?window.SonicPianoRoll.getTrackMidiChannel(track,activeTracks):track?track.midiChannel:0;window.SonicSF.playNote(pitch,velocity,durationMs,context.currentTime,program,destNode,prevCh,track?track.synth_engine:undefined);};const startTrackPlayback=offsetTime=>{const context=getAudioContext();const hasSolo=activeTracks.some(t=>t.solo)||soloedTrackId!==null;activeTracks.forEach(track=>{const isPlayable=hasSolo?track.id===soloedTrackId||track.solo:!track.muted;if(!isPlayable)return;const gainNode=getOrCreateTrackNode(track,context);const pannerNode=activeTrackNodesRef.current[track.id].pannerNode;const clips=track.clips&&track.clips.length>0?track.clips:track.buffer?[{id:'default',buffer:track.buffer,startTime:track.startTime||0,name:track.name,speed:track.speed||1.0}]:[];clips.forEach(clip=>{if(!clip.buffer)return;const source=context.createBufferSource();source.buffer=clip.buffer;source.playbackRate.value=clip.speed||1.0;source.connect(gainNode);const clipStart=clip.startTime||0;const clipDuration=clip.buffer.duration/(clip.speed||1.0);const clipEnd=clipStart+clipDuration;if(offsetTime0)&&window.SonicSF){var trkCh=window.SonicPianoRoll?window.SonicPianoRoll.getTrackMidiChannel(track,activeTracks):track.midiChannel!==undefined?track.midiChannel:0;const bpmVal=parseInt(bpm)||120;const secondsPerBeat=60.0/bpmVal;midiItems.forEach(item=>{const itemEndSec=item.startTime+(item.duration||4);const notes=item.notes||[];notes.forEach(note=>{// note start/duration is in beats (for MIDI items) const noteStartSec=item.startTime+(note.start_beat||0)*secondsPerBeat;if(noteStartSec>=itemEndSec)return;// skip notes outside item's visual duration -const noteDurSec=(note.duration_beats||1)*secondsPerBeat;const noteEndSec=noteStartSec+noteDurSec;if(offsetTime