diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-15 15:14:47 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-15 15:14:47 +0100 |
| commit | f7edf29a4c8208c7654a1c979dba980d741ad52d (patch) | |
| tree | fb5d24f41843a282bacf283a6de90465fbac338f /tools/timeline_editor | |
| parent | f23bd208b38ccfdc814daa99818a7e119f5ee313 (diff) | |
refactor(timeline-editor): rename variables to reflect beat-based units
Renamed time-related variables for clarity:
- pixelsPerSecond → pixelsPerBeat (timeline internally uses beats)
- audioDuration → audioDurationSeconds
- maxTime → maxTimeBeats
- Local variables: newTime → newTimeBeats, duration → durationBeats
- Updated stats display to show both beats and seconds
All internal state stores beat values; serializer writes beats without suffix per .seq format.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/timeline_editor')
| -rw-r--r-- | tools/timeline_editor/index.html | 76 |
1 files changed, 38 insertions, 38 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html index 9b03fa2..14a7791 100644 --- a/tools/timeline_editor/index.html +++ b/tools/timeline_editor/index.html @@ -208,10 +208,10 @@ // State const DEFAULT_BPM = 120; const state = { - sequences: [], currentFile: null, selectedItem: null, pixelsPerSecond: 100, + sequences: [], currentFile: null, selectedItem: null, pixelsPerBeat: 100, showBeats: true, quantizeUnit: 1, bpm: DEFAULT_BPM, isDragging: false, dragOffset: { x: 0, y: 0 }, lastActiveSeqIndex: -1, isDraggingHandle: false, handleType: null, handleDragOffset: 0, - audioBuffer: null, audioDuration: 0, audioSource: null, audioContext: null, + audioBuffer: null, audioDurationSeconds: 0, audioSource: null, audioContext: null, isPlaying: false, playbackStartTime: 0, playbackOffset: 0, playStartPosition: 0, animationFrameId: null, lastExpandedSeqIndex: -1, dragMoved: false, ...computeBPMValues(DEFAULT_BPM) @@ -361,12 +361,12 @@ let viewportController, playbackController; function computeCPULoad() { - if (state.sequences.length === 0) return { maxTime: 60, loads: [], conflicts: [] }; - let maxTime = Math.max(60, ...state.sequences.flatMap(seq => + if (state.sequences.length === 0) return { maxTimeBeats: 60, loads: [], conflicts: [] }; + let maxTimeBeats = Math.max(60, ...state.sequences.flatMap(seq => seq.effects.map(eff => seq.startTime + eff.endTime))); - if (state.audioDuration > 0) maxTime = Math.max(maxTime, timeToBeats(state.audioDuration)); + if (state.audioDurationSeconds > 0) maxTimeBeats = Math.max(maxTimeBeats, timeToBeats(state.audioDurationSeconds)); - const resolution = 0.1, numSamples = Math.ceil(maxTime / resolution); + const resolution = 0.1, numSamples = Math.ceil(maxTimeBeats / resolution); const loads = new Array(numSamples).fill(0); const conflicts = new Array(numSamples).fill(false); @@ -417,19 +417,19 @@ }); }); - return { maxTime, loads, conflicts, resolution }; + return { maxTimeBeats, loads, conflicts, resolution }; } function renderCPULoad() { const canvas = dom.cpuLoadCanvas, ctx = canvas.getContext('2d'); - const { maxTime, loads, conflicts, resolution } = computeCPULoad(); - const w = maxTime * state.pixelsPerSecond, h = 10; + const { maxTimeBeats, loads, conflicts, resolution } = computeCPULoad(); + const w = maxTimeBeats * state.pixelsPerBeat, h = 10; canvas.width = w; canvas.height = h; canvas.style.width = `${w}px`; canvas.style.height = `${h}px`; ctx.fillStyle = 'rgba(0, 0, 0, 0.3)'; ctx.fillRect(0, 0, w, h); if (loads.length === 0) return; - const barWidth = resolution * state.pixelsPerSecond; + const barWidth = resolution * state.pixelsPerBeat; loads.forEach((load, i) => { if (load === 0) return; const n = Math.min(load / 8, 1.0); @@ -446,27 +446,27 @@ function renderTimeline() { renderCPULoad(); dom.timeline.innerHTML = ''; document.getElementById('timeMarkers').innerHTML = ''; - let maxTime = 60; + let maxTimeBeats = 60; for (const seq of state.sequences) { - maxTime = Math.max(maxTime, seq.startTime + SEQUENCE_DEFAULT_DURATION); - for (const effect of seq.effects) maxTime = Math.max(maxTime, seq.startTime + effect.endTime); + maxTimeBeats = Math.max(maxTimeBeats, seq.startTime + SEQUENCE_DEFAULT_DURATION); + for (const effect of seq.effects) maxTimeBeats = Math.max(maxTimeBeats, seq.startTime + effect.endTime); } - if (state.audioDuration > 0) maxTime = Math.max(maxTime, state.audioDuration * state.beatsPerSecond); - const timelineWidth = maxTime * state.pixelsPerSecond; + if (state.audioDurationSeconds > 0) maxTimeBeats = Math.max(maxTimeBeats, state.audioDurationSeconds * state.beatsPerSecond); + const timelineWidth = maxTimeBeats * state.pixelsPerBeat; dom.timeline.style.width = `${timelineWidth}px`; let totalTimelineHeight = 0; const timeMarkers = document.getElementById('timeMarkers'); if (state.showBeats) { - for (let beat = 0; beat <= maxTime; beat += 4) { + for (let beat = 0; beat <= maxTimeBeats; beat += 4) { const marker = document.createElement('div'); - marker.className = 'time-marker'; marker.style.left = `${beat * state.pixelsPerSecond}px`; + marker.className = 'time-marker'; marker.style.left = `${beat * state.pixelsPerBeat}px`; marker.textContent = `${beat}b`; timeMarkers.appendChild(marker); } } else { - const maxSeconds = maxTime * state.secondsPerBeat; + const maxSeconds = maxTimeBeats * state.secondsPerBeat; for (let t = 0; t <= maxSeconds; t += 1) { const beatPos = t * state.beatsPerSecond, marker = document.createElement('div'); - marker.className = 'time-marker'; marker.style.left = `${beatPos * state.pixelsPerSecond}px`; + marker.className = 'time-marker'; marker.style.left = `${beatPos * state.pixelsPerBeat}px`; marker.textContent = `${t}s`; timeMarkers.appendChild(marker); } } @@ -483,9 +483,9 @@ const numEffects = seq.effects.length; const fullHeight = Math.max(SEQUENCE_MIN_HEIGHT, SEQUENCE_TOP_PADDING + numEffects * EFFECT_SPACING + SEQUENCE_BOTTOM_PADDING); const seqHeight = seq._collapsed ? SEQUENCE_COLLAPSED_HEIGHT : fullHeight; - seqDiv.style.left = `${seqVisualStart * state.pixelsPerSecond}px`; + seqDiv.style.left = `${seqVisualStart * state.pixelsPerBeat}px`; seqDiv.style.top = `${cumulativeY}px`; - seqDiv.style.width = `${(seqVisualEnd - seqVisualStart) * state.pixelsPerSecond}px`; + seqDiv.style.width = `${(seqVisualEnd - seqVisualStart) * state.pixelsPerBeat}px`; seqDiv.style.height = `${seqHeight}px`; seqDiv.style.minHeight = `${seqHeight}px`; seqDiv.style.maxHeight = `${seqHeight}px`; seq._yPosition = cumulativeY; cumulativeY += seqHeight + SEQUENCE_GAP; totalTimelineHeight = cumulativeY; const seqHeaderDiv = document.createElement('div'); seqHeaderDiv.className = 'sequence-header'; @@ -513,9 +513,9 @@ if (conflicts.has(effectIndex)) effectDiv.classList.add('conflict'); Object.assign(effectDiv.dataset, { seqIndex, effectIndex }); Object.assign(effectDiv.style, { - left: `${(seq.startTime + effect.startTime) * state.pixelsPerSecond}px`, + left: `${(seq.startTime + effect.startTime) * state.pixelsPerBeat}px`, top: `${seq._yPosition + SEQUENCE_TOP_PADDING + effectIndex * EFFECT_SPACING}px`, - width: `${(effect.endTime - effect.startTime) * state.pixelsPerSecond}px`, + width: `${(effect.endTime - effect.startTime) * state.pixelsPerBeat}px`, height: `${EFFECT_HEIGHT}px` }); effectDiv.innerHTML = `<div class="effect-handle left"></div><small>${effect.className}</small><div class="effect-handle right"></div>`; @@ -559,13 +559,13 @@ if (!state.isDragging || !state.selectedItem) return; state.dragMoved = true; const containerRect = dom.timelineContent.getBoundingClientRect(); - let newTime = Math.max(0, (e.clientX - containerRect.left + dom.timelineContent.scrollLeft - state.dragOffset.x) / state.pixelsPerSecond); - if (state.quantizeUnit > 0) newTime = Math.round(newTime * state.quantizeUnit) / state.quantizeUnit; - if (state.selectedItem.type === 'sequence') state.sequences[state.selectedItem.index].startTime = newTime; + let newTimeBeats = Math.max(0, (e.clientX - containerRect.left + dom.timelineContent.scrollLeft - state.dragOffset.x) / state.pixelsPerBeat); + if (state.quantizeUnit > 0) newTimeBeats = Math.round(newTimeBeats * state.quantizeUnit) / state.quantizeUnit; + if (state.selectedItem.type === 'sequence') state.sequences[state.selectedItem.index].startTime = newTimeBeats; else if (state.selectedItem.type === 'effect') { const seq = state.sequences[state.selectedItem.seqIndex], effect = seq.effects[state.selectedItem.effectIndex]; - const duration = effect.endTime - effect.startTime, relativeTime = newTime - seq.startTime; - effect.startTime = relativeTime; effect.endTime = effect.startTime + duration; + const durationBeats = effect.endTime - effect.startTime, relativeTimeBeats = newTimeBeats - seq.startTime; + effect.startTime = relativeTimeBeats; effect.endTime = effect.startTime + durationBeats; } renderTimeline(); updateProperties(); } @@ -583,7 +583,7 @@ state.selectedItem = { type: 'effect', seqIndex, effectIndex, index: seqIndex }; const seq = state.sequences[seqIndex], effect = seq.effects[effectIndex]; const containerRect = dom.timelineContent.getBoundingClientRect(); - const mouseTimeBeats = (e.clientX - containerRect.left + dom.timelineContent.scrollLeft) / state.pixelsPerSecond; + const mouseTimeBeats = (e.clientX - containerRect.left + dom.timelineContent.scrollLeft) / state.pixelsPerBeat; const handleTimeBeats = seq.startTime + (type === 'left' ? effect.startTime : effect.endTime); state.handleDragOffset = handleTimeBeats - mouseTimeBeats; document.addEventListener('mousemove', onHandleDrag); document.addEventListener('mouseup', stopHandleDrag); @@ -592,13 +592,13 @@ function onHandleDrag(e) { if (!state.isDraggingHandle || !state.selectedItem) return; const containerRect = dom.timelineContent.getBoundingClientRect(); - let newTime = (e.clientX - containerRect.left + dom.timelineContent.scrollLeft) / state.pixelsPerSecond + state.handleDragOffset; - newTime = Math.max(0, newTime); - if (state.quantizeUnit > 0) newTime = Math.round(newTime * state.quantizeUnit) / state.quantizeUnit; + let newTimeBeats = (e.clientX - containerRect.left + dom.timelineContent.scrollLeft) / state.pixelsPerBeat + state.handleDragOffset; + newTimeBeats = Math.max(0, newTimeBeats); + if (state.quantizeUnit > 0) newTimeBeats = Math.round(newTimeBeats * state.quantizeUnit) / state.quantizeUnit; const seq = state.sequences[state.selectedItem.seqIndex], effect = seq.effects[state.selectedItem.effectIndex]; - const relativeTime = newTime - seq.startTime; - if (state.handleType === 'left') effect.startTime = Math.min(relativeTime, effect.endTime - 0.1); - else if (state.handleType === 'right') effect.endTime = Math.max(effect.startTime + 0.1, relativeTime); + const relativeTimeBeats = newTimeBeats - seq.startTime; + if (state.handleType === 'left') effect.startTime = Math.min(relativeTimeBeats, effect.endTime - 0.1); + else if (state.handleType === 'right') effect.endTime = Math.max(effect.startTime + 0.1, relativeTimeBeats); renderTimeline(); updateProperties(); } @@ -702,9 +702,9 @@ function updateStats() { const effectCount = state.sequences.reduce((sum, seq) => sum + seq.effects.length, 0); - const maxTime = Math.max(0, ...state.sequences.flatMap(seq => + const maxTimeBeats = Math.max(0, ...state.sequences.flatMap(seq => seq.effects.map(e => seq.startTime + e.endTime).concat(seq.startTime))); - dom.stats.innerHTML = `📊 Sequences: ${state.sequences.length} | 🎬 Effects: ${effectCount} | ⏱️ Duration: ${maxTime.toFixed(2)}s`; + dom.stats.innerHTML = `📊 Sequences: ${state.sequences.length} | 🎬 Effects: ${effectCount} | ⏱️ Duration: ${maxTimeBeats.toFixed(2)}b (${beatsToTime(maxTimeBeats).toFixed(2)}s)`; } async function loadFromURLParams() { @@ -832,7 +832,7 @@ if (!playbackController || !state.audioBuffer) return; const containerRect = dom.timelineContent.getBoundingClientRect(); const clickX = e.clientX - containerRect.left + dom.timelineContent.scrollLeft - viewportController.TIMELINE_LEFT_PADDING; - const clickBeats = clickX / state.pixelsPerSecond; + const clickBeats = clickX / state.pixelsPerBeat; const clickTime = beatsToTime(clickBeats); const result = playbackController.seekTo(clickBeats, clickTime); if (result) showMessage(`Seek to ${result.clickTime.toFixed(2)}s (${result.clickBeats.toFixed(2)}b)`, 'success'); |
