diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 0ab6f8c..640136d 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -96,9 +96,12 @@ const scheduleCarlaNote = (synthEngine, channel, pitch, velocity, startWallTime, const delay = Math.max(0, (startWallTime - ctx.currentTime) * 1000); const carlaVel = Math.round((velocity || 0.8) * 127); const carlaCh = (synthEngine && synthEngine.midi_channel !== undefined) ? synthEngine.midi_channel : (channel || 0); + // Chẩn đoán: note MIDI item → Carla (bật console.log để verify route) + if (window.__carlaNoteLog === undefined) window.__carlaNoteLog = (window.__carlaNoteLog || 0) + 1; + console.log('[Carla] item note ch=' + carlaCh + ' pitch=' + pitch + ' vel=' + carlaVel + ' delay=' + delay.toFixed(0) + 'ms dur=' + (durMs || 300) + 'ms'); setTimeout(function () { window.SonicCarlaMidi.noteOn(carlaCh, pitch, carlaVel); }, delay); setTimeout(function () { window.SonicCarlaMidi.noteOff(carlaCh, pitch); }, delay + (durMs || 300) + 30); - } catch (e) { /* routing thất bại im lặng — soundfont vẫn chơi qua FluidSynth */ } + } catch (e) { console.warn('[Carla] scheduleCarlaNote error:', e); } }; // ── Carla bridge alive tracking + auto-open ──────────────────────────────── @@ -21197,7 +21200,9 @@ const App = () => { // Track VSTi + Carla local → MIDI item PHẢI play qua Carla bridge // (chỉ route Carla — KHÔNG play qua FluidSynth GM nữa, tránh âm // sai instrument chồng lên). Không có Carla → FluidSynth như cũ. - const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(track.synth_engine)) && window.__carlaRunning !== false; + // ⚠️ FIX: item route giống hệt keybed (không gate __carlaRunning — Carla mở + // thủ công/cũ vẫn phải route; gate cũ làm "keybed kêu, item câm"). + const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(track.synth_engine)); if (offsetTime < noteStartSec) { const delay = noteStartSec - offsetTime; const startTime = context.currentTime + delay; @@ -21344,7 +21349,7 @@ const App = () => { var subCh = assignTrackMidiChannel(subTrack, secTracks); // MIDI item trong SECTION → Carla bridge khi VSTi loaded // (chỉ route Carla — không play FluidSynth GM sai âm) - const subRouteCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(subTrack.synth_engine)) && window.__carlaRunning !== false; + const subRouteCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(subTrack.synth_engine)); if (subRouteCarla || window.SonicCarlaMidi.shouldRoutePlayback(subTrack.synth_engine)) ensureCarlaForPlayback(subTrack.synth_engine); if (offsetTime < noteStartMain) { @@ -21470,7 +21475,7 @@ const App = () => { const program = track.instrumentProgram !== undefined ? track.instrumentProgram : 0; // MIDI item → Carla bridge khi VSTi loaded (chỉ route Carla — không // play FluidSynth GM sai âm chồng lên) - const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(track.synth_engine)) && window.__carlaRunning !== false; + const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(track.synth_engine)); if (offsetTime < noteStartSec) { const delay = noteStartSec - offsetTime; const startTime = context.currentTime + delay; @@ -21550,7 +21555,7 @@ const App = () => { const durMs = effectiveDur * 1000; // Track VSTi + Carla local → note PHẢI play qua Carla bridge (chỉ route // Carla — không play FluidSynth GM sai âm chồng lên) - const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(synthEngine)) && window.__carlaRunning !== false; + const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(synthEngine)); if (!routeToCarla && window.SonicSF) { window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode, mainCh, synthEngine); } @@ -21586,7 +21591,7 @@ const App = () => { var schedTime = startWallTime + effStart; var durMs = effDur * 1000; // Ghost note → Carla bridge khi ghost track VSTi (chỉ route Carla) - var gRouteCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(ghostSynth)) && window.__carlaRunning !== false; + var gRouteCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(ghostSynth)); if (!gRouteCarla && window.SonicSF) { window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, schedTime, ghostProg, ghostDest, ghostCh, ghostSynth); } diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index d4a73b4..dccbd21 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -20,7 +20,8 @@ const ensureSonicInstrument=ctx=>{try{if(!window.SonicSF||!window.SonicSF.select // track dùng VSTi + có Carla local. Dùng chung cho mọi đường playback // (main timeline, local loop, piano roll, ghost notes) để MIDI item PHẢI play // qua Carla bridge khi VSTi được loaded. -const scheduleCarlaNote=(synthEngine,channel,pitch,velocity,startWallTime,durMs)=>{try{if(!window.SonicCarlaMidi||!window.SonicCarlaMidi.shouldRoutePlayback(synthEngine))return;const ctx=getAudioContext();const delay=Math.max(0,(startWallTime-ctx.currentTime)*1000);const carlaVel=Math.round((velocity||0.8)*127);const carlaCh=synthEngine&&synthEngine.midi_channel!==undefined?synthEngine.midi_channel:channel||0;setTimeout(function(){window.SonicCarlaMidi.noteOn(carlaCh,pitch,carlaVel);},delay);setTimeout(function(){window.SonicCarlaMidi.noteOff(carlaCh,pitch);},delay+(durMs||300)+30);}catch(e){/* routing thất bại im lặng — soundfont vẫn chơi qua FluidSynth */}};// ── Carla bridge alive tracking + auto-open ──────────────────────────────── +const scheduleCarlaNote=(synthEngine,channel,pitch,velocity,startWallTime,durMs)=>{try{if(!window.SonicCarlaMidi||!window.SonicCarlaMidi.shouldRoutePlayback(synthEngine))return;const ctx=getAudioContext();const delay=Math.max(0,(startWallTime-ctx.currentTime)*1000);const carlaVel=Math.round((velocity||0.8)*127);const carlaCh=synthEngine&&synthEngine.midi_channel!==undefined?synthEngine.midi_channel:channel||0;// Chẩn đoán: note MIDI item → Carla (bật console.log để verify route) +if(window.__carlaNoteLog===undefined)window.__carlaNoteLog=(window.__carlaNoteLog||0)+1;console.log('[Carla] item note ch='+carlaCh+' pitch='+pitch+' vel='+carlaVel+' delay='+delay.toFixed(0)+'ms dur='+(durMs||300)+'ms');setTimeout(function(){window.SonicCarlaMidi.noteOn(carlaCh,pitch,carlaVel);},delay);setTimeout(function(){window.SonicCarlaMidi.noteOff(carlaCh,pitch);},delay+(durMs||300)+30);}catch(e){console.warn('[Carla] scheduleCarlaNote error:',e);}};// ── Carla bridge alive tracking + auto-open ──────────────────────────────── // window.__carlaRunning: undefined = chưa biết | true = đang chạy | false = đã chết. // Quyết định route MIDI item EXCLUSIVE qua Carla hay fallback FluidSynth — // tránh "câm toàn phần" khi Carla bị đóng (route chỉ-Carla mà Carla chết = im lặng). @@ -1356,7 +1357,9 @@ const noteStartSec=item.startTime+(note.start_beat||0)*secondsPerBeat;if(noteSta const noteDurSec=(note.duration_beats||1)*secondsPerBeat;const noteEndSec=noteStartSec+noteDurSec;if(offsetTime{// ⚠️ Guard: stop → setTimeout sót không được fire (VU nhảy @@ -1377,7 +1380,7 @@ const activeStart=Math.max(clipStartMain,secStart);const activeEnd=Math.min(clip const subMidiItems=subTrack.midiItems||[];if(window.SonicSF&&subMidiItems.length>0){subMidiItems.forEach(item=>{const notes=item.notes||[];notes.forEach(note=>{const noteStartSecLocal=item.startTime+(note.start_beat||0)*secondsPerBeat;const noteDurSec=(note.duration_beats||1)*secondsPerBeat;const noteStartMain=secStart+noteStartSecLocal;const noteEndMain=noteStartMain+noteDurSec;// Only play if the note starts inside the Section item bounds and hasn't finished yet if(noteStartMain>=secStart&¬eStartMain0)&&window.SonicSF){var lcCh=assignTrackMidiChannel(track,tracks);const bpmVal=parseInt(bpm)||120;const secondsPerBeat=60.0/bpmVal;midiItems.forEach(item=>{const notes=item.notes||[];notes.forEach(note=>{const noteStartSec=item.startTime+(note.start_beat||0)*secondsPerBeat;const noteDurSec=(note.duration_beats||1)*secondsPerBeat;const noteEndSec=noteStartSec+noteDurSec;if(offsetTime{if(st.type!=='PIANO_ROLL')return;const context=getAudioContext();try{if(window.__ensureMasteringRouting)window.__ensureMasteringRouting();}catch(e){}refreshCarlaStatus();const midiNotes=notesOverride||st.notes||[];const bpmVal=parseInt(bpm)||120;const secondsPerBeat=60.0/bpmVal;const startWallTime=context.currentTime;const track=activeTracksRef.current?activeTracksRef.current.find(t=>t.id===st.trackId):null;if(!track){console.warn('[Play] Piano Roll track not found:',st.trackId);return;}const destNode=getOrCreateTrackNode(track,context);// Instrument context resolve từ TRACK live (nguồn duy nhất) — track "đã // loaded instrument" (instrumentProgram GM hoặc synth_engine soundfont) @@ -1408,11 +1411,11 @@ const instCtx=resolveTrackInstrumentCtx(track,activeTracksRef.current||[]);const // the item window, so no absolute-session offset is applied anywhere here. midiNotes.forEach(note=>{const noteOnBeat=note.start_beat||0;const noteDurBeat=note.duration_beats||1;const noteStartSec=noteOnBeat*secondsPerBeat;const noteDurSec=noteDurBeat*secondsPerBeat;if(noteStartSec+noteDurSec>offsetSeconds){const effectiveStart=Math.max(0,noteStartSec-offsetSeconds);const effectiveDur=noteDurSec-Math.max(0,offsetSeconds-noteStartSec);const scheduledTime=startWallTime+effectiveStart;const durMs=effectiveDur*1000;// Track VSTi + Carla local → note PHẢI play qua Carla bridge (chỉ route // Carla — không play FluidSynth GM sai âm chồng lên) -const routeToCarla=!!(window.SonicCarlaMidi&&window.SonicCarlaMidi.shouldRoutePlayback(synthEngine))&&window.__carlaRunning!==false;if(!routeToCarla&&window.SonicSF){window.SonicSF.playNote(note.pitch||60,note.velocity||0.8,durMs,scheduledTime,instrumentProgram,destNode,mainCh,synthEngine);}// MIDI items → Carla (track VSTi + Carla local): phát VSTi realtime +const routeToCarla=!!(window.SonicCarlaMidi&&window.SonicCarlaMidi.shouldRoutePlayback(synthEngine));if(!routeToCarla&&window.SonicSF){window.SonicSF.playNote(note.pitch||60,note.velocity||0.8,durMs,scheduledTime,instrumentProgram,destNode,mainCh,synthEngine);}// MIDI items → Carla (track VSTi + Carla local): phát VSTi realtime // (schedule theo audio clock bằng setTimeout — preview, timing gần đúng). if(routeToCarla){const carlaDelay=Math.max(0,(scheduledTime-context.currentTime)*1000);const carlaVel=Math.round((note.velocity||0.8)*127);const carlaCh=synthEngine&&synthEngine.midi_channel!==undefined?synthEngine.midi_channel:mainCh;const carlaPitch=note.pitch||60;setTimeout(function(){window.SonicCarlaMidi.noteOn(carlaCh,carlaPitch,carlaVel);},carlaDelay);setTimeout(function(){window.SonicCarlaMidi.noteOff(carlaCh,carlaPitch);},carlaDelay+durMs+30);}}});// Play ghost notes from active tracks var ghostLayers=st.ghostPlayLayers||[];ghostLayers.forEach(function(layer){var ghostTrack=allTracks.find(function(t){return t.id===layer.trackId;});var ghostProg=layer.instrumentProgram!==undefined?layer.instrumentProgram:ghostTrack?ghostTrack.instrumentProgram:undefined;var ghostSynth=layer.synthEngine||(ghostTrack?ghostTrack.synth_engine:undefined);if(ghostProg===undefined&&!ghostSynth)return;var ghostDest=getOrCreateTrackNode(ghostTrack,context);var ghostCh=ghostTrack?assignTrackMidiChannel(ghostTrack,allTracks):0;layer.notes.forEach(function(note){var beat=note.start_beat||0;var dur=note.duration_beats||1;var startSec=beat*secondsPerBeat;var durSec=dur*secondsPerBeat;if(startSec+durSec>offsetSeconds){var effStart=Math.max(0,startSec-offsetSeconds);var effDur=durSec-Math.max(0,offsetSeconds-startSec);var schedTime=startWallTime+effStart;var durMs=effDur*1000;// Ghost note → Carla bridge khi ghost track VSTi (chỉ route Carla) -var gRouteCarla=!!(window.SonicCarlaMidi&&window.SonicCarlaMidi.shouldRoutePlayback(ghostSynth))&&window.__carlaRunning!==false;if(!gRouteCarla&&window.SonicSF){window.SonicSF.playNote(note.pitch||60,note.velocity||0.8,durMs,schedTime,ghostProg,ghostDest,ghostCh,ghostSynth);}if(gRouteCarla)scheduleCarlaNote(ghostSynth,ghostCh,note.pitch||60,note.velocity||0.8,schedTime,durMs);}});});};const handlePlayPause=()=>{console.log('[Play] click activeTab=',activeTab,'isPlaying=',isPlaying,'subPlaying=',subTabs.filter(s=>s.isPlaying).length);if(activeTab!=='main'&&!activeTab.startsWith('session_')){// Sub-tab playback transport +var gRouteCarla=!!(window.SonicCarlaMidi&&window.SonicCarlaMidi.shouldRoutePlayback(ghostSynth));if(!gRouteCarla&&window.SonicSF){window.SonicSF.playNote(note.pitch||60,note.velocity||0.8,durMs,schedTime,ghostProg,ghostDest,ghostCh,ghostSynth);}if(gRouteCarla)scheduleCarlaNote(ghostSynth,ghostCh,note.pitch||60,note.velocity||0.8,schedTime,durMs);}});});};const handlePlayPause=()=>{console.log('[Play] click activeTab=',activeTab,'isPlaying=',isPlaying,'subPlaying=',subTabs.filter(s=>s.isPlaying).length);if(activeTab!=='main'&&!activeTab.startsWith('session_')){// Sub-tab playback transport const st=subTabs.find(s=>s.id===activeTab);if(!st||!st.buffer&&st.type!=='PIANO_ROLL')return;// MAIN/SECTION đang play (ngầm — MIDI section/main còn kêu sau khi mở // tab): Space phải STOP trước — không được play sub-tab (user: trong // PIANO ROLL không phát hiện MIDI đang play → space 2 lần mới stop). diff --git a/app/templates/index.html b/app/templates/index.html index 5a803f4..10d25ad 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -31,12 +31,12 @@ - - + + - + @@ -44,7 +44,7 @@ - +