fix: piano roll draw notes snapped to grid via getSnapBeat

note.start_beat + duration_beats snapped before rendering.
Applies to active, ghost, and recording notes.
This commit is contained in:
2026-07-30 21:15:38 +07:00
parent 5ed6acd4f5
commit 323be6a6f4
3 changed files with 24 additions and 9 deletions
+15 -6
View File
@@ -6175,9 +6175,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
ctx.fillStyle = layer.track_color || '#888';
ctx.strokeStyle = layer.track_color || '#888';
layer.notes.forEach(function(note) {
var x = (renderBeatOffset + note.relative_start_beat) * pixelsPerBeat;
var snapStart = snapVal !== 'free' ? getSnapBeat(note.relative_start_beat, snapVal) : note.relative_start_beat;
var rawEnd = note.relative_start_beat + note.duration_beats;
var snapEnd = snapVal !== 'free' ? getSnapBeat(rawEnd, snapVal) : rawEnd;
var x = (renderBeatOffset + snapStart) * pixelsPerBeat;
var y = (127 - note.pitch) * NoteHeight;
var w = note.duration_beats * pixelsPerBeat;
var w = Math.max(2, (snapEnd - snapStart) * pixelsPerBeat);
var h = NoteHeight - 1;
ctx.fillRect(x, y, w, h);
});
@@ -6187,9 +6190,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
// Layer 3: Active notes with velocity layer representation
notes.forEach((note) => {
const x = (renderBeatOffset + note.start_beat) * pixelsPerBeat;
const snapStart = snapVal !== 'free' ? getSnapBeat(note.start_beat, snapVal) : note.start_beat;
const rawEnd = note.start_beat + note.duration_beats;
const snapEnd = snapVal !== 'free' ? getSnapBeat(rawEnd, snapVal) : rawEnd;
const x = (renderBeatOffset + snapStart) * pixelsPerBeat;
const y = (127 - note.pitch) * NoteHeight;
const w = note.duration_beats * pixelsPerBeat;
const w = Math.max(2, (snapEnd - snapStart) * pixelsPerBeat);
const isSelected = selectedNoteIds.includes(note.id);
// Draw background of note
@@ -6209,9 +6215,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
// Draw real-time recording notes
if (recordingState === 'RECORDING' && recTempMidiNotes && recTempMidiNotes.length > 0) {
recTempMidiNotes.forEach(note => {
const x = (renderBeatOffset + note.start_beat) * pixelsPerBeat;
const snapStart = snapVal !== 'free' ? getSnapBeat(note.start_beat, snapVal) : note.start_beat;
const rawEnd = note.start_beat + (note.duration_beats || 0.25);
const snapEnd = snapVal !== 'free' ? getSnapBeat(rawEnd, snapVal) : rawEnd;
const x = (renderBeatOffset + snapStart) * pixelsPerBeat;
const y = (127 - note.pitch) * NoteHeight;
const w = (note.duration_beats || 0.25) * pixelsPerBeat;
const w = Math.max(2, (snapEnd - snapStart) * pixelsPerBeat);
ctx.fillStyle = 'rgba(255, 100, 100, 0.35)';
ctx.strokeStyle = '#ff6464';
ctx.lineWidth = 1;