summaryrefslogtreecommitdiff
path: root/src/tests/test_jittered_audio.cc
blob: f880c74766d909eb4f7ca3300bd69a555083b80a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// This file is part of the 64k demo project.
// It tests the ring buffer under jittered consumption (stress test).

#include <stdio.h>

#if !defined(STRIP_ALL)

#include "audio/audio.h"
#include "audio/jittered_audio_backend.h"
#include "audio/synth.h"
#include "audio/tracker.h"
#include <assert.h>
#include <chrono>
#include <thread>

void test_jittered_audio_basic() {
  printf("Test: Basic jittered audio consumption...\n");

  // Initialize audio system
  synth_init();
  tracker_init();

  // Set up jittered backend with realistic parameters
  // At 32kHz, 10ms = 320 samples = 160 frames (stereo)
  // Jitter of ±5ms means 5-15ms intervals, or 80-240 frames
  JitteredAudioBackend jittered_backend;
  jittered_backend.set_base_interval(10.0f); // 10ms base interval
  jittered_backend.set_jitter_amount(5.0f);  // ±5ms jitter
  jittered_backend.set_chunk_size_range(
      80, 240); // Realistic chunk sizes for 5-15ms

  audio_set_backend(&jittered_backend);
  audio_init();

  // Start audio thread
  audio_start();
  assert(jittered_backend.is_running());

  // Simulate main loop for 0.5 seconds (quick stress test)
  const float total_time = 0.5f;
  const float dt = 1.0f / 60.0f; // 60fps
  float music_time = 0.0f;

  for (float t = 0.0f; t < total_time; t += dt) {
    music_time += dt; // Normal tempo

    // Update tracker and fill buffer
    tracker_update(music_time);
    audio_render_ahead(music_time, dt);

    // Sleep to simulate frame time
    std::this_thread::sleep_for(std::chrono::milliseconds(16));
  }

  // Stop audio
  audio_shutdown();

  // Check results
  const int frames_consumed = jittered_backend.get_total_frames_consumed();
  const int underruns = jittered_backend.get_underrun_count();

  printf("  Frames consumed: %d\n", frames_consumed);
  printf("  Underruns: %d\n", underruns);

  // Should have consumed roughly 0.5 seconds worth of audio
  // At 32kHz stereo: 0.5 seconds = 16000 samples = 8000 frames
  assert(frames_consumed > 4000);  // At least 0.25 seconds (8000 samples)
  assert(frames_consumed < 12000); // At most 0.75 seconds (24000 samples)

  // Underruns are acceptable in this test, but shouldn't be excessive
  assert(underruns < 20); // Less than 20 underruns in 0.5 seconds

  printf("  ✓ Basic jittered audio consumption PASSED\n");
}

void test_jittered_audio_with_acceleration() {
  printf("Test: Jittered audio with tempo acceleration...\n");

  // Initialize audio system
  synth_init();
  tracker_init();

  // Set up jittered backend with aggressive settings for stress test
  // At 32kHz, 15ms = 480 samples = 240 frames (stereo)
  // Jitter of ±10ms means 5-25ms intervals, or 80-400 frames
  JitteredAudioBackend jittered_backend;
  jittered_backend.set_base_interval(15.0f);      // Slower consumption
  jittered_backend.set_jitter_amount(10.0f);      // High jitter
  jittered_backend.set_chunk_size_range(80, 400); // Realistic stress test range

  audio_set_backend(&jittered_backend);
  audio_init();

  // Start audio thread
  audio_start();

  // Simulate acceleration scenario (similar to real demo)
  const float total_time = 3.0f;
  const float dt = 1.0f / 60.0f;
  float music_time = 0.0f;
  float physical_time = 0.0f;

  for (int frame = 0; frame < 180; ++frame) { // 3 seconds @ 60fps
    physical_time = frame * dt;

    // Variable tempo (accelerate from 1.5-3s)
    float tempo_scale = 1.0f;
    if (physical_time >= 1.5f && physical_time < 3.0f) {
      const float progress = (physical_time - 1.5f) / 1.5f;
      tempo_scale = 1.0f + progress * 1.0f; // 1.0 → 2.0
    }

    music_time += dt * tempo_scale;

    // Update tracker and fill buffer
    tracker_update(music_time);
    audio_render_ahead(music_time, dt);

    // Sleep to simulate frame time
    std::this_thread::sleep_for(std::chrono::milliseconds(16));

    // Progress indicator (every 30 frames for shorter test)
    if (frame % 30 == 0) {
      printf(
          "  Frame %d: music_time=%.2fs, tempo=%.2fx, consumed=%d frames, "
          "underruns=%d\r",
          frame, music_time, tempo_scale,
          jittered_backend.get_total_frames_consumed(),
          jittered_backend.get_underrun_count());
      fflush(stdout);
    }
  }
  printf("\n");

  // Stop audio
  audio_shutdown();

  // Check results
  const int frames_consumed = jittered_backend.get_total_frames_consumed();
  const int underruns = jittered_backend.get_underrun_count();

  printf("  Total frames consumed: %d\n", frames_consumed);
  printf("  Total underruns: %d\n", underruns);

  // Should have consumed roughly 3.75 seconds worth of audio
  // (3 seconds physical time with acceleration 1.0x → 2.0x)
  // At 32kHz stereo: 3.75 seconds = 120000 samples = 60000 frames
  assert(frames_consumed > 40000); // At least 2.5 seconds (80000 samples)
  assert(frames_consumed < 80000); // At most 5 seconds (160000 samples)

  // During acceleration with jitter, some underruns are expected but not
  // excessive
  assert(underruns < 60); // Less than 60 underruns in 3 seconds

  printf("  ✓ Jittered audio with acceleration PASSED\n");
}

int main() {
  printf("Running Jittered Audio Backend tests...\n\n");
  test_jittered_audio_basic();
  test_jittered_audio_with_acceleration();
  printf("\n✅ All Jittered Audio Backend tests PASSED\n");
  return 0;
}

#else

int main() {
  printf("Jittered Audio Backend tests skipped (STRIP_ALL enabled)\n");
  return 0;
}

#endif /* !defined(STRIP_ALL) */