fix: focused MIDI item notes brighter than siblings

This commit is contained in:
2026-07-31 09:53:17 +07:00
parent 60d05e2b4d
commit 698fa818e9
2 changed files with 7 additions and 6 deletions
+2 -2
View File
@@ -182,8 +182,8 @@ for(let pitch=0;pitch<128;pitch++){const y=(127-pitch)*NoteHeight;const isBlack=
let snapBeats=0.25;if(snapValue==='1')snapBeats=1.0;else if(snapValue==='1/2')snapBeats=0.5;else if(snapValue==='1/4')snapBeats=0.25;else if(snapValue==='1/8')snapBeats=0.125;else if(snapValue==='1/16')snapBeats=0.0625;else if(snapValue==='4')snapBeats=4.0;else if(snapValue==='1/32')snapBeats=0.03125;for(let beat=0;beat<=viewBeats;beat+=snapBeats){const x=beat*pixelsPerBeat;if(x>viewWidth)break;const isBar=beat%timeSigNum===0;ctx.strokeStyle=isBar?'#444450':'#2d2d35';ctx.lineWidth=isBar?1.2:0.6;ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,h);ctx.stroke();}// Layer 2: Ghost Notes (background reference from other tracks)
if(showGhostNotes&&sessionSyncMode&&ghostLayers.length>0){ghostLayers.forEach(function(layer){ctx.save();var isSameTrackLayer=layer.isSameTrack;ctx.globalAlpha=isSameTrackLayer?0.35:0.25;ctx.fillStyle=isSameTrackLayer?'rgba(251, 191, 36, 0.35)':(layer.track_color||'#888');ctx.strokeStyle=isSameTrackLayer?'rgba(245, 158, 11, 0.6)':(layer.track_color||'#888');layer.notes.forEach(function(note){var snapStart=snapValue!=='free'?getSnapBeat(note.relative_start_beat,snapValue):note.relative_start_beat;var rawEnd=note.relative_start_beat+note.duration_beats;var snapEnd=snapValue!=='free'?getSnapBeat(rawEnd,snapValue):rawEnd;var x=(renderBeatOffset+snapStart)*pixelsPerBeat;var y=(127-note.pitch)*NoteHeight;var w=Math.max(2,(snapEnd-snapStart)*pixelsPerBeat);var h=NoteHeight-1;ctx.fillRect(x,y,w,h);if(isSameTrackLayer)ctx.strokeRect(x+0.5,y+0.5,w-1,h-1);});ctx.restore();});}// Layer 3: Active notes with velocity layer representation
notes.forEach(note=>{const snapStart=snapValue!=='free'?getSnapBeat(note.start_beat,snapValue):note.start_beat;const rawEnd=note.start_beat+note.duration_beats;const snapEnd=snapValue!=='free'?getSnapBeat(rawEnd,snapValue):rawEnd;const x=(renderBeatOffset+snapStart)*pixelsPerBeat;const y=(127-note.pitch)*NoteHeight;const w=Math.max(2,(snapEnd-snapStart)*pixelsPerBeat);const isSelected=selectedNoteIds.includes(note.id);// Draw background of note
ctx.fillStyle=isSelected?'rgba(59, 130, 246, 0.5)':'rgba(234, 179, 8, 0.5)';ctx.strokeStyle=isSelected?'#3b82f6':'#f59e0b';ctx.lineWidth=isSelected?1.5:1.2;ctx.fillRect(x+1,y+1,w-2,NoteHeight-2);ctx.strokeRect(x+1,y+1,w-2,NoteHeight-2);// Draw velocity layer (solid yellow/blue bar inside, proportional to velocity)
const vel=note.velocity!==undefined?note.velocity:0.8;const velW=Math.max(2,(w-2)*vel);ctx.fillStyle=isSelected?'#3b82f6':'#eab308';ctx.fillRect(x+1,y+1,velW,NoteHeight-2);});// Draw real-time recording notes
const playingNow=st.isPlaying;ctx.fillStyle=isSelected?'rgba(96, 165, 250, 0.6)':(playingNow?'rgba(254, 240, 138, 0.75)':'rgba(253, 224, 71, 0.6)');ctx.strokeStyle=isSelected?'#60a5fa':(playingNow?'#fef08a':'#fde047');ctx.lineWidth=isSelected?1.5:1.2;ctx.fillRect(x+1,y+1,w-2,NoteHeight-2);ctx.strokeRect(x+1,y+1,w-2,NoteHeight-2);// Draw velocity layer (solid yellow/blue bar inside, proportional to velocity)
const vel=note.velocity!==undefined?note.velocity:0.8;const velW=Math.max(2,(w-2)*vel);ctx.fillStyle=isSelected?'#60a5fa':(playingNow?'#fde047':'#facc15');ctx.fillRect(x+1,y+1,velW,NoteHeight-2);});// Draw real-time recording notes
if(recordingState==='RECORDING'&&recTempMidiNotes&&recTempMidiNotes.length>0){recTempMidiNotes.forEach(note=>{const snapStart=snapValue!=='free'?getSnapBeat(note.start_beat,snapValue):note.start_beat;const rawEnd=note.start_beat+(note.duration_beats||0.25);const snapEnd=snapValue!=='free'?getSnapBeat(rawEnd,snapValue):rawEnd;const x=(renderBeatOffset+snapStart)*pixelsPerBeat;const y=(127-note.pitch)*NoteHeight;const w=Math.max(2,(snapEnd-snapStart)*pixelsPerBeat);ctx.fillStyle='rgba(255, 100, 100, 0.35)';ctx.strokeStyle='#ff6464';ctx.lineWidth=1;ctx.fillRect(x+1,y+1,Math.max(2,w-2),NoteHeight-2);ctx.strokeRect(x+1,y+1,Math.max(2,w-2),NoteHeight-2);const vel=Math.min(1,note.velocity||0.8);ctx.fillStyle='#ff6464';ctx.fillRect(x+1,y+1,Math.max(2,(w-2)*vel),NoteHeight-2);});}// Draw selection marquee if active
if(selectionMarquee){const minBeat=Math.min(selectionMarquee.startBeat,selectionMarquee.currentBeat);const maxBeat=Math.max(selectionMarquee.startBeat,selectionMarquee.currentBeat);const minPitch=Math.min(selectionMarquee.startPitch,selectionMarquee.currentPitch);const maxPitch=Math.max(selectionMarquee.startPitch,selectionMarquee.currentPitch);const mx=minBeat*pixelsPerBeat;const my=(127-maxPitch)*NoteHeight;const mw=(maxBeat-minBeat)*pixelsPerBeat;const mh=(maxPitch-minPitch+1)*NoteHeight;ctx.fillStyle='rgba(59, 130, 246, 0.15)';ctx.strokeStyle='#3b82f6';ctx.lineWidth=1;ctx.setLineDash([4,4]);ctx.fillRect(mx,my,mw,mh);ctx.strokeRect(mx,my,mw,mh);ctx.setLineDash([]);}// Draw playhead
if(st.currentTime!==undefined&&st.currentTime!==null){const phBeat=st.currentTime/(60.0/(parseInt(bpm)||120));const phX=phBeat*pixelsPerBeat;if(phX>=0&&phX<=viewWidth){ctx.strokeStyle='#f59e0b';ctx.lineWidth=1.5;ctx.beginPath();ctx.moveTo(phX,0);ctx.lineTo(phX,(128-PITCH_START)*NoteHeight);ctx.stroke();}}},[notes,snapValue,rollZoom,selectedNoteIds,selectionMarquee,st.currentTime,bpm,viewWidth,viewBeats,recordingState,recTempMidiNotes,showGhostNotes,sessionSyncMode,ghostLayers,renderBeatOffset,renderTick]);React.useLayoutEffect(()=>{const canvas=ccCanvasRef.current;if(!canvas)return;const ctx=canvas.getContext('2d');const dpr=window.devicePixelRatio||1;const h=ccHeight;canvas.width=viewWidth*dpr;canvas.height=h*dpr;ctx.scale(dpr,dpr);ctx.fillStyle='#161616';ctx.fillRect(0,0,viewWidth,h);ctx.strokeStyle='#252525';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(0,h/2);ctx.lineTo(viewWidth,h/2);ctx.stroke();notes.forEach(note=>{const x=(renderBeatOffset+note.start_beat)*pixelsPerBeat;const isSelected=selectedNoteIds.includes(note.id);let val=note.velocity!==undefined?note.velocity:0.8;if(ccMode==='pan'){val=(note.pan!==undefined?note.pan:0.0)*0.5+0.5;}const stemH=val*(h-20)+10;const y=h-stemH;ctx.strokeStyle=ccMode==='pan'?isSelected?'#60a5fa':'#a78bfa':isSelected?'#3b82f6':'#fbbf24';ctx.lineWidth=2.5;ctx.beginPath();ctx.moveTo(x,h);ctx.lineTo(x,y);ctx.stroke();ctx.fillStyle=ccMode==='pan'?isSelected?'#3b82f6':'#c084fc':isSelected?'#3b82f6':'#fbbf24';ctx.beginPath();ctx.arc(x,y,3.5,0,2*Math.PI);ctx.fill();});},[notes,ccMode,rollZoom,viewWidth,selectedNoteIds,renderBeatOffset]);React.useEffect(()=>{const scrollToC3=()=>{if(gridScrollRef.current){const ch=gridScrollRef.current.clientHeight||400;gridScrollRef.current.scrollTop=Math.max(0,(127-48)*NoteHeight+NoteHeight-ch);}};scrollToC3();const timer=setTimeout(scrollToC3,100);return()=>clearTimeout(timer);},[]);// Sync ghost play data to subTab state for playback integration