diff options
| -rw-r--r-- | src/audio/wav_dump_backend.cc | 111 | ||||
| -rw-r--r-- | src/audio/wav_dump_backend.h | 5 | ||||
| -rw-r--r-- | src/main.cc | 41 | ||||
| -rw-r--r-- | src/tests/test_wav_dump.cc | 50 |
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() { |
