fix: ruler range drag/move, default cursor, SonicSF smooth stopAll

This commit is contained in:
2026-07-26 11:42:06 +07:00
parent d3c28d2fa7
commit e18b261943
3 changed files with 48 additions and 17 deletions
+36 -13
View File
@@ -5852,21 +5852,13 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
}), React.createElement("div", {
ref: rulerScrollRef,
className: "flex-1 overflow-hidden",
style: { cursor: 'default' },
onMouseDown: (e) => {
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left + e.currentTarget.scrollLeft;
const clickBeat = x / pixelsPerBeat;
const clickTime = clickBeat * beatSec;
if (clickTime >= 0) {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime, isPlaying: false } : s));
if (isPlaying) {
onStop();
setTimeout(() => {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
onPlayPause();
}, 40);
}
}
const clickInRange = loopStartBeat !== null && loopEndBeat !== null && clickBeat >= loopStartBeat && clickBeat <= loopEndBeat;
if (e.ctrlKey || e.metaKey) {
setLoopStartBeat(null);
setLoopEndBeat(null);
@@ -5883,9 +5875,40 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
}
return;
}
if (clickTime >= 0) {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime, isPlaying: false } : s));
if (isPlaying) {
onStop();
setTimeout(() => {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
onPlayPause();
}, 40);
}
}
if (clickInRange) {
const rangeWidth = loopEndBeat - loopStartBeat;
const offset = clickBeat - loopStartBeat;
rulerDragRef.current = { mode: 'move', startX: e.clientX, offset, rangeWidth, startBeat: loopStartBeat };
const onMove = (ev) => {
const r = rulerScrollRef.current;
if (!r || !rulerDragRef.current || rulerDragRef.current.mode !== 'move') return;
const rRect = r.getBoundingClientRect();
const bx = ev.clientX - rRect.left + r.scrollLeft;
const rawBeat = Math.max(0, bx / pixelsPerBeat);
const centerBeat = getSnapBeat(rawBeat - rulerDragRef.current.offset, snapVal);
const halfRange = rulerDragRef.current.rangeWidth / 2;
const newStart = Math.max(0, centerBeat - halfRange);
setLoopStartBeat(newStart);
setLoopEndBeat(newStart + rulerDragRef.current.rangeWidth);
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, selectionStart: newStart * beatSec, selectionEnd: (newStart + rulerDragRef.current.rangeWidth) * beatSec } : s));
};
const onUp = () => { rulerDragRef.current = null; document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); };
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
return;
}
const snappedStartBeat = getSnapBeat(clickBeat, snapVal);
const startData = { startX: e.clientX, startBeat: snappedStartBeat, scrollLeft: e.currentTarget.scrollLeft };
rulerDragRef.current = startData;
rulerDragRef.current = { startX: e.clientX, startBeat: snappedStartBeat, scrollLeft: e.currentTarget.scrollLeft };
const onMove = (ev) => {
const r = rulerScrollRef.current;
if (!r || !rulerDragRef.current) return;
@@ -5958,7 +5981,7 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
document.addEventListener('mouseup', onUp);
}
}), React.createElement("div", {
style: { position: 'absolute', left: '4px', right: '4px', top: 0, bottom: 0, cursor: 'grab' },
style: { position: 'absolute', left: '4px', right: '4px', top: 0, bottom: 0, cursor: 'move' },
onMouseDown: (e) => {
e.stopPropagation();
const startBeat = loopStartBeat;
File diff suppressed because one or more lines are too long
+11 -3
View File
@@ -153,7 +153,7 @@
osc.stop(stopAt);
const oscId = `${note}_${Date.now()}_${Math.random()}`;
activeOscillators[oscId] = osc;
activeOscillators[oscId] = { osc, gain: noteGain };
// Clean up active oscillator reference after it stops
setTimeout(() => {
@@ -164,8 +164,16 @@
},
stopAll: function () {
Object.values(activeOscillators).forEach(osc => {
try { osc.stop(); } catch (e) { }
const ctx = getCtx();
Object.values(activeOscillators).forEach(entry => {
try {
if (entry.gain) {
entry.gain.gain.cancelScheduledValues(ctx.currentTime);
entry.gain.gain.setValueAtTime(entry.gain.gain.value || 1, ctx.currentTime);
entry.gain.gain.linearRampToValueAtTime(0.001, ctx.currentTime + 0.008);
}
if (entry.osc) entry.osc.stop(ctx.currentTime + 0.01);
} catch (e) { }
});
Object.keys(activeOscillators).forEach(k => delete activeOscillators[k]);
},