diff options
Diffstat (limited to 'tools/timeline_editor')
| -rw-r--r-- | tools/timeline_editor/index.html | 30 |
1 files changed, 21 insertions, 9 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html index 4d6c81e..363c5cb 100644 --- a/tools/timeline_editor/index.html +++ b/tools/timeline_editor/index.html @@ -188,12 +188,14 @@ const VERTICAL_SCROLL_SPEED = 0.3; const SEQUENCE_GAP = 10; const SEQUENCE_DEFAULT_WIDTH = 10; + const SEQUENCE_DEFAULT_DURATION = 16; const SEQUENCE_MIN_HEIGHT = 70; const SEQUENCE_COLLAPSED_HEIGHT = 35; const SEQUENCE_TOP_PADDING = 20; const SEQUENCE_BOTTOM_PADDING = 5; const EFFECT_SPACING = 30; const EFFECT_HEIGHT = 26; + const WAVEFORM_AMPLITUDE_SCALE = 0.4; // State const state = { @@ -354,17 +356,28 @@ function renderWaveform() { if (!state.audioBuffer) return; const canvas = dom.waveformCanvas, ctx = canvas.getContext('2d'); - const w = timeToBeats(state.audioDuration) * state.pixelsPerSecond, h = 80; + + // Calculate maxTime same as timeline to ensure alignment + let maxTime = 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); + } + if (state.audioDuration > 0) maxTime = Math.max(maxTime, state.audioDuration * state.bpm / 60.0); + + const w = maxTime * state.pixelsPerSecond, h = 80; 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); const channelData = state.audioBuffer.getChannelData(0); - const samplesPerPixel = Math.ceil(channelData.length / w); - const centerY = h / 2, amplitudeScale = h * 0.4; + const audioBeats = timeToBeats(state.audioDuration); + const audioPixelWidth = audioBeats * state.pixelsPerSecond; + const samplesPerPixel = Math.ceil(channelData.length / audioPixelWidth); + const centerY = h / 2, amplitudeScale = h * WAVEFORM_AMPLITUDE_SCALE; ctx.strokeStyle = '#4ec9b0'; ctx.lineWidth = 1; ctx.beginPath(); - for (let x = 0; x < w; x++) { + for (let x = 0; x < audioPixelWidth; x++) { const start = Math.floor(x * samplesPerPixel); const end = Math.min(start + samplesPerPixel, channelData.length); let min = 1.0, max = -1.0; @@ -378,13 +391,12 @@ } ctx.stroke(); ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; - ctx.beginPath(); ctx.moveTo(0, centerY); ctx.lineTo(w, centerY); ctx.stroke(); + ctx.beginPath(); ctx.moveTo(0, centerY); ctx.lineTo(audioPixelWidth, centerY); ctx.stroke(); - // Draw beat markers - const maxBeats = timeToBeats(state.audioDuration); + // Draw beat markers across full maxTime width ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)'; ctx.lineWidth = 1; - for (let beat = 0; beat <= maxBeats; beat++) { + for (let beat = 0; beat <= maxTime; beat++) { const x = beat * state.pixelsPerSecond; ctx.beginPath(); ctx.moveTo(x, 0); @@ -564,7 +576,7 @@ dom.timeline.innerHTML = ''; document.getElementById('timeMarkers').innerHTML = ''; let maxTime = 60; for (const seq of state.sequences) { - maxTime = Math.max(maxTime, seq.startTime + 16); + maxTime = Math.max(maxTime, seq.startTime + SEQUENCE_DEFAULT_DURATION); for (const effect of seq.effects) maxTime = Math.max(maxTime, seq.startTime + effect.endTime); } if (state.audioDuration > 0) maxTime = Math.max(maxTime, state.audioDuration * state.bpm / 60.0); |
