fix: realtime velocity with updatedNotes + ruler restart without onStop

This commit is contained in:
2026-07-26 11:28:47 +07:00
parent 7ede9bbf72
commit d3a3adca8a
2 changed files with 49 additions and 34 deletions
+34 -19
View File
@@ -5424,12 +5424,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
const currentNote = notes[cursorNoteIdx]; const currentNote = notes[cursorNoteIdx];
const currentVal = ccMode === 'pan' ? ((currentNote.pan || 0) / 2.0 + 0.5) : (currentNote.velocity !== undefined ? currentNote.velocity : 0.8); 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) { if (Math.abs(currentVal - val) > 0.001) {
setNotes(prev => prev.map((n, i) => { const updatedNotes = notes.map((n, i) => {
if (i !== cursorNoteIdx) return n; if (i !== cursorNoteIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 }; if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val }; return { ...n, velocity: val };
})); });
if (isPlaying && onRescheduleMidi) onRescheduleMidi(); setNotes(updatedNotes);
if (isPlaying && onRescheduleMidi) onRescheduleMidi(updatedNotes);
} }
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [cursorNoteIdx] }; ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [cursorNoteIdx] };
} else { } else {
@@ -5444,11 +5445,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
if (noteIdx !== -1) { if (noteIdx !== -1) {
const currentVal = ccMode === 'pan' ? ((notes[noteIdx].pan || 0) / 2.0 + 0.5) : (notes[noteIdx].velocity !== undefined ? notes[noteIdx].velocity : 0.8); 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) { if (Math.abs(currentVal - val) > 0.001) {
setNotes(prev => prev.map((n, i) => { const updatedNotes = notes.map((n, i) => {
if (i !== noteIdx) return n; if (i !== noteIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 }; if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val }; return { ...n, velocity: val };
})); });
setNotes(updatedNotes);
if (isPlaying && onRescheduleMidi) onRescheduleMidi(updatedNotes);
} }
} }
}; };
@@ -5474,12 +5477,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
if (cursorNote && selectedNoteIds.includes(cursorNote.id) && !painted.includes(cursorNoteIdx)) { if (cursorNote && selectedNoteIds.includes(cursorNote.id) && !painted.includes(cursorNoteIdx)) {
const currentVal = ccMode === 'pan' ? ((cursorNote.pan || 0) / 2.0 + 0.5) : (cursorNote.velocity !== undefined ? cursorNote.velocity : 0.8); 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) { if (Math.abs(currentVal - val) > 0.001) {
setNotes(prev => prev.map((n, i) => { const updatedNotes = notes.map((n, i) => {
if (i !== cursorNoteIdx) return n; if (i !== cursorNoteIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 }; if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val }; return { ...n, velocity: val };
})); });
if (isPlaying && onRescheduleMidi) onRescheduleMidi(); setNotes(updatedNotes);
if (isPlaying && onRescheduleMidi) onRescheduleMidi(updatedNotes);
} }
drag.lastPainted = [...painted, cursorNoteIdx]; drag.lastPainted = [...painted, cursorNoteIdx];
} }
@@ -5491,12 +5495,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
if (candidateIdx !== -1 && !painted.includes(candidateIdx)) { if (candidateIdx !== -1 && !painted.includes(candidateIdx)) {
const currentVal = ccMode === 'pan' ? ((notes[candidateIdx].pan || 0) / 2.0 + 0.5) : (notes[candidateIdx].velocity !== undefined ? notes[candidateIdx].velocity : 0.8); 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) { if (Math.abs(currentVal - val) > 0.001) {
setNotes(prev => prev.map((n, i) => { const updatedNotes = notes.map((n, i) => {
if (i !== candidateIdx) return n; if (i !== candidateIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 }; if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val }; return { ...n, velocity: val };
})); });
if (isPlaying && onRescheduleMidi) onRescheduleMidi(); setNotes(updatedNotes);
if (isPlaying && onRescheduleMidi) onRescheduleMidi(updatedNotes);
} }
drag.lastPainted = [...painted, candidateIdx]; drag.lastPainted = [...painted, candidateIdx];
} }
@@ -5855,11 +5860,21 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
if (clickTime >= 0) { if (clickTime >= 0) {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s)); setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
if (isPlaying) { if (isPlaying) {
onStop();
setTimeout(() => {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s)); setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
onPlayPause(); setTimeout(() => {
}, 30); stopAllPlayback();
const sub = subTabs.find(s2 => s2.id === st.id);
if (sub) {
const ctx = getAudioContext();
startOffsetTimeRef.current = clickTime;
startAudioTimeRef.current = ctx.currentTime;
startBufferOffsetRef.current = clickTime * (sub.speed || 1.0);
schedulePianoRollMidi(sub, clickTime);
startSubTabPlayback(sub, clickTime);
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime, isPlaying: true } : s));
animationFrameIdRef.current = requestAnimationFrame(updatePlayhead);
}
}, 20);
} }
} }
if (e.ctrlKey || e.metaKey) { if (e.ctrlKey || e.metaKey) {
@@ -10027,10 +10042,10 @@ const App = () => {
}); });
} }
}; };
const schedulePianoRollMidi = (st, offsetSeconds) => { const schedulePianoRollMidi = (st, offsetSeconds, notesOverride) => {
if (st.type !== 'PIANO_ROLL') return; if (st.type !== 'PIANO_ROLL') return;
const context = getAudioContext(); const context = getAudioContext();
const midiNotes = st.notes || []; const midiNotes = notesOverride || st.notes || [];
const bpmVal = parseInt(bpm) || 120; const bpmVal = parseInt(bpm) || 120;
const secondsPerBeat = 60.0 / bpmVal; const secondsPerBeat = 60.0 / bpmVal;
const startWallTime = context.currentTime; const startWallTime = context.currentTime;
@@ -15814,7 +15829,7 @@ const App = () => {
onMidiInputSelect: handleMidiInputSelect, onMidiInputSelect: handleMidiInputSelect,
activeMidiPitches: activeMidiPitches, activeMidiPitches: activeMidiPitches,
onInstrumentSelect: (trackId) => { openInstrumentSelector(trackId); }, onInstrumentSelect: (trackId) => { openInstrumentSelector(trackId); },
onRescheduleMidi: () => { onRescheduleMidi: (updatedNotes) => {
const playingSub = subTabs.find(s => s.id === activeTab && s.type === 'PIANO_ROLL' && s.isPlaying); const playingSub = subTabs.find(s => s.id === activeTab && s.type === 'PIANO_ROLL' && s.isPlaying);
if (playingSub) { if (playingSub) {
const offset = playingSub.currentTime || 0; const offset = playingSub.currentTime || 0;
@@ -15824,7 +15839,7 @@ const App = () => {
startOffsetTimeRef.current = offset; startOffsetTimeRef.current = offset;
startAudioTimeRef.current = ctx.currentTime; startAudioTimeRef.current = ctx.currentTime;
startBufferOffsetRef.current = offset * (playingSub.speed || 1.0); startBufferOffsetRef.current = offset * (playingSub.speed || 1.0);
schedulePianoRollMidi(playingSub, offset); schedulePianoRollMidi(playingSub, offset, updatedNotes);
}, 20); }, 20);
} }
} }
File diff suppressed because one or more lines are too long