fix: brush shape draw + remove transport + loop repeat sound
This commit is contained in:
+41
-78
@@ -5217,25 +5217,20 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
const snappedPitch = snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch;
|
||||
if (!visited.includes(snappedPitch)) {
|
||||
const newPitches = [...visited, snappedPitch];
|
||||
const totalSpan = Math.max(0.125, beat - draggedNote.initialBeat);
|
||||
const perNoteDur = totalSpan / newPitches.length;
|
||||
const brushIds = draggedNote.brushIds || [];
|
||||
setNotes(prev => {
|
||||
const cleaned = prev.filter(n => !brushIds.includes(n.id) && n.id !== draggedNote.drawNoteId);
|
||||
const brushNotes = newPitches.map((p, i) => ({
|
||||
id: 'note_' + Date.now() + Math.random().toString(36).substr(2, 8) + '_' + i,
|
||||
pitch: p,
|
||||
start_beat: draggedNote.initialBeat + i * perNoteDur,
|
||||
duration_beats: Math.max(0.125, perNoteDur * 0.9),
|
||||
velocity: brushVelocityRef.current,
|
||||
pan: 0.0
|
||||
}));
|
||||
const newBrushIds = brushNotes.map(bn => bn.id);
|
||||
setSelectedNoteIds(newBrushIds);
|
||||
draggedNote.brushIds = newBrushIds;
|
||||
draggedNote.visitedPitches = newPitches;
|
||||
return [...cleaned, ...brushNotes];
|
||||
});
|
||||
const snappedBeat = getSnapBeat(beat, snapVal);
|
||||
const newNote = {
|
||||
id: 'note_' + Date.now() + Math.random().toString(36).substr(2, 8) + '_' + newPitches.length,
|
||||
pitch: snappedPitch,
|
||||
start_beat: snappedBeat,
|
||||
duration_beats: getSnapDuration(snapVal),
|
||||
velocity: brushVelocityRef.current,
|
||||
pan: 0.0
|
||||
};
|
||||
setNotes(prev => [...prev, newNote]);
|
||||
setSelectedNoteIds(prev => [...prev, newNote.id]);
|
||||
draggedNote.brushIds = [...brushIds, newNote.id];
|
||||
draggedNote.visitedPitches = newPitches;
|
||||
}
|
||||
const container = gridScrollRef.current;
|
||||
if (container) {
|
||||
@@ -5757,27 +5752,6 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
|
||||
title: st.instrumentName || "Synth",
|
||||
className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 max-w-[50px] ${st.instrumentName ? 'bg-violet-900 text-violet-300 border-violet-700' : 'bg-zinc-800 text-zinc-500 border-transparent hover:text-zinc-300'}`
|
||||
}, React.createElement("i", { "data-lucide": "music", className: "w-3 h-3 shrink-0" }), React.createElement("span", { className: "truncate text-[9px]" }, st.instrumentName || 'Synth')), React.createElement("div", {
|
||||
className: "flex items-center gap-0.5 ml-1"
|
||||
}, React.createElement("button", {
|
||||
onClick: () => setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: Math.max(0, (st.currentTime || 0) - beatSec * 4) } : s)),
|
||||
className: "w-6 h-6 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700",
|
||||
title: "Back 1 bar"
|
||||
}, React.createElement("i", { "data-lucide": "step-back", className: "w-3 h-3" })), React.createElement("button", {
|
||||
onClick: onPlayPause,
|
||||
className: `w-6 h-6 flex items-center justify-center rounded border ${isPlaying ? 'bg-emerald-600 text-black' : 'bg-cyan-600 text-white'} border-cyan-500`,
|
||||
title: isPlaying ? "Pause" : "Play"
|
||||
}, React.createElement("i", { "data-lucide": isPlaying ? "pause" : "play", className: "w-3 h-3 fill-current" })), React.createElement("button", {
|
||||
onClick: onStop,
|
||||
className: "w-6 h-6 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700",
|
||||
title: "Stop"
|
||||
}, React.createElement("i", { "data-lucide": "square", className: "w-3 h-3 fill-current" })), React.createElement("button", {
|
||||
onClick: onRecord,
|
||||
className: `w-6 h-6 flex items-center justify-center rounded border ${recordingState === 'RECORDING' ? 'bg-red-600 text-white border-red-500 animate-pulse' : 'bg-zinc-800 text-red-500 border-zinc-700'}`
|
||||
}, React.createElement("i", { "data-lucide": "circle", className: "w-3 h-3 fill-current" })), React.createElement("button", {
|
||||
onClick: () => setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: (st.currentTime || 0) + beatSec * 4 } : s)),
|
||||
className: "w-6 h-6 flex items-center justify-center bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded border border-zinc-700",
|
||||
title: "Forward 1 bar"
|
||||
}, React.createElement("i", { "data-lucide": "step-forward", className: "w-3 h-3" }))), React.createElement("div", {
|
||||
className: "flex items-center gap-1 ml-1 text-xs"
|
||||
}, React.createElement("span", { className: "text-zinc-500" }, "AI:"), React.createElement("input", {
|
||||
type: "number", value: aiBarStart, onChange: e => setAiBarStart(parseInt(e.target.value) || 0),
|
||||
@@ -9480,6 +9454,7 @@ const App = () => {
|
||||
currentTime: start,
|
||||
isPlaying: true
|
||||
} : s));
|
||||
schedulePianoRollMidi(st, start);
|
||||
startSubTabPlayback(st, start);
|
||||
animationFrameIdRef.current = requestAnimationFrame(updatePlayhead);
|
||||
return;
|
||||
@@ -9968,6 +9943,32 @@ const App = () => {
|
||||
});
|
||||
}
|
||||
};
|
||||
const schedulePianoRollMidi = (st, offsetSeconds) => {
|
||||
if (st.type !== 'PIANO_ROLL') return;
|
||||
const context = getAudioContext();
|
||||
const midiNotes = st.notes || [];
|
||||
const bpmVal = parseInt(bpm) || 120;
|
||||
const secondsPerBeat = 60.0 / bpmVal;
|
||||
const startWallTime = context.currentTime;
|
||||
const track = activeTracksRef.current ? activeTracksRef.current.find(t => t.id === st.trackId) : null;
|
||||
const destNode = getOrCreateTrackNode(track, context);
|
||||
const instrumentProgram = track ? track.instrumentProgram : undefined;
|
||||
midiNotes.forEach(note => {
|
||||
const noteOnBeat = note.start_beat || 0;
|
||||
const noteDurBeat = note.duration_beats || 1;
|
||||
const noteStartSec = noteOnBeat * secondsPerBeat;
|
||||
const noteDurSec = noteDurBeat * secondsPerBeat;
|
||||
if (noteStartSec + noteDurSec > offsetSeconds) {
|
||||
const effectiveStart = Math.max(0, noteStartSec - offsetSeconds);
|
||||
const effectiveDur = noteDurSec - Math.max(0, offsetSeconds - noteStartSec);
|
||||
const scheduledTime = startWallTime + effectiveStart;
|
||||
const durMs = effectiveDur * 1000;
|
||||
if (window.SonicSF) {
|
||||
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const handlePlayPause = () => {
|
||||
if (activeTab !== 'main' && !activeTab.startsWith('session_')) {
|
||||
// Sub-tab playback transport
|
||||
@@ -9984,45 +9985,7 @@ const App = () => {
|
||||
const startOffset = st.currentTime || 0;
|
||||
|
||||
if (st.type === 'PIANO_ROLL') {
|
||||
// Piano Roll: play MIDI notes via SonicSF
|
||||
const context = getAudioContext();
|
||||
const notes = st.notes || [];
|
||||
const bpmVal = parseInt(bpm) || 120;
|
||||
const secondsPerBeat = 60.0 / bpmVal;
|
||||
const startWallTime = context.currentTime;
|
||||
|
||||
// Find the track to get instrument settings
|
||||
const track = activeTracks.find(t => t.id === st.trackId);
|
||||
const destNode = getOrCreateTrackNode(track, context);
|
||||
const instrumentProgram = track ? track.instrumentProgram : undefined;
|
||||
const instrumentName = track ? track.instrumentName : undefined;
|
||||
|
||||
notes.forEach(note => {
|
||||
const noteOnBeat = note.start_beat || 0;
|
||||
const noteDurBeat = note.duration_beats || 1;
|
||||
const noteStartSec = noteOnBeat * secondsPerBeat;
|
||||
const noteDurSec = noteDurBeat * secondsPerBeat;
|
||||
|
||||
if (noteStartSec + noteDurSec > startOffset) {
|
||||
const effectiveStart = Math.max(0, noteStartSec - startOffset);
|
||||
const effectiveDur = noteDurSec - Math.max(0, startOffset - noteStartSec);
|
||||
const scheduledTime = startWallTime + effectiveStart;
|
||||
const durMs = effectiveDur * 1000;
|
||||
|
||||
if (window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
durMs,
|
||||
scheduledTime,
|
||||
instrumentProgram,
|
||||
destNode
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Call startSubTabPlayback to spin up the silent buffer playhead timing/references
|
||||
schedulePianoRollMidi(st, startOffset);
|
||||
startSubTabPlayback(st, startOffset);
|
||||
} else {
|
||||
startSubTabPlayback(st, startOffset);
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user