diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/main.cc | 43 | ||||
| -rw-r--r-- | src/audio/audio.cc | 17 | ||||
| -rw-r--r-- | src/audio/tracker.cc | 11 | ||||
| -rw-r--r-- | src/effects/flash_cube_effect.cc | 6 | ||||
| -rw-r--r-- | src/effects/gaussian_blur_effect.h | 4 | ||||
| -rw-r--r-- | src/effects/particle_spray_effect.h | 2 | ||||
| -rw-r--r-- | src/effects/particles_effect.h | 2 | ||||
| -rw-r--r-- | src/effects/sdf_test_effect.cc | 4 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 4 | ||||
| -rw-r--r-- | src/gpu/gpu.cc | 1 | ||||
| -rw-r--r-- | src/tests/gpu/test_shader_assets.cc | 4 |
11 files changed, 51 insertions, 47 deletions
diff --git a/src/app/main.cc b/src/app/main.cc index 537da74..3c80520 100644 --- a/src/app/main.cc +++ b/src/app/main.cc @@ -207,7 +207,10 @@ int main(int argc, char** argv) { #endif /* !defined(STRIP_ALL) */ // Pre-fill ring buffer to target lookahead (prevents startup delay) - fill_audio_buffer(audio_get_required_prefill_time(), 0.0); + // Skip pre-fill in WAV dump mode (direct render, no ring buffer) + if (!dump_wav) { + fill_audio_buffer(audio_get_required_prefill_time(), 0.0); + } audio_start(); g_last_audio_time = audio_get_playback_time(); // Initialize after start @@ -268,37 +271,41 @@ int main(int argc, char** argv) { printf("Running WAV dump simulation (%.1fs - %.1fs)...\n", start_time, end_time); - // Seek to start time if needed + // Seek to start time if needed (advance state without rendering) if (start_time > 0.0f) { const double step = 1.0 / 60.0; for (double t = 0.0; t < start_time; t += step) { - fill_audio_buffer(step, t); - audio_render_silent((float)step); + g_audio_engine.update(g_music_time, (float)step * g_tempo_scale); + g_music_time += (float)step * g_tempo_scale; } printf("Seeked to %.1fs\n", start_time); } - 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 + const float update_dt = 1.0f / 60.0f; // 60Hz update rate + const int sample_rate = 32000; - AudioRingBuffer* ring_buffer = audio_get_ring_buffer(); - std::vector<float> chunk_buffer(samples_per_update); + std::vector<float> chunk_buffer(2048); // Max samples for one update double physical_time = start_time; + double frame_accumulator = 0.0; while (physical_time < end_time) { - // Update music time and tracker (using tempo logic from - // fill_audio_buffer) - fill_audio_buffer(update_dt, physical_time); + // Calculate exact frames for this update + frame_accumulator += sample_rate * update_dt; + const int frames_this_update = (int)frame_accumulator; + frame_accumulator -= frames_this_update; + const int samples_this_update = frames_this_update * 2; - // Read rendered audio from ring buffer - if (ring_buffer != nullptr) { - ring_buffer->read(chunk_buffer.data(), samples_per_update); - } + // Update tracker/audio state + g_audio_engine.update(g_music_time, update_dt * g_tempo_scale); - // Write to WAV file - wav_backend.write_audio(chunk_buffer.data(), samples_per_update); + // Render directly to buffer (bypass ring buffer) + if (frames_this_update > 0) { + synth_render(chunk_buffer.data(), frames_this_update); + wav_backend.write_audio(chunk_buffer.data(), samples_this_update); + } + // Advance music time + g_music_time += update_dt * g_tempo_scale; physical_time += update_dt; // Progress indicator every second diff --git a/src/audio/audio.cc b/src/audio/audio.cc index ba76a28..a220fbb 100644 --- a/src/audio/audio.cc +++ b/src/audio/audio.cc @@ -78,9 +78,9 @@ void audio_start() { #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; + 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); } @@ -97,11 +97,12 @@ 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 + // 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; diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc index 37f0683..38c814d 100644 --- a/src/audio/tracker.cc +++ b/src/audio/tracker.cc @@ -193,7 +193,8 @@ static int get_free_pattern_slot() { // sample-accurate timing) // volume_mult: Additional volume multiplier (for humanization) static void trigger_note_event(const TrackerEvent& event, - int start_offset_samples, float volume_mult = 1.0f) { + int start_offset_samples, + float volume_mult = 1.0f) { #if defined(DEBUG_LOG_TRACKER) // VALIDATION: Check sample_id bounds if (event.sample_id >= g_tracker_samples_count) { @@ -234,10 +235,10 @@ static void trigger_note_event(const TrackerEvent& event, } void tracker_update(float music_time_sec, float dt_music_sec) { - // TODO(timing): CRITICAL BUG - Events trigger ~180ms early over 63 beats @ BPM=90 - // Observed: Beat 63 snare at 41.82s in WAV, should be at 42.00s (180ms drift) - // NOTE: This is NOT a float vs double precision issue - floats handle <500s times fine - // Root cause unknown - suspects: + // TODO(timing): CRITICAL BUG - Events trigger ~180ms early over 63 beats @ + // BPM=90 Observed: Beat 63 snare at 41.82s in WAV, should be at 42.00s (180ms + // drift) NOTE: This is NOT a float vs double precision issue - floats handle + // <500s times fine Root cause unknown - suspects: // 1. Systematic bias in time calculation (not random accumulation) // 2. Truncation in audio.cc:103 chunk_frames = (int)(dt * sample_rate) // 3. BPM calculation precision below (unit_duration_sec) diff --git a/src/effects/flash_cube_effect.cc b/src/effects/flash_cube_effect.cc index 29e9897..383e66a 100644 --- a/src/effects/flash_cube_effect.cc +++ b/src/effects/flash_cube_effect.cc @@ -60,12 +60,12 @@ void FlashCubeEffect::render(WGPURenderPassEncoder pass, // Detect beat changes for flash trigger (using intensity as proxy for beat // hits) Intensity spikes on beats, so we can use it to trigger flashes if (uniforms.audio_intensity > 0.5f && - flash_intensity_ < 0.3f) { // High intensity + flash cooled down + flash_intensity_ < 0.2f) { // High intensity + flash cooled down flash_intensity_ = 1.0f; // Trigger full flash } // Exponential decay of flash - flash_intensity_ *= 0.90f; // Slower fade for more visible effect + flash_intensity_ *= 0.95f; // Slower fade for more visible effect // Always have base brightness, add flash on top float base_brightness = 0.2f; @@ -80,7 +80,7 @@ void FlashCubeEffect::render(WGPURenderPassEncoder pass, // Slowly rotate the cube for visual interest scene_.objects[0].rotation = - quat::from_axis(vec3(0.3f, 1, 0.2f), uniforms.time * 0.05f); + quat::from_axis(vec3(0.3f, 1, 0.2f), uniforms.time * 0.04f); // Position camera OUTSIDE the cube looking at it from a distance // This way we see the cube as a background element diff --git a/src/effects/gaussian_blur_effect.h b/src/effects/gaussian_blur_effect.h index 651c5c3..bf1062f 100644 --- a/src/effects/gaussian_blur_effect.h +++ b/src/effects/gaussian_blur_effect.h @@ -8,9 +8,9 @@ // Parameters for GaussianBlurEffect (set at construction time) struct GaussianBlurParams { - float strength = 1.0f; // Default + float strength = 1.0f; // Default float strength_audio = 0.5f; // how much to pulse with audio - float stretch = 1.f; // y/x axis ratio + float stretch = 1.f; // y/x axis ratio float _pad = 0.; }; static_assert(sizeof(GaussianBlurParams) == 16, diff --git a/src/effects/particle_spray_effect.h b/src/effects/particle_spray_effect.h index c83d691..216e13f 100644 --- a/src/effects/particle_spray_effect.h +++ b/src/effects/particle_spray_effect.h @@ -3,8 +3,8 @@ #pragma once -#include "gpu/effect.h" #include "effects/particle_defs.h" +#include "gpu/effect.h" class ParticleSprayEffect : public Effect { public: diff --git a/src/effects/particles_effect.h b/src/effects/particles_effect.h index 6d46ea2..a69039f 100644 --- a/src/effects/particles_effect.h +++ b/src/effects/particles_effect.h @@ -3,8 +3,8 @@ #pragma once -#include "gpu/effect.h" #include "effects/particle_defs.h" +#include "gpu/effect.h" class ParticlesEffect : public Effect { public: diff --git a/src/effects/sdf_test_effect.cc b/src/effects/sdf_test_effect.cc index 28b3513..264809f 100644 --- a/src/effects/sdf_test_effect.cc +++ b/src/effects/sdf_test_effect.cc @@ -9,8 +9,8 @@ SDFTestEffect::SDFTestEffect(const GpuContext& ctx) : SDFEffect(ctx) { ResourceBinding bindings[] = { {uniforms_.get(), WGPUBufferBindingType_Uniform}, {camera_params_.get(), WGPUBufferBindingType_Uniform}}; - pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, - sdf_test_shader_wgsl, bindings, 2); + pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, sdf_test_shader_wgsl, + bindings, 2); pass_.vertex_count = 3; } diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index 85498ad..d4df20b 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -40,12 +40,8 @@ #include <memory> - - // Common particle definition is now in effects/particle_defs.h - - // Auto-generated functions from sequence compiler void LoadTimeline(MainSequence& main_seq, const GpuContext& ctx); diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc index ce234fa..ff4def7 100644 --- a/src/gpu/gpu.cc +++ b/src/gpu/gpu.cc @@ -143,7 +143,6 @@ RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format, WGPUShaderModule shader_module = wgpuDeviceCreateShaderModule(device, &shader_desc); - // Create Bind Group Layout & Bind Group std::vector<WGPUBindGroupLayoutEntry> bgl_entries; std::vector<WGPUBindGroupEntry> bg_entries; diff --git a/src/tests/gpu/test_shader_assets.cc b/src/tests/gpu/test_shader_assets.cc index 135c477..63f9b5d 100644 --- a/src/tests/gpu/test_shader_assets.cc +++ b/src/tests/gpu/test_shader_assets.cc @@ -42,8 +42,8 @@ int main() { all_passed &= validate_shader(AssetId::ASSET_SHADER_COMMON_UNIFORMS, "COMMON_UNIFORMS", {"struct", "GlobalUniforms"}); - all_passed &= validate_shader(AssetId::ASSET_SHADER_SDF_SHAPES, - "SDF_SHAPES", {"fn", "sd"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_SDF_SHAPES, "SDF_SHAPES", + {"fn", "sd"}); all_passed &= validate_shader(AssetId::ASSET_SHADER_LIGHTING, "LIGHTING", {"fn", "calc"}); all_passed &= validate_shader(AssetId::ASSET_SHADER_RAY_BOX, "RAY_BOX", |
