summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tests/test_tracker_timing.cc35
-rw-r--r--src/tests/test_variable_tempo.cc264
2 files changed, 94 insertions, 205 deletions
diff --git a/src/tests/test_tracker_timing.cc b/src/tests/test_tracker_timing.cc
index a279c8e..9f15197 100644
--- a/src/tests/test_tracker_timing.cc
+++ b/src/tests/test_tracker_timing.cc
@@ -13,6 +13,12 @@
#if !defined(STRIP_ALL)
+// Helper: Setup audio engine for testing
+static void setup_audio_test(MockAudioBackend& backend, AudioEngine& engine) {
+ audio_set_backend(&backend);
+ engine.init();
+}
+
// Helper: Check if a timestamp exists in events within tolerance
static bool has_event_at_time(const std::vector<VoiceTriggerEvent>& events,
float expected_time, float tolerance = 0.001f) {
@@ -60,23 +66,16 @@ void test_basic_event_recording() {
printf("Test: Basic event recording with mock backend...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
+ setup_audio_test(backend, engine);
- // Trigger at t=0.0 (should trigger initial patterns)
engine.update(0.0f, 0.0f);
-
const auto& events = backend.get_events();
printf(" Events triggered at t=0.0: %zu\n", events.size());
- // Verify we got some events
assert(events.size() > 0);
-
- // All events at t=0 should have timestamp near 0
for (const auto& evt : events) {
- assert(evt.timestamp_sec < 0.1f); // Within 100ms of start
+ assert(evt.timestamp_sec < 0.1f);
}
engine.shutdown();
@@ -87,27 +86,21 @@ void test_progressive_triggering() {
printf("Test: Progressive pattern triggering...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
+ setup_audio_test(backend, engine);
- // Update at t=0
engine.update(0.0f, 0.0f);
const size_t events_at_0 = backend.get_events().size();
printf(" Events at t=0.0: %zu\n", events_at_0);
- // Update at t=1.0
engine.update(1.0f, 0.0f);
const size_t events_at_1 = backend.get_events().size();
printf(" Events at t=1.0: %zu\n", events_at_1);
- // Update at t=2.0
engine.update(2.0f, 0.0f);
const size_t events_at_2 = backend.get_events().size();
printf(" Events at t=2.0: %zu\n", events_at_2);
- // Events should accumulate (or at least not decrease)
assert(events_at_1 >= events_at_0);
assert(events_at_2 >= events_at_1);
@@ -119,12 +112,9 @@ void test_simultaneous_triggers() {
printf("Test: SIMULTANEOUS pattern triggers at same time...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
+ setup_audio_test(backend, engine);
- // Clear and update to first trigger point
backend.clear_events();
engine.update(0.0f, 0.0f);
@@ -167,12 +157,9 @@ void test_timing_monotonicity() {
printf("Test: Event timestamps are monotonically increasing...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
+ setup_audio_test(backend, engine);
- // Update through several time points
for (float t = 0.0f; t <= 5.0f; t += 0.5f) {
engine.update(t, 0.5f);
}
diff --git a/src/tests/test_variable_tempo.cc b/src/tests/test_variable_tempo.cc
index 4fc81e3..cd83a06 100644
--- a/src/tests/test_variable_tempo.cc
+++ b/src/tests/test_variable_tempo.cc
@@ -12,43 +12,49 @@
#if !defined(STRIP_ALL)
-// Helper: Calculate expected physical time for music_time at constant tempo
-static float calc_physical_time(float music_time, float tempo_scale) {
- return music_time / tempo_scale;
+// Helper: Setup audio engine for testing
+static void setup_audio_test(MockAudioBackend& backend, AudioEngine& engine) {
+ audio_set_backend(&backend);
+ engine.init();
+ engine.load_music_data(&g_tracker_score, g_tracker_samples,
+ g_tracker_sample_assets, g_tracker_samples_count);
}
-// Helper: Simulate music time advancement
-static float advance_music_time(float current_music_time, float dt,
- float tempo_scale) {
- return current_music_time + (dt * tempo_scale);
+// Helper: Simulate tempo advancement with fixed steps
+static void simulate_tempo(AudioEngine& engine, float& music_time,
+ float duration, float tempo_scale, float dt = 0.1f) {
+ const int steps = (int)(duration / dt);
+ for (int i = 0; i < steps; ++i) {
+ music_time += dt * tempo_scale;
+ engine.update(music_time, dt * tempo_scale);
+ }
+}
+
+// Helper: Simulate tempo with variable scaling function
+static void simulate_tempo_fn(AudioEngine& engine, float& music_time,
+ float& physical_time, float duration, float dt,
+ float (*tempo_fn)(float)) {
+ const int steps = (int)(duration / dt);
+ for (int i = 0; i < steps; ++i) {
+ physical_time += dt;
+ const float tempo_scale = tempo_fn(physical_time);
+ music_time += dt * tempo_scale;
+ engine.update(music_time, dt * tempo_scale);
+ }
}
void test_basic_tempo_scaling() {
printf("Test: Basic tempo scaling (1.0x, 2.0x, 0.5x)...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
// Test 1: Normal tempo (1.0x)
{
backend.clear_events();
float music_time = 0.0f;
- float tempo_scale = 1.0f;
-
- // Simulate 1 second of physical time
- for (int i = 0; i < 10; ++i) {
- float dt = 0.1f; // 100ms physical steps
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
-
- // After 1 second physical time at 1.0x tempo:
- // music_time should be ~1.0
+ simulate_tempo(engine, music_time, 1.0f, 1.0f);
printf(" 1.0x tempo: music_time = %.3f (expected ~1.0)\n", music_time);
assert(std::abs(music_time - 1.0f) < 0.01f);
}
@@ -56,19 +62,9 @@ void test_basic_tempo_scaling() {
// Test 2: Fast tempo (2.0x)
{
backend.clear_events();
- engine.reset(); // Reset engine
+ engine.reset();
float music_time = 0.0f;
- float tempo_scale = 2.0f;
-
- // Simulate 1 second of physical time
- for (int i = 0; i < 10; ++i) {
- float dt = 0.1f;
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
-
- // After 1 second physical time at 2.0x tempo:
- // music_time should be ~2.0
+ simulate_tempo(engine, music_time, 1.0f, 2.0f);
printf(" 2.0x tempo: music_time = %.3f (expected ~2.0)\n", music_time);
assert(std::abs(music_time - 2.0f) < 0.01f);
}
@@ -78,17 +74,7 @@ void test_basic_tempo_scaling() {
backend.clear_events();
engine.reset();
float music_time = 0.0f;
- float tempo_scale = 0.5f;
-
- // Simulate 1 second of physical time
- for (int i = 0; i < 10; ++i) {
- float dt = 0.1f;
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
-
- // After 1 second physical time at 0.5x tempo:
- // music_time should be ~0.5
+ simulate_tempo(engine, music_time, 1.0f, 0.5f);
printf(" 0.5x tempo: music_time = %.3f (expected ~0.5)\n", music_time);
assert(std::abs(music_time - 0.5f) < 0.01f);
}
@@ -101,54 +87,31 @@ void test_2x_speedup_reset_trick() {
printf("Test: 2x SPEED-UP reset trick...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
- // Scenario: Accelerate to 2.0x, then reset to 1.0x
float music_time = 0.0f;
- float tempo_scale = 1.0f;
float physical_time = 0.0f;
-
- const float dt = 0.1f; // 100ms steps
+ const float dt = 0.1f;
// Phase 1: Accelerate from 1.0x to 2.0x over 5 seconds
printf(" Phase 1: Accelerating 1.0x → 2.0x\n");
- for (int i = 0; i < 50; ++i) {
- physical_time += dt;
- tempo_scale = 1.0f + (physical_time / 5.0f); // Linear acceleration
- tempo_scale = fminf(tempo_scale, 2.0f);
-
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
+ auto accel_fn = [](float t) {
+ return fminf(1.0f + (t / 5.0f), 2.0f);
+ };
+ simulate_tempo_fn(engine, music_time, physical_time, 5.0f, dt, accel_fn);
+ const float tempo_scale = accel_fn(physical_time);
printf(" After 5s physical: tempo=%.2fx, music_time=%.3f\n", tempo_scale,
music_time);
- assert(tempo_scale >= 1.99f); // Should be at 2.0x
-
- // Record state before reset
- const float music_time_before_reset = music_time;
- const size_t events_before_reset = backend.get_events().size();
+ assert(tempo_scale >= 1.99f);
// Phase 2: RESET - back to 1.0x tempo
printf(" Phase 2: RESET to 1.0x tempo\n");
- tempo_scale = 1.0f;
-
- // Continue for another 2 seconds
- for (int i = 0; i < 20; ++i) {
- physical_time += dt;
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
-
- printf(" After reset + 2s: tempo=%.2fx, music_time=%.3f\n", tempo_scale,
- music_time);
+ const float music_time_before_reset = music_time;
+ simulate_tempo(engine, music_time, 2.0f, 1.0f, dt);
- // Verify: music_time advanced 2.0 units in 2 seconds at 1.0x tempo
+ printf(" After reset + 2s: tempo=1.0x, music_time=%.3f\n", music_time);
const float music_time_delta = music_time - music_time_before_reset;
printf(" Music time delta: %.3f (expected ~2.0)\n", music_time_delta);
assert(std::abs(music_time_delta - 2.0f) < 0.1f);
@@ -161,53 +124,31 @@ void test_2x_slowdown_reset_trick() {
printf("Test: 2x SLOW-DOWN reset trick...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
- // Scenario: Decelerate to 0.5x, then reset to 1.0x
float music_time = 0.0f;
- float tempo_scale = 1.0f;
float physical_time = 0.0f;
-
const float dt = 0.1f;
// Phase 1: Decelerate from 1.0x to 0.5x over 5 seconds
printf(" Phase 1: Decelerating 1.0x → 0.5x\n");
- for (int i = 0; i < 50; ++i) {
- physical_time += dt;
- tempo_scale = 1.0f - (physical_time / 10.0f); // Linear deceleration
- tempo_scale = fmaxf(tempo_scale, 0.5f);
-
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
+ auto decel_fn = [](float t) {
+ return fmaxf(1.0f - (t / 10.0f), 0.5f);
+ };
+ simulate_tempo_fn(engine, music_time, physical_time, 5.0f, dt, decel_fn);
+ const float tempo_scale = decel_fn(physical_time);
printf(" After 5s physical: tempo=%.2fx, music_time=%.3f\n", tempo_scale,
music_time);
- assert(tempo_scale <= 0.51f); // Should be at 0.5x
-
- // Record state before reset
- const float music_time_before_reset = music_time;
+ assert(tempo_scale <= 0.51f);
// Phase 2: RESET - back to 1.0x tempo
printf(" Phase 2: RESET to 1.0x tempo\n");
- tempo_scale = 1.0f;
-
- // Continue for another 2 seconds
- for (int i = 0; i < 20; ++i) {
- physical_time += dt;
- music_time += dt * tempo_scale;
- engine.update(music_time, dt * tempo_scale);
- }
-
- printf(" After reset + 2s: tempo=%.2fx, music_time=%.3f\n", tempo_scale,
- music_time);
+ const float music_time_before_reset = music_time;
+ simulate_tempo(engine, music_time, 2.0f, 1.0f, dt);
- // Verify: music_time advanced 2.0 units in 2 seconds at 1.0x tempo
+ printf(" After reset + 2s: tempo=1.0x, music_time=%.3f\n", music_time);
const float music_time_delta = music_time - music_time_before_reset;
printf(" Music time delta: %.3f (expected ~2.0)\n", music_time_delta);
assert(std::abs(music_time_delta - 2.0f) < 0.1f);
@@ -220,54 +161,31 @@ void test_pattern_density_swap() {
printf("Test: Pattern density swap at reset points...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
- // Simulate: sparse pattern → accelerate → reset + dense pattern
float music_time = 0.0f;
- float tempo_scale = 1.0f;
- // Phase 1: Sparse pattern at normal tempo (first 3 patterns trigger)
+ // Phase 1: Sparse pattern at normal tempo
printf(" Phase 1: Sparse pattern, normal tempo\n");
- for (float t = 0.0f; t < 3.0f; t += 0.1f) {
- music_time += 0.1f * tempo_scale;
- engine.update(music_time, 0.1f * tempo_scale);
- }
+ simulate_tempo(engine, music_time, 3.0f, 1.0f);
const size_t sparse_events = backend.get_events().size();
printf(" Events during sparse phase: %zu\n", sparse_events);
// Phase 2: Accelerate to 2.0x
printf(" Phase 2: Accelerating to 2.0x\n");
- tempo_scale = 2.0f;
- for (float t = 0.0f; t < 2.0f; t += 0.1f) {
- music_time += 0.1f * tempo_scale;
- engine.update(music_time, 0.1f * tempo_scale);
- }
+ simulate_tempo(engine, music_time, 2.0f, 2.0f);
const size_t events_at_2x = backend.get_events().size() - sparse_events;
printf(" Additional events during 2.0x: %zu\n", events_at_2x);
- // Phase 3: Reset to 1.0x (in real impl, would switch to denser pattern)
+ // Phase 3: Reset to 1.0x
printf(" Phase 3: Reset to 1.0x (simulating denser pattern)\n");
- tempo_scale = 1.0f;
-
- // At this point, real implementation would trigger a pattern with
- // 2x more events per beat to maintain perceived density
-
const size_t events_before_reset_phase = backend.get_events().size();
- for (float t = 0.0f; t < 2.0f; t += 0.1f) {
- music_time += 0.1f * tempo_scale;
- engine.update(music_time, 0.1f * tempo_scale);
- }
+ simulate_tempo(engine, music_time, 2.0f, 1.0f);
const size_t events_after_reset = backend.get_events().size();
printf(" Events during reset phase: %zu\n",
events_after_reset - events_before_reset_phase);
-
- // Verify patterns triggered throughout
assert(backend.get_events().size() > 0);
engine.shutdown();
@@ -278,49 +196,40 @@ void test_continuous_acceleration() {
printf("Test: Continuous acceleration from 0.5x to 2.0x...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
float music_time = 0.0f;
- float tempo_scale = 0.5f;
float physical_time = 0.0f;
+ const float dt = 0.05f;
+ const float min_tempo = 0.5f;
+ const float max_tempo = 2.0f;
- const float dt = 0.05f; // 50ms steps for smoother curve
-
- // Accelerate from 0.5x to 2.0x over 10 seconds
printf(" Accelerating 0.5x → 2.0x over 10 seconds\n");
- float min_tempo = 0.5f;
- float max_tempo = 2.0f;
+ auto accel_fn = [min_tempo, max_tempo](float t) {
+ const float progress = t / 10.0f;
+ return fmaxf(min_tempo, fminf(max_tempo,
+ min_tempo + progress * (max_tempo - min_tempo)));
+ };
- for (int i = 0; i < 200; ++i) {
+ const int steps = (int)(10.0f / dt);
+ for (int i = 0; i < steps; ++i) {
physical_time += dt;
- float progress = physical_time / 10.0f; // 0.0 to 1.0
- tempo_scale = min_tempo + progress * (max_tempo - min_tempo);
- tempo_scale = fmaxf(min_tempo, fminf(max_tempo, tempo_scale));
-
+ const float tempo_scale = accel_fn(physical_time);
music_time += dt * tempo_scale;
engine.update(music_time, dt * tempo_scale);
-
- // Log at key points
if (i % 50 == 0) {
printf(" t=%.1fs: tempo=%.2fx, music_time=%.3f\n", physical_time,
tempo_scale, music_time);
}
}
- printf(" Final: tempo=%.2fx, music_time=%.3f\n", tempo_scale, music_time);
-
- // Verify tempo reached target
- assert(tempo_scale >= 1.99f);
+ const float final_tempo = accel_fn(physical_time);
+ printf(" Final: tempo=%.2fx, music_time=%.3f\n", final_tempo, music_time);
+ assert(final_tempo >= 1.99f);
- // Verify music_time progressed correctly
- // Integral of (0.5 + 1.5t/10) from 0 to 10 = 0.5*10 + 1.5*10²/(2*10) = 5
- // + 7.5 = 12.5
+ // Verify music_time (integral: 0.5*10 + 1.5*10²/(2*10) = 12.5)
const float expected_music_time = 12.5f;
printf(" Expected music_time: %.3f, actual: %.3f\n", expected_music_time,
music_time);
@@ -334,40 +243,33 @@ void test_oscillating_tempo() {
printf("Test: Oscillating tempo (sine wave)...\n");
MockAudioBackend backend;
- audio_set_backend(&backend);
-
AudioEngine engine;
- engine.init();
- engine.load_music_data(&g_tracker_score, g_tracker_samples,
- g_tracker_sample_assets, g_tracker_samples_count);
+ setup_audio_test(backend, engine);
float music_time = 0.0f;
float physical_time = 0.0f;
-
const float dt = 0.05f;
- // Oscillate tempo between 0.8x and 1.2x
printf(" Oscillating tempo: 0.8x ↔ 1.2x\n");
- for (int i = 0; i < 100; ++i) {
- physical_time += dt;
- float tempo_scale = 1.0f + 0.2f * sinf(physical_time * 2.0f);
+ auto oscil_fn = [](float t) {
+ return 1.0f + 0.2f * sinf(t * 2.0f);
+ };
+ const int steps = 100;
+ for (int i = 0; i < steps; ++i) {
+ physical_time += dt;
+ const float tempo_scale = oscil_fn(physical_time);
music_time += dt * tempo_scale;
engine.update(music_time, dt * tempo_scale);
-
if (i % 25 == 0) {
printf(" t=%.2fs: tempo=%.3fx, music_time=%.3f\n", physical_time,
tempo_scale, music_time);
}
}
- // After oscillation, music_time should be approximately equal to
- // physical_time (since average tempo is 1.0x)
printf(" Final: physical_time=%.2fs, music_time=%.3f (expected ~%.2f)\n",
physical_time, music_time, physical_time);
-
- // Allow some tolerance for integral error
assert(std::abs(music_time - physical_time) < 0.5f);
engine.shutdown();