summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio/wav_dump_backend.cc111
-rw-r--r--src/audio/wav_dump_backend.h5
-rw-r--r--src/main.cc41
-rw-r--r--src/tests/test_wav_dump.cc50
4 files changed, 101 insertions, 106 deletions
diff --git a/src/audio/wav_dump_backend.cc b/src/audio/wav_dump_backend.cc
index 9fa13f5..eda835c 100644
--- a/src/audio/wav_dump_backend.cc
+++ b/src/audio/wav_dump_backend.cc
@@ -5,10 +5,6 @@
#if !defined(STRIP_ALL)
-#include "audio.h"
-#include "ring_buffer.h"
-#include "synth.h"
-#include "tracker.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
@@ -16,7 +12,7 @@
WavDumpBackend::WavDumpBackend()
: wav_file_(nullptr), samples_written_(0),
output_filename_("audio_dump.wav"), is_active_(false),
- duration_sec_(60.0f) {
+ duration_sec_(0.0f) {
sample_buffer_.resize(kBufferSize);
}
@@ -28,10 +24,6 @@ void WavDumpBackend::set_output_file(const char* filename) {
output_filename_ = filename;
}
-void WavDumpBackend::set_duration(float seconds) {
- duration_sec_ = seconds;
-}
-
void WavDumpBackend::init() {
// Open WAV file for writing
wav_file_ = fopen(output_filename_, "wb");
@@ -49,90 +41,29 @@ void WavDumpBackend::init() {
void WavDumpBackend::start() {
is_active_ = true;
- printf("WAV dump started, rendering audio...\n");
-
- // Render audio in small chunks with tracker updates
- // This matches the seek logic in main.cc
- const float update_dt = 1.0f / 60.0f; // 60Hz update rate (matches main loop)
- const int frames_per_update = (int)(kSampleRate * update_dt); // ~533 frames
- const int samples_per_update =
- frames_per_update * 2; // Stereo: 2 samples per frame
- const int total_updates = (int)(duration_sec_ / update_dt);
-
- // Music time tracking
- float music_time = 0.0f;
- float tempo_scale = 1.0f;
- float physical_time = 0.0f;
-
- // Get ring buffer for reading
- AudioRingBuffer* ring_buffer = audio_get_ring_buffer();
-
- // Temporary buffer for each update chunk (stereo)
- std::vector<float> chunk_buffer(samples_per_update);
-
- for (int update_count = 0; update_count < total_updates; ++update_count) {
- // Update tempo scaling (matches main.cc phases)
- if (physical_time < 10.0f) {
- tempo_scale = 1.0f;
- } else if (physical_time < 15.0f) {
- const float progress = (physical_time - 10.0f) / 5.0f;
- tempo_scale = 1.0f + progress * 1.0f; // 1.0 → 2.0
- } else if (physical_time < 20.0f) {
- tempo_scale = 1.0f;
- } else if (physical_time < 25.0f) {
- const float progress = (physical_time - 20.0f) / 5.0f;
- tempo_scale = 1.0f - progress * 0.5f; // 1.0 → 0.5
- } else {
- tempo_scale = 1.0f;
- }
-
- // Advance music time
- music_time += update_dt * tempo_scale;
- physical_time += update_dt;
-
- // Update tracker (triggers patterns)
- tracker_update(music_time);
-
- // Fill ring buffer with upcoming audio
- audio_render_ahead(music_time, update_dt);
-
- // Read from ring buffer (same as audio callback would do)
- if (ring_buffer != nullptr) {
- ring_buffer->read(chunk_buffer.data(), samples_per_update);
- }
-
- // Convert float to int16 and write to WAV (stereo interleaved)
- for (int i = 0; i < samples_per_update; ++i) {
- float sample = chunk_buffer[i];
- if (sample > 1.0f)
- sample = 1.0f;
- if (sample < -1.0f)
- sample = -1.0f;
+ printf("WAV dump started (passive mode - frontend drives rendering)\n");
+}
- const int16_t sample_i16 = (int16_t)(sample * 32767.0f);
- fwrite(&sample_i16, sizeof(int16_t), 1, wav_file_);
- }
+void WavDumpBackend::write_audio(const float* samples, int num_samples) {
+ if (!is_active_ || wav_file_ == nullptr) {
+ return;
+ }
- samples_written_ += samples_per_update;
+ // Convert float samples to int16 and write to WAV
+ for (int i = 0; i < num_samples; ++i) {
+ float sample = samples[i];
- // Progress indicator
- if (update_count % 60 == 0) {
- printf(" Rendering: %.1fs / %.1fs (music: %.1fs, tempo: %.2fx)\r",
- physical_time, duration_sec_, music_time, tempo_scale);
- fflush(stdout);
- }
+ // Clamp to [-1.0, 1.0]
+ if (sample > 1.0f)
+ sample = 1.0f;
+ if (sample < -1.0f)
+ sample = -1.0f;
- // Call frame rendering hook (pass frames, not samples)
- on_frames_rendered(frames_per_update);
+ const int16_t sample_i16 = (int16_t)(sample * 32767.0f);
+ fwrite(&sample_i16, sizeof(int16_t), 1, wav_file_);
}
- printf(
- "\nWAV dump complete: %zu samples (%.2f seconds stereo, %.2f music "
- "time)\n",
- samples_written_, (float)samples_written_ / (kSampleRate * 2),
- music_time);
-
- is_active_ = false;
+ samples_written_ += num_samples;
}
void WavDumpBackend::shutdown() {
@@ -142,8 +73,12 @@ void WavDumpBackend::shutdown() {
fclose(wav_file_);
wav_file_ = nullptr;
- printf("WAV file written: %s\n", output_filename_);
+ const float duration_sec = (float)samples_written_ / (kSampleRate * 2);
+ printf("WAV file written: %s (%.2f seconds, %zu samples)\n",
+ output_filename_, duration_sec, samples_written_);
}
+
+ is_active_ = false;
}
void WavDumpBackend::write_wav_header(FILE* file, uint32_t num_samples) {
diff --git a/src/audio/wav_dump_backend.h b/src/audio/wav_dump_backend.h
index eb6e011..6482ef3 100644
--- a/src/audio/wav_dump_backend.h
+++ b/src/audio/wav_dump_backend.h
@@ -26,8 +26,9 @@ class WavDumpBackend : public AudioBackend {
// Set output filename (call before init())
void set_output_file(const char* filename);
- // Set duration in seconds (default: 60s, call before start())
- void set_duration(float seconds);
+ // Write audio data to WAV file (stereo interleaved float samples)
+ // num_samples: Total number of samples (2x num_frames for stereo)
+ void write_audio(const float* samples, int num_samples);
// Get total samples written
size_t get_samples_written() const {
diff --git a/src/main.cc b/src/main.cc
index 0fb0fb6..a3e4a69 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -6,10 +6,12 @@
#include "audio/audio.h"
#include "audio/audio_engine.h"
#include "audio/gen.h"
+#include "audio/ring_buffer.h"
#include "audio/synth.h"
#include "audio/tracker.h"
#if !defined(STRIP_ALL)
#include "audio/wav_dump_backend.h"
+#include <vector>
#endif
#include "generated/assets.h" // Include generated asset header
#include "gpu/demo_effects.h" // For GetDemoDuration()
@@ -163,8 +165,45 @@ int main(int argc, char** argv) {
audio_start();
#if !defined(STRIP_ALL)
- // In WAV dump mode, audio_start() renders everything and we can exit
+ // In WAV dump mode, run headless simulation and write audio to file
if (dump_wav) {
+ printf("Running WAV dump simulation...\n");
+
+ const float demo_duration = GetDemoDuration();
+ const float max_duration = (demo_duration > 0.0f) ? demo_duration : 60.0f;
+ const float update_dt = 1.0f / 60.0f; // 60Hz update rate
+ const int frames_per_update = (int)(32000 * update_dt); // ~533 frames
+ const int samples_per_update = frames_per_update * 2; // Stereo
+
+ AudioRingBuffer* ring_buffer = audio_get_ring_buffer();
+ std::vector<float> chunk_buffer(samples_per_update);
+
+ double physical_time = 0.0;
+ while (physical_time < max_duration) {
+ // Update music time and tracker (using tempo logic from fill_audio_buffer)
+ fill_audio_buffer(physical_time);
+
+ // Read rendered audio from ring buffer
+ if (ring_buffer != nullptr) {
+ ring_buffer->read(chunk_buffer.data(), samples_per_update);
+ }
+
+ // Write to WAV file
+ wav_backend.write_audio(chunk_buffer.data(), samples_per_update);
+
+ physical_time += update_dt;
+
+ // Progress indicator every second
+ if ((int)physical_time % 1 == 0 && physical_time - update_dt < (int)physical_time) {
+ printf(" Rendering: %.1fs / %.1fs (music: %.1fs, tempo: %.2fx)\r",
+ physical_time, max_duration, g_music_time, g_tempo_scale);
+ fflush(stdout);
+ }
+ }
+
+ printf("\nWAV dump complete: %.2fs physical, %.2fs music time\n",
+ physical_time, g_music_time);
+
audio_shutdown();
gpu_shutdown();
platform_shutdown(&platform_state);
diff --git a/src/tests/test_wav_dump.cc b/src/tests/test_wav_dump.cc
index c68578b..cc2de19 100644
--- a/src/tests/test_wav_dump.cc
+++ b/src/tests/test_wav_dump.cc
@@ -3,10 +3,12 @@
#include "audio/audio.h"
#include "audio/audio_engine.h"
+#include "audio/ring_buffer.h"
#include "audio/wav_dump_backend.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
+#include <vector>
#if !defined(STRIP_ALL)
@@ -32,31 +34,48 @@ void test_wav_format_matches_live_audio() {
const char* test_file = "test_format.wav";
+ // Initialize audio system
+ audio_init();
+
+ // Initialize AudioEngine
+ AudioEngine engine;
+ engine.init();
+
// Create WAV dump backend
WavDumpBackend wav_backend;
wav_backend.set_output_file(test_file);
- wav_backend.set_duration(2.0f); // Only 2 seconds for quick testing
- audio_set_backend(&wav_backend);
+ wav_backend.init();
+ wav_backend.start();
- // Initialize audio system (calls synth_init internally)
- audio_init();
+ // Simulate 2 seconds of audio rendering (frontend-driven)
+ const float duration = 2.0f;
+ const float update_dt = 1.0f / 60.0f;
+ const int frames_per_update = (int)(32000 * update_dt);
+ const int samples_per_update = frames_per_update * 2; // Stereo
- // Initialize AudioEngine (replaces direct synth_init/tracker_init)
- AudioEngine engine;
- engine.init();
+ AudioRingBuffer* ring_buffer = audio_get_ring_buffer();
+ std::vector<float> chunk_buffer(samples_per_update);
- // Manually trigger some audio for testing
- engine.update(0.0f); // Trigger patterns at t=0
+ float music_time = 0.0f;
+ for (float t = 0.0f; t < duration; t += update_dt) {
+ // Update audio engine (triggers patterns)
+ engine.update(music_time);
+ music_time += update_dt;
- // Render short duration (1 second = 60 updates @ 60Hz)
- for (int i = 0; i < 60; ++i) {
- float t = i / 60.0f;
- engine.update(t);
+ // Render audio ahead
+ audio_render_ahead(music_time, update_dt);
+
+ // Read from ring buffer
+ if (ring_buffer != nullptr) {
+ ring_buffer->read(chunk_buffer.data(), samples_per_update);
+ }
- // Simulate audio render (WavDumpBackend will handle this in start())
+ // Write to WAV file
+ wav_backend.write_audio(chunk_buffer.data(), samples_per_update);
}
- audio_start(); // This triggers the actual WAV rendering
+ // Shutdown
+ wav_backend.shutdown();
engine.shutdown();
audio_shutdown();
@@ -146,6 +165,7 @@ void test_wav_format_matches_live_audio() {
printf(" ✓ WAV format verified: stereo, 32kHz, 16-bit PCM\n");
printf(" ✓ Matches live audio output configuration\n");
+ printf(" ✓ Backend is passive (frontend-driven)\n");
}
void test_wav_stereo_buffer_size() {