summaryrefslogtreecommitdiff
path: root/src/audio/audio.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/audio.cc')
-rw-r--r--src/audio/audio.cc46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/audio/audio.cc b/src/audio/audio.cc
index d044b00..f5bc4ab 100644
--- a/src/audio/audio.cc
+++ b/src/audio/audio.cc
@@ -57,11 +57,35 @@ void audio_init() {
g_audio_backend->init();
}
+float audio_get_required_prefill_time() {
+ return (float)RING_BUFFER_LOOKAHEAD_MS / 1000.0f;
+}
+
+bool audio_is_prefilled() {
+ const int buffered = g_ring_buffer.available_read();
+ const float buffered_time =
+ (float)buffered / (RING_BUFFER_SAMPLE_RATE * RING_BUFFER_CHANNELS);
+ const float required = audio_get_required_prefill_time();
+ return buffered_time >= (required - 0.001f); // 1ms tolerance
+}
+
void audio_start() {
if (g_audio_backend == nullptr) {
printf("Cannot start: audio not initialized.\n");
return;
}
+
+#if !defined(STRIP_ALL)
+ if (!audio_is_prefilled()) {
+ const int buffered = g_ring_buffer.available_read();
+ const float buffered_ms =
+ (float)buffered / (RING_BUFFER_SAMPLE_RATE * RING_BUFFER_CHANNELS) *
+ 1000.0f;
+ printf("WARNING: Audio buffer not pre-filled (%.1fms < %.1fms)\n",
+ buffered_ms, audio_get_required_prefill_time() * 1000.0f);
+ }
+#endif
+
g_audio_backend->start();
}
@@ -73,12 +97,21 @@ void audio_render_ahead(float music_time, float dt, float target_fill) {
// Render in small chunks to keep synth time synchronized with tracker
// Chunk size: one frame's worth of audio (~16.6ms @ 60fps)
+ // TODO(timing): CRITICAL BUG - Truncation here may cause 180ms drift over 63 beats
+ // (int) cast loses fractional samples: 0.333 samples/frame * 2560 frames = 853 samples = 27ms
+ // But observed drift is 180ms, so this is not the only source (27ms < 180ms)
+ // NOTE: This is NOT a float vs double precision issue - floats handle <500s times fine
+ // See also: tracker.cc BPM timing calculation
const int chunk_frames = (int)(dt * RING_BUFFER_SAMPLE_RATE);
const int chunk_samples = chunk_frames * RING_BUFFER_CHANNELS;
if (chunk_frames <= 0)
return;
+ static int64_t g_total_render_calls = 0;
+ static int64_t g_total_frames_rendered = 0;
+ const int64_t frames_before = g_ring_buffer.get_total_written() / RING_BUFFER_CHANNELS;
+
// Keep rendering small chunks until buffer is full enough
while (true) {
// First, try to flush any pending samples from previous partial writes
@@ -195,6 +228,19 @@ void audio_render_ahead(float music_time, float dt, float target_fill) {
}
}
}
+
+ // DEBUG: Track actual frames rendered vs expected
+ const int64_t frames_after = g_ring_buffer.get_total_written() / RING_BUFFER_CHANNELS;
+ const int64_t actual_rendered = frames_after - frames_before;
+ g_total_render_calls++;
+ g_total_frames_rendered += actual_rendered;
+
+ if (g_total_render_calls % 600 == 0) { // Every 10 seconds at 60fps
+ const float expected_frames = g_total_render_calls * (float)(chunk_frames);
+ const float drift_ms = (expected_frames - g_total_frames_rendered) / RING_BUFFER_SAMPLE_RATE * 1000.0f;
+ printf("[RENDER_DRIFT] calls=%lld expect=%.1f actual=%lld drift=%.2fms\n",
+ g_total_render_calls, expected_frames, g_total_frames_rendered, drift_ms);
+ }
}
float audio_get_playback_time() {