fix: session-absolute timing for playhead + MIDI scheduling
- st.currentTime is now session-absolute (0 = session bar 0) - schedulePianoRollMidi adds sessionBeatOffset to note timing so notes at renderBeatOffset beats are scheduled with correct delay - Playhead rendering: removed renderBeatOffset (uses st.currentTime directly as session-absolute beats) - handleEditMidiInTab/handleSwitchMidiItem: currentTime = 0 - Ruler click: clickTime = clickBeat * beatSec (session-absolute)
This commit is contained in:
+14
-11
@@ -4646,7 +4646,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
|||||||
var trk = (activeTracks || []).find(function(t) { return t.id === match._trackId; });
|
var trk = (activeTracks || []).find(function(t) { return t.id === match._trackId; });
|
||||||
var newBeatOff = (match.startTime / secondsPerBar) * timeSigNum;
|
var newBeatOff = (match.startTime / secondsPerBar) * timeSigNum;
|
||||||
var spb = 60.0 / (parseInt(bpm) || 120);
|
var spb = 60.0 / (parseInt(bpm) || 120);
|
||||||
var newTime = -newBeatOff * spb;
|
var newTime = 0;
|
||||||
setSubTabs(function(prev) {
|
setSubTabs(function(prev) {
|
||||||
return prev.map(function(s) {
|
return prev.map(function(s) {
|
||||||
if (s.id !== st.id) return s;
|
if (s.id !== st.id) return s;
|
||||||
@@ -4986,7 +4986,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
|||||||
|
|
||||||
// Draw playhead
|
// Draw playhead
|
||||||
if (st.currentTime !== undefined && st.currentTime !== null) {
|
if (st.currentTime !== undefined && st.currentTime !== null) {
|
||||||
const phBeat = renderBeatOffset + st.currentTime / (60.0 / (parseInt(bpm) || 120));
|
const phBeat = st.currentTime / (60.0 / (parseInt(bpm) || 120));
|
||||||
const phX = phBeat * pixelsPerBeat;
|
const phX = phBeat * pixelsPerBeat;
|
||||||
if (phX >= 0 && phX <= viewWidth) {
|
if (phX >= 0 && phX <= viewWidth) {
|
||||||
ctx.strokeStyle = '#f59e0b';
|
ctx.strokeStyle = '#f59e0b';
|
||||||
@@ -6017,7 +6017,7 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
|
|||||||
const rect = e.currentTarget.getBoundingClientRect();
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
const x = e.clientX - rect.left + e.currentTarget.scrollLeft;
|
const x = e.clientX - rect.left + e.currentTarget.scrollLeft;
|
||||||
const clickBeat = x / pixelsPerBeat;
|
const clickBeat = x / pixelsPerBeat;
|
||||||
const clickTime = (clickBeat - renderBeatOffset) * beatSec;
|
const clickTime = clickBeat * beatSec;
|
||||||
const clickInRange = loopStartBeat !== null && loopEndBeat !== null && clickBeat >= loopStartBeat && clickBeat <= loopEndBeat;
|
const clickInRange = loopStartBeat !== null && loopEndBeat !== null && clickBeat >= loopStartBeat && clickBeat <= loopEndBeat;
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
setLoopStartBeat(null);
|
setLoopStartBeat(null);
|
||||||
@@ -8282,10 +8282,6 @@ const App = () => {
|
|||||||
const tabLabel = `Piano Roll: ${midiItem.name || 'MIDI'}`;
|
const tabLabel = `Piano Roll: ${midiItem.name || 'MIDI'}`;
|
||||||
const ctx = getAudioContext();
|
const ctx = getAudioContext();
|
||||||
const silentBuffer = ctx.createBuffer(1, 128, ctx.sampleRate);
|
const silentBuffer = ctx.createBuffer(1, 128, ctx.sampleRate);
|
||||||
var bpmVal = parseInt(bpm) || 120;
|
|
||||||
var beatSec = 60.0 / bpmVal;
|
|
||||||
var secPerBar = beatSec * 4;
|
|
||||||
var beatOff = (midiItem.startTime / secPerBar) * 4;
|
|
||||||
const newTab = {
|
const newTab = {
|
||||||
id: tabId,
|
id: tabId,
|
||||||
label: tabLabel,
|
label: tabLabel,
|
||||||
@@ -8298,7 +8294,7 @@ const App = () => {
|
|||||||
buffer: silentBuffer,
|
buffer: silentBuffer,
|
||||||
instrumentProgram: track.instrumentProgram,
|
instrumentProgram: track.instrumentProgram,
|
||||||
instrumentName: track.instrumentName,
|
instrumentName: track.instrumentName,
|
||||||
currentTime: -beatOff * beatSec,
|
currentTime: 0,
|
||||||
isPlaying: false,
|
isPlaying: false,
|
||||||
selectionStart: null,
|
selectionStart: null,
|
||||||
selectionEnd: null,
|
selectionEnd: null,
|
||||||
@@ -10403,8 +10399,15 @@ const App = () => {
|
|||||||
var allTracks = activeTracksRef.current || [];
|
var allTracks = activeTracksRef.current || [];
|
||||||
var mainCh = 0;
|
var mainCh = 0;
|
||||||
for (var i = 0; i < allTracks.length; i++) { if (allTracks[i].id === st.trackId) { mainCh = i >= 9 ? i + 1 : i; if (mainCh >= 16) mainCh = 0; break; } }
|
for (var i = 0; i < allTracks.length; i++) { if (allTracks[i].id === st.trackId) { mainCh = i >= 9 ? i + 1 : i; if (mainCh >= 16) mainCh = 0; break; } }
|
||||||
|
// Compute session beat offset from target item
|
||||||
|
var sessionBeatOffset = 0;
|
||||||
|
var targetTrk = allTracks.find(function(t) { return t.id === st.trackId; });
|
||||||
|
if (targetTrk) {
|
||||||
|
var targetIt = (targetTrk.midiItems || []).find(function(m) { return m.id === st.target_id; });
|
||||||
|
if (targetIt) sessionBeatOffset = (targetIt.startTime / ((60.0 / bpmVal) * 4)) * 4;
|
||||||
|
}
|
||||||
midiNotes.forEach(note => {
|
midiNotes.forEach(note => {
|
||||||
const noteOnBeat = note.start_beat || 0;
|
const noteOnBeat = (note.start_beat || 0) + sessionBeatOffset;
|
||||||
const noteDurBeat = note.duration_beats || 1;
|
const noteDurBeat = note.duration_beats || 1;
|
||||||
const noteStartSec = noteOnBeat * secondsPerBeat;
|
const noteStartSec = noteOnBeat * secondsPerBeat;
|
||||||
const noteDurSec = noteDurBeat * secondsPerBeat;
|
const noteDurSec = noteDurBeat * secondsPerBeat;
|
||||||
@@ -10424,12 +10427,12 @@ const App = () => {
|
|||||||
var ghostTrack = allTracks.find(function(t) { return t.id === layer.trackId; });
|
var ghostTrack = allTracks.find(function(t) { return t.id === layer.trackId; });
|
||||||
var ghostProg = layer.instrumentProgram !== undefined ? layer.instrumentProgram : (ghostTrack ? ghostTrack.instrumentProgram : undefined);
|
var ghostProg = layer.instrumentProgram !== undefined ? layer.instrumentProgram : (ghostTrack ? ghostTrack.instrumentProgram : undefined);
|
||||||
var ghostSynth = layer.synthEngine || (ghostTrack ? ghostTrack.synth_engine : undefined);
|
var ghostSynth = layer.synthEngine || (ghostTrack ? ghostTrack.synth_engine : undefined);
|
||||||
if (ghostProg === undefined && !ghostSynth) return; // skip tracks with no instrument
|
if (ghostProg === undefined && !ghostSynth) return;
|
||||||
var ghostDest = getOrCreateTrackNode(ghostTrack, context);
|
var ghostDest = getOrCreateTrackNode(ghostTrack, context);
|
||||||
var ghostCh = 0;
|
var ghostCh = 0;
|
||||||
for (var i = 0; i < allTracks.length; i++) { if (allTracks[i].id === layer.trackId) { ghostCh = i >= 9 ? i + 1 : i; if (ghostCh >= 16) ghostCh = 0; break; } }
|
for (var i = 0; i < allTracks.length; i++) { if (allTracks[i].id === layer.trackId) { ghostCh = i >= 9 ? i + 1 : i; if (ghostCh >= 16) ghostCh = 0; break; } }
|
||||||
layer.notes.forEach(function(note) {
|
layer.notes.forEach(function(note) {
|
||||||
var beat = note.start_beat || 0;
|
var beat = (note.start_beat || 0) + sessionBeatOffset;
|
||||||
var dur = note.duration_beats || 1;
|
var dur = note.duration_beats || 1;
|
||||||
var startSec = beat * secondsPerBeat;
|
var startSec = beat * secondsPerBeat;
|
||||||
var durSec = dur * secondsPerBeat;
|
var durSec = dur * secondsPerBeat;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user