From 93a65b43094641b4c188b4fc260b8ed44c883728 Mon Sep 17 00:00:00 2001 From: skal Date: Thu, 5 Feb 2026 02:01:57 +0100 Subject: move MD files --- ANALYSIS_VARIABLE_TEMPO_V2.md | 414 ------------------------------------------ 1 file changed, 414 deletions(-) delete mode 100644 ANALYSIS_VARIABLE_TEMPO_V2.md (limited to 'ANALYSIS_VARIABLE_TEMPO_V2.md') diff --git a/ANALYSIS_VARIABLE_TEMPO_V2.md b/ANALYSIS_VARIABLE_TEMPO_V2.md deleted file mode 100644 index add347c..0000000 --- a/ANALYSIS_VARIABLE_TEMPO_V2.md +++ /dev/null @@ -1,414 +0,0 @@ -# Variable Tempo: Simplified Approach (V2) - -## User's Proposal: Trigger Timing Only - -**Key Insight:** Don't change spectrograms or playback speed - just change **when** they trigger! - -### The Simple Solution - -```cpp -// In main loop -static float music_time = 0.0f; -static float tempo_scale = 1.0f; // Can be animated/changed - -void update_game_logic(float dt) { - // Music time advances at variable rate - music_time += dt * tempo_scale; - - // Patterns trigger based on music_time (not physical time) - tracker_update(music_time); -} -``` - -**That's it!** 🎉 - -### What This Achieves - -**Drums, melodies, samples:** All sound identical (no pitch shift) -**Tempo changes:** Patterns trigger faster/slower based on `tempo_scale` -**No synth changes:** Playback rate stays at 32kHz (unchanged) - -### Example Timeline - -**Pattern triggers at music_time = 4.0** - -| tempo_scale | Physical Time | Effect | -|-------------|---------------|--------| -| 1.0 | 4.0s | Normal tempo | -| 2.0 | 2.0s | Triggers 2x earlier (accelerated) | -| 0.5 | 8.0s | Triggers 2x later (slowed) | - -**Visual:** -``` -Physical Time: 0s----1s----2s----3s----4s----5s----6s----7s----8s - ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ - -tempo_scale=1.0: 🥁 (triggers at 4s) - -tempo_scale=2.0: 🥁 (triggers at 2s - accelerated!) - -tempo_scale=0.5: 🥁 (triggers at 8s - slowed) -``` - -### The "Reset" Trick - -**Problem:** Music has accelerated to 2.0x, feels too fast -**Solution:** Reset tempo and switch to denser pattern - -```cpp -// Music accelerating -tempo_scale = 1.0 + t * 0.1; // Gradually speeds up - -// At some point (e.g., tempo_scale = 2.0) -if (tempo_scale >= 2.0) { - tempo_scale = 1.0; // Reset to normal - trigger_pattern_dense(); // Switch to 2x denser pattern -} -``` - -**Pattern Authoring:** -``` -Pattern A (sparse): [kick]---[snare]---[kick]---[snare]--- - beat 0 beat 1 beat 2 beat 3 - -Pattern B (dense): [kick]-[snare]-[kick]-[snare]- - beat 0 beat 0.5 beat 1 beat 1.5 -``` - -**Result:** Music feels the same tempo, but you've "reset" the timeline! - ---- - -## What Needs to Change in Current Code - -### Change 1: Main Loop (main.cc) - -**Current:** -```cpp -void update_game_logic(double t) { - tracker_update((float)t); // ❌ Uses physical time directly -} -``` - -**New:** -```cpp -static float g_music_time = 0.0f; -static float g_tempo_scale = 1.0f; - -void update_game_logic(double t) { - float dt = get_delta_time(); // Physical time delta - - // Music time advances at variable rate - g_music_time += dt * g_tempo_scale; - - tracker_update(g_music_time); // ✅ Uses music time -} -``` - -### Change 2: Tempo Control API (NEW) - -```cpp -// In tracker.h or main.cc -void set_tempo_scale(float scale); -float get_tempo_scale(); -float get_music_time(); -``` - -### Change 3: Tracker (NO CHANGES NEEDED!) - -**tracker.h:** Keep as-is -**tracker.cc:** Keep as-is -**TrackerScore.bpm:** Keep it! (used for compositing patterns) - -The tracker already works with abstract "time" - we just feed it `music_time` instead of physical time. - -### Change 4: Synth (NO CHANGES NEEDED!) - -Spectrograms play back at fixed rate (32kHz). -No pitch shifting, no interpolation needed. - ---- - -## Detailed Example: Accelerating Drum Beat - -### Setup -```cpp -// Pattern: kick on beats 0 and 2, snare on beats 1 and 3 -TrackerEvent drum_events[] = { - {0.0f, KICK_ID, 1.0f, 0.0f}, // Beat 0 - {1.0f, SNARE_ID, 0.9f, 0.0f}, // Beat 1 - {2.0f, KICK_ID, 1.0f, 0.0f}, // Beat 2 - {3.0f, SNARE_ID, 0.9f, 0.0f}, // Beat 3 -}; - -TrackerPattern drum_pattern = {drum_events, 4, 4.0f}; -``` - -### Scenario: Gradual Acceleration - -```cpp -// Main loop -float tempo_scale = 1.0f; - -void update_game_logic(float dt) { - // Gradually accelerate (0.05x faster per second) - tempo_scale += dt * 0.05f; - - music_time += dt * tempo_scale; - tracker_update(music_time); -} -``` - -**Timeline:** -``` -Physical Time: 0s 1s 2s 3s 4s 5s 6s 7s 8s -tempo_scale: 1.0 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 - -Music Time: 0.0 1.05 2.15 3.30 4.50 5.75 7.05 8.40 9.80 - ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ -Pattern Trigs: 🥁 🥁 🥁 🥁 🥁 🥁 🥁 🥁 🥁 -(every 4 beats) 0 4 8 12 16 20 24 28 32 - -Physical times when patterns trigger: -- Pattern 0: t=0.0s -- Pattern 1: t≈3.8s (instead of 4.0s) -- Pattern 2: t≈7.2s (instead of 8.0s) -- Pattern 3: t≈10.2s (instead of 12.0s) -``` - -**Effect:** Drum beat gradually feels faster, even though drums sound the same! - ---- - -## The "Reset" Strategy - -### Problem -After accelerating to 2.0x, music feels too rushed. Want to maintain energy but reset tempo. - -### Solution: Tempo Reset + Pattern Swap - -**Before Reset:** -- tempo_scale = 2.0 -- Pattern A (sparse): kicks every 1 beat - -**After Reset:** -- tempo_scale = 1.0 -- Pattern B (dense): kicks every 0.5 beats - -**Result:** Same physical rate of kicks, but tempo has "reset"! - -### Code Example - -```cpp -void update_game_logic(float dt) { - music_time += dt * tempo_scale; - - // Gradually accelerate - tempo_scale += dt * 0.1f; - - // Check for reset point - if (tempo_scale >= 2.0f) { - // Reset tempo - tempo_scale = 1.0f; - - // Switch to denser pattern - current_pattern_density *= 2; // 2x more events per beat - - // Optionally: retrigger current section with new density - retrigger_section_with_density(current_pattern_density); - } - - tracker_update(music_time); -} -``` - -### Pattern Authoring Strategy - -**Create patterns at multiple densities:** - -```cpp -// Sparse pattern (quarter notes) -TrackerEvent pattern_1x[] = { - {0.0f, KICK, 1.0f, 0.0f}, - {1.0f, SNARE, 0.9f, 0.0f}, - {2.0f, KICK, 1.0f, 0.0f}, - {3.0f, SNARE, 0.9f, 0.0f}, -}; - -// Dense pattern (eighth notes) -TrackerEvent pattern_2x[] = { - {0.0f, KICK, 1.0f, 0.0f}, - {0.5f, HIHAT, 0.6f, 0.0f}, - {1.0f, SNARE, 0.9f, 0.0f}, - {1.5f, HIHAT, 0.6f, 0.0f}, - {2.0f, KICK, 1.0f, 0.0f}, - {2.5f, HIHAT, 0.6f, 0.0f}, - {3.0f, SNARE, 0.9f, 0.0f}, - {3.5f, HIHAT, 0.6f, 0.0f}, -}; - -// Very dense pattern (sixteenth notes) -TrackerEvent pattern_4x[] = { - // ... even more events -}; -``` - -**Use pattern density to match tempo:** -- tempo_scale = 1.0x → use pattern_1x -- tempo_scale = 2.0x → reset to 1.0x, use pattern_2x -- tempo_scale = 4.0x → reset to 1.0x, use pattern_4x - ---- - -## Comparison: Old Proposal vs. New Proposal - -| Aspect | Old Proposal (Complex) | New Proposal (Simple) | -|--------|------------------------|----------------------| -| **Synth Changes** | Variable playback speed, interpolation | ❌ None | -| **Pitch Shifting** | Yes (side effect) | ❌ No | -| **Spectrograms** | Modified at playback | ✅ Unchanged | -| **Complexity** | High (12 hours) | Low (1 hour) | -| **Code Changes** | ~500 lines | ~20 lines | -| **Size Impact** | +2-3 KB | +50 bytes | -| **Quality** | Good with interpolation | ✅ Perfect (no artifacts) | - ---- - -## Implementation Checklist - -### Step 1: Add Music Time State (5 minutes) -```cpp -// In main.cc (global scope) -static float g_music_time = 0.0f; -static float g_tempo_scale = 1.0f; -static float g_last_physical_time = 0.0f; -``` - -### Step 2: Update Main Loop (10 minutes) -```cpp -void update_game_logic(double physical_time) { - // Calculate delta - float dt = (float)(physical_time - g_last_physical_time); - g_last_physical_time = physical_time; - - // Advance music time at scaled rate - g_music_time += dt * g_tempo_scale; - - // Update tracker with music time (not physical time) - tracker_update(g_music_time); -} - -// In main loop -while (!should_close) { - double current_time = platform_state.time + seek_time; - update_game_logic(current_time); // Pass physical time - // ... -} -``` - -### Step 3: Add Tempo Control API (10 minutes) -```cpp -// In main.cc or new file -void set_tempo_scale(float scale) { - g_tempo_scale = scale; -} - -float get_tempo_scale() { - return g_tempo_scale; -} - -float get_music_time() { - return g_music_time; -} -``` - -### Step 4: Test with Simple Acceleration (10 minutes) -```cpp -// Temporary test: gradual acceleration -void update_game_logic(double physical_time) { - float dt = get_delta_time(); - - // TEST: Accelerate from 1.0x to 2.0x over 10 seconds - g_tempo_scale = 1.0f + (physical_time / 10.0f); - g_tempo_scale = fminf(g_tempo_scale, 2.0f); - - g_music_time += dt * g_tempo_scale; - tracker_update(g_music_time); -} -``` - -### Step 5: Implement Reset Logic (15 minutes) -```cpp -// When tempo hits threshold, reset and switch patterns -if (g_tempo_scale >= 2.0f) { - g_tempo_scale = 1.0f; - // Trigger denser pattern here -} -``` - -### Step 6: Create Pattern Variants (tracker compiler work) -- Author patterns at 1x, 2x, 4x densities -- Map tempo ranges to pattern variants - ---- - -## Advantages of This Approach - -✅ **Simple:** ~20 lines of code -✅ **No pitch shift:** Samples sound identical -✅ **No synth changes:** Risk-free -✅ **Flexible:** Easy to animate tempo curves -✅ **Tiny size impact:** ~50 bytes -✅ **Perfect quality:** No interpolation artifacts - ---- - -## Example: Tempo Curves - -### Linear Acceleration -```cpp -tempo_scale = 1.0f + t * 0.1f; // 0.1x faster per second -``` - -### Exponential Acceleration -```cpp -tempo_scale = powf(2.0f, t / 10.0f); // Doubles every 10 seconds -``` - -### Oscillating Tempo -```cpp -tempo_scale = 1.0f + 0.3f * sinf(t * 0.5f); // Wave between 0.7x and 1.3x -``` - -### Manual Control (BPM curve from score) -```cpp -// Define tempo curve in tracker score -float tempo_curve[] = {1.0f, 1.1f, 1.3f, 1.6f, 2.0f, 1.0f, ...}; -tempo_scale = interpolate_tempo_curve(music_time); -``` - ---- - -## Conclusion - -**Your proposal is brilliant!** 🎯 - -### What You Get -- Variable tempo without modifying spectrograms -- No pitch shifting (drums sound like drums) -- Simple implementation (~1 hour) -- Tiny code size (~50 bytes) -- Perfect audio quality - -### What You Need to Do -1. Add `g_music_time` and `g_tempo_scale` to main loop -2. Compute `music_time += dt * tempo_scale` -3. Pass `music_time` to `tracker_update()` -4. Animate `tempo_scale` however you want! - -### For the "Reset" Trick -- Author patterns at 1x, 2x, 4x note densities -- When tempo hits 2.0x, reset to 1.0x and switch to denser pattern -- Result: Same perceived tempo, but timeline has reset - -**Ready to implement?** This is a 1-hour task! -- cgit v1.2.3