diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-07 16:41:30 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-07 16:41:30 +0100 |
| commit | f2963ac821a3af1c54002ba13944552166956d04 (patch) | |
| tree | 32f25ce610a15d97ce04e7a002ceb0fc920dfd1e /src/gpu | |
| parent | 6b4dce2598a61c2901f7387aeb51a6796b180bd3 (diff) | |
fix(audio): Synchronize audio-visual timing with playback time
Problem: test_demo was "flashing a lot" - visual effects triggered ~400ms
before audio was heard, causing poor synchronization.
Root Causes:
1. Beat calculation used physical time (platform_state.time), but audio
peak measured at playback time (400ms behind due to ring buffer)
2. Peak decay too slow (0.7 per callback = 800ms fade) relative to beat
interval (500ms at 120 BPM)
Solution:
1. Use audio_get_playback_time() for beat calculation
- Automatically accounts for ring buffer latency
- No hardcoded constants (was considering hardcoding 400ms offset)
- System queries its own state
2. Faster decay rate (0.5 vs 0.7) to match beat interval
3. Added inline PeakMeterEffect for visual debugging
Changes:
- src/test_demo.cc:
- Added inline PeakMeterEffect class (red bar visualization)
- Use audio_get_playback_time() instead of physical time for beat calc
- Updated logging to show audio time
- src/audio/backend/miniaudio_backend.cc:
- Changed decay rate from 0.7 to 0.5 (500ms fade time)
- src/gpu/gpu.{h,cc}:
- Added gpu_add_custom_effect() API for runtime effect injection
- Exposed g_device, g_queue, g_format as non-static globals
- doc/PEAK_METER_DEBUG.md:
- Initial analysis of timing issues
- doc/AUDIO_TIMING_ARCHITECTURE.md:
- Comprehensive architecture documentation
- Time source hierarchy (physical → audio playback → music)
- Future work: TimeProvider class, tracker_get_bpm() API
Architectural Principle:
Single source of truth - platform_get_time() is the only physical clock.
Everything else derives from it. No hardcoded latency constants.
Result: Visual effects now sync perfectly with heard audio.
Diffstat (limited to 'src/gpu')
| -rw-r--r-- | src/gpu/gpu.cc | 13 | ||||
| -rw-r--r-- | src/gpu/gpu.h | 7 |
2 files changed, 18 insertions, 2 deletions
diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc index 45f0f34..63a30ff 100644 --- a/src/gpu/gpu.cc +++ b/src/gpu/gpu.cc @@ -21,10 +21,11 @@ static WGPUInstance g_instance = nullptr; static WGPUAdapter g_adapter = nullptr; -static WGPUDevice g_device = nullptr; -static WGPUQueue g_queue = nullptr; +WGPUDevice g_device = nullptr; // Non-static for external access (debug builds) +WGPUQueue g_queue = nullptr; // Non-static for external access (debug builds) static WGPUSurface g_surface = nullptr; static WGPUSurfaceConfiguration g_config = {}; +WGPUTextureFormat g_format = WGPUTextureFormat_BGRA8Unorm; // Exposed for custom effects static MainSequence g_main_sequence; @@ -354,6 +355,7 @@ void gpu_init(PlatformState* platform_state) { g_config.device = g_device; g_config.format = swap_chain_format; + g_format = swap_chain_format; // Update global format for external access g_config.usage = WGPUTextureUsage_RenderAttachment; g_config.width = platform_state->width; g_config.height = platform_state->height; @@ -386,6 +388,13 @@ void gpu_resize(int width, int height) { void gpu_simulate_until(float time) { g_main_sequence.simulate_until(time, 1.0f / 60.0f); } + +void gpu_add_custom_effect(Effect* effect, float start_time, float end_time, int priority) { + auto seq = std::make_shared<Sequence>(); + seq->add_effect(std::shared_ptr<Effect>(effect), start_time, end_time, priority); + seq->init(&g_main_sequence); + g_main_sequence.add_sequence(seq, 0.0f, priority); +} #endif /* !defined(STRIP_ALL) */ void gpu_shutdown() { diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h index d7f5a8d..b8f58b2 100644 --- a/src/gpu/gpu.h +++ b/src/gpu/gpu.h @@ -7,6 +7,7 @@ #include "platform/platform.h" struct PlatformState; // Forward declaration +class Effect; // Forward declaration // Basic wrapper for WebGPU buffers struct GpuBuffer { @@ -36,6 +37,12 @@ void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat); void gpu_resize(int width, int height); #if !defined(STRIP_ALL) void gpu_simulate_until(float time); +void gpu_add_custom_effect(Effect* effect, float start_time, float end_time, int priority); + +// Expose WebGPU globals for custom effects (debug builds only) +extern WGPUDevice g_device; +extern WGPUQueue g_queue; +extern WGPUTextureFormat g_format; #endif void gpu_shutdown(); |
