summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/main.cc43
-rw-r--r--src/audio/audio.cc17
-rw-r--r--src/audio/tracker.cc11
-rw-r--r--src/effects/flash_cube_effect.cc6
-rw-r--r--src/effects/gaussian_blur_effect.h4
-rw-r--r--src/effects/particle_spray_effect.h2
-rw-r--r--src/effects/particles_effect.h2
-rw-r--r--src/effects/sdf_test_effect.cc4
-rw-r--r--src/gpu/demo_effects.h4
-rw-r--r--src/gpu/gpu.cc1
-rw-r--r--src/tests/gpu/test_shader_assets.cc4
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",