fix: brush draw only pitch change + CC velocity skip unchanged
This commit is contained in:
+51
-38
@@ -5209,18 +5209,9 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
const rawDur = beat - draggedNote.startOffsetBeat;
|
||||
const newDur = getSnapBeat(Math.max(0.125, rawDur), snapVal);
|
||||
const visited = draggedNote.visitedPitches || [];
|
||||
if (visited.length <= 1) {
|
||||
setNotes(prev => prev.map(n => {
|
||||
if (n.id !== draggedNote.drawNoteId) return n;
|
||||
return { ...n, duration_beats: newDur };
|
||||
}));
|
||||
}
|
||||
const snappedPitch = snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch;
|
||||
const lastDrawBeat = draggedNote.lastDrawBeat !== undefined ? draggedNote.lastDrawBeat : draggedNote.startOffsetBeat;
|
||||
const minBeatStep = Math.max(0.03125, getSnapDuration(snapVal) * 0.25);
|
||||
const pitchChanged = !visited.includes(snappedPitch);
|
||||
const beatChanged = Math.abs(beat - lastDrawBeat) >= minBeatStep;
|
||||
if (pitchChanged || (beatChanged && visited.length > 0)) {
|
||||
if (pitchChanged) {
|
||||
const brushIds = draggedNote.brushIds || [];
|
||||
const newNote = {
|
||||
id: 'note_' + Date.now() + Math.random().toString(36).substr(2, 8) + '_' + (visited.length + 1),
|
||||
@@ -5233,9 +5224,15 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
setNotes(prev => [...prev, newNote]);
|
||||
setSelectedNoteIds(prev => [...prev, newNote.id]);
|
||||
draggedNote.brushIds = [...brushIds, newNote.id];
|
||||
draggedNote.lastDrawBeat = beat;
|
||||
if (pitchChanged) {
|
||||
draggedNote.visitedPitches = [...visited, snappedPitch];
|
||||
draggedNote.visitedPitches = [...visited, snappedPitch];
|
||||
} else {
|
||||
const brushIds = draggedNote.brushIds || [];
|
||||
const lastBrushId = brushIds.length > 0 ? brushIds[brushIds.length - 1] : draggedNote.drawNoteId;
|
||||
if (lastBrushId) {
|
||||
setNotes(prev => prev.map(n => {
|
||||
if (n.id !== lastBrushId) return n;
|
||||
return { ...n, duration_beats: newDur };
|
||||
}));
|
||||
}
|
||||
}
|
||||
const container = gridScrollRef.current;
|
||||
@@ -5409,11 +5406,15 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
});
|
||||
}
|
||||
if (cursorNoteIdx !== -1 && selectedNoteIds.includes(notes[cursorNoteIdx] ? notes[cursorNoteIdx].id : -1)) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== cursorNoteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
const currentNote = notes[cursorNoteIdx];
|
||||
const currentVal = ccMode === 'pan' ? ((currentNote.pan || 0) / 2.0 + 0.5) : (currentNote.velocity !== undefined ? currentNote.velocity : 0.8);
|
||||
if (Math.abs(currentVal - val) > 0.001) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== cursorNoteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
}
|
||||
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [cursorNoteIdx] };
|
||||
} else {
|
||||
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [] };
|
||||
@@ -5425,11 +5426,14 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
}
|
||||
|
||||
if (noteIdx !== -1) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== noteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
const currentVal = ccMode === 'pan' ? ((notes[noteIdx].pan || 0) / 2.0 + 0.5) : (notes[noteIdx].velocity !== undefined ? notes[noteIdx].velocity : 0.8);
|
||||
if (Math.abs(currentVal - val) > 0.001) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== noteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5460,11 +5464,14 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
if (cursorNoteIdx !== -1) {
|
||||
const cursorNote = notes[cursorNoteIdx];
|
||||
if (cursorNote && selectedNoteIds.includes(cursorNote.id) && !painted.includes(cursorNoteIdx)) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== cursorNoteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
const currentVal = ccMode === 'pan' ? ((cursorNote.pan || 0) / 2.0 + 0.5) : (cursorNote.velocity !== undefined ? cursorNote.velocity : 0.8);
|
||||
if (Math.abs(currentVal - val) > 0.001) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== cursorNoteIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
}
|
||||
drag.lastPainted = [...painted, cursorNoteIdx];
|
||||
}
|
||||
}
|
||||
@@ -5475,11 +5482,14 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
const candidateIdx = notes.findIndex(n => snappedBeat >= n.start_beat && snappedBeat <= n.start_beat + n.duration_beats);
|
||||
|
||||
if (candidateIdx !== -1 && !painted.includes(candidateIdx)) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== candidateIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
const currentVal = ccMode === 'pan' ? ((notes[candidateIdx].pan || 0) / 2.0 + 0.5) : (notes[candidateIdx].velocity !== undefined ? notes[candidateIdx].velocity : 0.8);
|
||||
if (Math.abs(currentVal - val) > 0.001) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== candidateIdx) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
}
|
||||
drag.lastPainted = [...painted, candidateIdx];
|
||||
} else if (candidateIdx === -1) {
|
||||
let nearest = -1;
|
||||
@@ -5490,11 +5500,14 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
if (d < minDist) { minDist = d; nearest = idx; }
|
||||
});
|
||||
if (nearest !== -1 && !painted.includes(nearest)) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== nearest) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
const currentVal = ccMode === 'pan' ? ((notes[nearest].pan || 0) / 2.0 + 0.5) : (notes[nearest].velocity !== undefined ? notes[nearest].velocity : 0.8);
|
||||
if (Math.abs(currentVal - val) > 0.001) {
|
||||
setNotes(prev => prev.map((n, i) => {
|
||||
if (i !== nearest) return n;
|
||||
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||
return { ...n, velocity: val };
|
||||
}));
|
||||
}
|
||||
drag.lastPainted = [...painted, nearest];
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -195,3 +195,8 @@
|
||||
- **Tóm tắt thay đổi:** Brush drawing: theo dõi cả `lastDrawBeat` và `visitedPitches`. Tạo note mới khi pitch thay đổi HOẶC beat di chuyển >= minBeatStep (25% của grid duration). Dùng raw `beat` thay vì `getSnapBeat` để note theo sát vị trí chuột. Khởi tạo `lastDrawBeat: start` trong draggedNote. Fix lỗi draw bị ngắt quãng do chỉ tạo note khi pitch thay đổi.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||
- **Ghi chú/Test (nếu có):** `npm run build` pass.
|
||||
|
||||
### [2026-07-26 10:47] Task: Fix brush draw only pitch change + CC velocity flicker skip
|
||||
- **Tóm tắt thay đổi:** (1) Brush draw: chỉ tạo note mới khi `pitchChanged` (pitch chưa visited). Nếu cùng pitch, extend `duration_beats` của note cuối cùng trong `brushIds` — không tạo note mới trùng pitch. (2) CC velocity: thêm check `Math.abs(currentVal - val) > 0.001` trước mỗi `setNotes` — skip render khi velocity/pan không thay đổi, loại bỏ flicker cho cả selected + non-selected mode.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||
- **Ghi chú/Test (nếu có):** `npm run build` pass. currentVal tính: velocity = note.velocity || 0.8; pan = (note.pan || 0) / 2 + 0.5.
|
||||
|
||||
Reference in New Issue
Block a user