# Beat-Based Timing System ## Overview The demo uses **beat-based timing** for visual effect sequences, ensuring musical synchronization regardless of BPM changes. All timeline sequences are authored in beats (musical time) and converted to physical seconds at runtime. **Key Principle:** Variable tempo only affects audio sample triggering. Visual effects run at constant physical time with optional beat-synchronized animation. --- ## Quick Start ### Timeline Authoring ```seq # BPM 120 SEQUENCE 0 0 "Intro" # Beat 0 (bar 1) EFFECT + Flash 0 2 # Beats 0-2 (half bar) EFFECT + Fade 4 8 # Beats 4-8 (full bar) SEQUENCE 16 1 "Drop" # Beat 16 (bar 5) EFFECT + Heptagon 0 16 # 4 bars ``` **Conversion:** At 120 BPM, 1 beat = 0.5 seconds, 4 beats = 2 seconds ### Shader Animation ```wgsl @group(0) @binding(2) var uniforms: CommonUniforms; // Use beat_time for musical animation let bar_cycle = uniforms.beat_time / 4.0; // Bars let pulse = sin(bar_cycle * TAU); // Use beat_phase for smooth per-beat effects let wave = sin(uniforms.beat_phase * TAU); // Use time for constant-speed physics let rotation = uniforms.time * TAU; ``` --- ## Uniform Structure All effects receive `CommonPostProcessUniforms` with timing data: ```cpp struct CommonPostProcessUniforms { vec2 resolution; // Screen dimensions (pixels) float aspect_ratio; // Width/height ratio float time; // Physical seconds (constant, unaffected by tempo) float beat_time; // Absolute beats (musical time from audio clock) float beat_phase; // Fractional beat 0.0-1.0 (smooth oscillation) float audio_intensity; // Audio peak for beat sync float _pad; // Alignment padding }; // 32 bytes ``` **Use Cases:** - `time`: Physics animation, constant-speed rotation/movement - `beat_time`: Bar-based patterns, musical synchronization - `beat_phase`: Smooth per-beat pulse/wave effects --- ## Timeline Format ### Time Notation **Default:** Beats (no suffix) ```seq SEQUENCE 0 0 # Beat 0 EFFECT + Flash 0 4 # Beats 0-4 ``` **Explicit Seconds:** Use `s` suffix (rare) ```seq SEQUENCE 2.5s 0 # 2.5 physical seconds EFFECT + Flash 0 4 # Still uses beats for duration ``` **Explicit Beats:** Use `b` suffix (optional clarity) ```seq SEQUENCE 8b 0 # Same as "8" EFFECT + Flash 0b 4b # Same as "0 4" ``` ### BPM Declaration **Required** in all timeline files: ```seq # BPM 120 ``` Specifies beats per minute for runtime conversion to seconds. --- ## Architecture ### Timing Flow ``` Platform Clock (physical seconds) │ ├──► Physical Time ────────┐ │ (constant) │ │ │ └──► Audio Time ────┐ │ (playback) │ │ ▼ │ Beat Calculation │ (BPM * 60) │ │ │ ▼ ▼ Visual Effects Rendering (time + beat_time + beat_phase) ``` ### Key Insight **Variable tempo changes `music_time`** (used for audio event triggering), but **visual effects receive `time` (physical)** and **`beat_time` (from audio playback clock)**, not from tempo-scaled music time. This separation ensures: - ✅ Visual effects run at constant frame rate - ✅ Beat-synced animations track actual audio playback - ✅ Tempo changes don't cause visual stuttering --- ## Implementation ### Beat Calculation (Runtime) ```cpp // main.cc - Calculate from audio playback time const float absolute_beat_time = current_audio_time * g_tracker_score.bpm / 60.0f; const float beat_phase = fmodf(absolute_beat_time, 1.0f); // Pass to GPU rendering gpu_draw(visual_peak, aspect_ratio, physical_time, absolute_beat_time, beat_phase); ``` ### Timeline Compilation ```cpp // seq_compiler.cc - Convert beats to seconds at compile time std::string convert_to_time(const std::string& value, float bpm) { if (value.back() == 's') return explicit_seconds; // Pass through // Default: treat as beats float beat = std::stof(value); float time = beat * 60.0f / bpm; return time; } ``` **Result:** Generated `timeline.cc` contains physical seconds for effect activation. --- ## Migration ### Existing Timelines Already migrated with explicit `s` suffix to preserve timing: ```seq SEQUENCE 2.50s 0 # Physical seconds preserved EFFECT + Flash 0.00s 1.00s ``` ### New Content Use beat notation (recommended): ```seq # BPM 140 SEQUENCE 0 0 "Intro" EFFECT + Flash 0 4 # 4 beats = 1.71s @ 140 BPM EFFECT + Fade 4 8 # 4 beats = 1.71s ``` **Benefits:** - Natural musical alignment (bars/beats) - BPM changes don't break timing - Easier to author to music --- ## Examples ### Four-Bar Pattern ```seq # BPM 120 SEQUENCE 0 0 "Verse 1" EFFECT - Background 0 16 # 4 bars background EFFECT + Flash 0 1 # First beat flash EFFECT + Pulse 4 5 # Second bar pulse EFFECT + Fade 15 16 # Final beat fade ``` ### Multi-Bar Sequence ```seq SEQUENCE 16 0 "Chorus" # Bar 5 EFFECT + Heptagon 0 32 # 8 bars (full chorus) EFFECT + Particles 8 24 # Bars 7-11 (middle) ``` ### Beat-Synced Shader ```wgsl fn fragment_main(@location(0) uv: vec2) -> @location(0) vec4 { // Pulse every bar (4 beats) let bar_phase = fract(uniforms.beat_time / 4.0); let bar_pulse = smoothstep(0.0, 0.1, bar_phase) * (1.0 - smoothstep(0.9, 1.0, bar_phase)); // Smooth per-beat wave let beat_wave = sin(uniforms.beat_phase * TAU); // Combine let intensity = bar_pulse * 0.5 + beat_wave * 0.3; return vec4(color * intensity, 1.0); } ``` --- ## Troubleshooting ### Shader Compilation Error: "invalid accessor 'beat'" **Cause:** Old shader using `uniforms.beat` (deprecated field) **Fix:** Use `uniforms.beat_phase` or `uniforms.beat_time` ```wgsl // OLD (error): let x = uniforms.beat; // NEW: let x = uniforms.beat_phase; // For 0-1 fractional let y = uniforms.beat_time; // For absolute beats ``` ### Timeline Parse Error **Cause:** Missing BPM declaration **Fix:** Add BPM at top of file ```seq # BPM 120 ← Required SEQUENCE 0 0 ``` ### Effects Start at Wrong Time **Cause:** Mixing beats and seconds without explicit suffixes **Fix:** Be explicit ```seq SEQUENCE 8 0 # 8 beats (not 8 seconds) SEQUENCE 8s 0 # 8 seconds (explicit) SEQUENCE 8b 0 # 8 beats (explicit, same as first) ``` --- ## See Also - **Format Reference:** `doc/SEQUENCE.md` - Complete .seq syntax - **Implementation:** `BEAT_TIMING_SUMMARY.md` - Technical details - **Effect Creation:** `doc/EFFECT_WORKFLOW.md` - Adding new effects - **Timeline Editor:** `tools/timeline_editor/README.md` - Visual editing