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.cc55
1 files changed, 50 insertions, 5 deletions
diff --git a/src/audio/audio.cc b/src/audio/audio.cc
index de1c702..3b7b7fd 100644
--- a/src/audio/audio.cc
+++ b/src/audio/audio.cc
@@ -17,6 +17,12 @@
// Global ring buffer for audio streaming
static AudioRingBuffer g_ring_buffer;
+// Pending write buffer for partially written samples
+// Maximum size: one chunk (533 frames @ 60fps = 1066 samples stereo)
+#define MAX_PENDING_SAMPLES 2048
+static float g_pending_buffer[MAX_PENDING_SAMPLES];
+static int g_pending_samples = 0; // How many samples are waiting to be written
+
// Global backend pointer for audio abstraction
static AudioBackend* g_audio_backend = nullptr;
static MiniaudioBackend g_default_backend;
@@ -54,6 +60,9 @@ int register_spec_asset(AssetId id) {
void audio_init() {
synth_init();
+ // Clear pending buffer
+ g_pending_samples = 0;
+
// Use default backend if none set
if (g_audio_backend == nullptr) {
g_audio_backend = &g_default_backend;
@@ -85,6 +94,31 @@ void audio_render_ahead(float music_time, float dt) {
// Keep rendering small chunks until buffer is full enough
while (true) {
+ // First, try to flush any pending samples from previous partial writes
+ if (g_pending_samples > 0) {
+ const int written = g_ring_buffer.write(g_pending_buffer, g_pending_samples);
+
+ if (written > 0) {
+ // Some or all samples were written
+ // Move remaining samples to front of buffer
+ const int remaining = g_pending_samples - written;
+ if (remaining > 0) {
+ for (int i = 0; i < remaining; ++i) {
+ g_pending_buffer[i] = g_pending_buffer[written + i];
+ }
+ }
+ g_pending_samples = remaining;
+
+ // Notify backend
+ if (g_audio_backend != nullptr) {
+ g_audio_backend->on_frames_rendered(written / RING_BUFFER_CHANNELS);
+ }
+ }
+
+ // If still have pending samples, buffer is full - wait for consumption
+ if (g_pending_samples > 0) break;
+ }
+
// Check current buffer state
const int buffered_samples = g_ring_buffer.available_read();
const float buffered_time =
@@ -93,7 +127,7 @@ void audio_render_ahead(float music_time, float dt) {
// Stop if buffer is full enough
if (buffered_time >= target_lookahead) break;
- // Check if buffer has space for this chunk BEFORE rendering
+ // Check if buffer has space for this chunk
const int available_space = g_ring_buffer.available_write();
if (available_space < chunk_samples) {
// Buffer is too full, wait for audio callback to consume more
@@ -106,17 +140,28 @@ void audio_render_ahead(float music_time, float dt) {
// Render audio from synth (advances synth state incrementally)
synth_render(temp_buffer, chunk_frames);
- // Write to ring buffer (should succeed since we checked space)
+ // Write to ring buffer
const int written = g_ring_buffer.write(temp_buffer, chunk_samples);
- // Notify backend of frames rendered (for testing/tracking)
+ // If partial write, save remaining samples to pending buffer
+ if (written < chunk_samples) {
+ const int remaining = chunk_samples - written;
+ if (remaining <= MAX_PENDING_SAMPLES) {
+ for (int i = 0; i < remaining; ++i) {
+ g_pending_buffer[i] = temp_buffer[written + i];
+ }
+ g_pending_samples = remaining;
+ }
+ }
+
+ // Notify backend of frames rendered (count frames sent to synth)
if (g_audio_backend != nullptr) {
- g_audio_backend->on_frames_rendered(written / RING_BUFFER_CHANNELS);
+ g_audio_backend->on_frames_rendered(chunk_frames);
}
delete[] temp_buffer;
- // Safety: if write failed unexpectedly, stop to avoid infinite loop
+ // If we couldn't write everything, stop and retry next frame
if (written < chunk_samples) break;
}
}