summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-01-31 15:44:30 +0100
committerskal <pascal.massimino@gmail.com>2026-01-31 15:44:30 +0100
commit0bb7fa35cc340a65c944e546231632379bcfa30c (patch)
tree23913b4d5d26686b45acee5648b147a44877e3a0
parent72c0cbcb38732b698d33d52459fed67af56ee2ec (diff)
feat: Add --seek command line option for fast-forward debugging
This feature allows developers to jump to a specific time in the demo sequence (e.g., './demo64k --seek 10.5'). It simulates the game logic, audio state (rendering silent buffers), and visual physics (compute shaders) from t=0 up to the target time before starting real-time playback. Audio initialization is refactored to separate device init and start.
-rw-r--r--src/audio/audio.cc15
-rw-r--r--src/audio/audio.h2
-rw-r--r--src/gpu/effect.cc38
-rw-r--r--src/gpu/effect.h4
-rw-r--r--src/gpu/gpu.cc4
-rw-r--r--src/gpu/gpu.h1
-rw-r--r--src/main.cc44
-rw-r--r--tools/spectool.cc1
8 files changed, 101 insertions, 8 deletions
diff --git a/src/audio/audio.cc b/src/audio/audio.cc
index 6249479..517e376 100644
--- a/src/audio/audio.cc
+++ b/src/audio/audio.cc
@@ -38,7 +38,9 @@ void audio_init() {
printf("Failed to open playback device.\n");
return;
}
+}
+void audio_start() {
if (ma_device_start(&g_device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&g_device);
@@ -46,6 +48,19 @@ void audio_init() {
}
}
+void audio_render_silent(float duration_sec) {
+ const int sample_rate = 32000;
+ const int chunk_size = 512;
+ int total_frames = (int)(duration_sec * sample_rate);
+ float buffer[chunk_size * 2]; // Stereo
+
+ while (total_frames > 0) {
+ int frames_to_render = (total_frames > chunk_size) ? chunk_size : total_frames;
+ synth_render(buffer, frames_to_render);
+ total_frames -= frames_to_render;
+ }
+}
+
void audio_update() {
}
diff --git a/src/audio/audio.h b/src/audio/audio.h
index dd43d97..67c83c2 100644
--- a/src/audio/audio.h
+++ b/src/audio/audio.h
@@ -5,5 +5,7 @@
#pragma once
void audio_init();
+void audio_start(); // Starts the audio device callback
+void audio_render_silent(float duration_sec); // Fast-forwards audio state
void audio_update();
void audio_shutdown();
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc
index cfef7f9..040b523 100644
--- a/src/gpu/effect.cc
+++ b/src/gpu/effect.cc
@@ -195,6 +195,44 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
wgpuTextureRelease(surface_texture.texture);
}
+void MainSequence::simulate_until(float target_time, float step_rate) {
+#ifndef STRIP_ALL
+ // Assuming 128 BPM as per main.cc.
+ // Ideally this should be passed in or shared.
+ const float bpm = 128.0f;
+ const float aspect_ratio = 16.0f / 9.0f; // Dummy aspect
+
+ for (float t = 0.0f; t < target_time; t += step_rate) {
+ WGPUCommandEncoderDescriptor encoder_desc = {};
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &encoder_desc);
+
+ float beat = fmodf(t * bpm / 60.0f, 1.0f);
+
+ // Update active lists
+ for (auto &entry : sequences_) {
+ if (t >= entry.start_time) {
+ entry.seq->update_active_list(t - entry.start_time);
+ }
+ }
+
+ // Dispatch compute
+ for (auto &entry : sequences_) {
+ if (t >= entry.start_time) {
+ // peak = 0.0f during simulation (no audio analysis)
+ entry.seq->dispatch_compute(encoder, t - entry.start_time, beat, 0.0f, aspect_ratio);
+ }
+ }
+
+ WGPUCommandBufferDescriptor cmd_desc = {};
+ WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, &cmd_desc);
+ wgpuQueueSubmit(queue, 1, &commands);
+ }
+#else
+ (void)target_time;
+ (void)step_rate;
+#endif
+}
+
void MainSequence::shutdown() {
for (auto &entry : sequences_) {
entry.seq->reset();
diff --git a/src/gpu/effect.h b/src/gpu/effect.h
index 50b2d72..f92e3ef 100644
--- a/src/gpu/effect.h
+++ b/src/gpu/effect.h
@@ -105,6 +105,10 @@ public:
void render_frame(float global_time, float beat, float peak,
float aspect_ratio, WGPUSurface surface);
+ // Fast-forwards the simulation (updates & compute) without rendering.
+ // Used for seeking/debugging.
+ void simulate_until(float target_time, float step_rate);
+
void shutdown();
private:
diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc
index 0655bb3..e68b84d 100644
--- a/src/gpu/gpu.cc
+++ b/src/gpu/gpu.cc
@@ -425,6 +425,10 @@ void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat) {
g_main_sequence.render_frame(time, beat, audio_peak, aspect_ratio, g_surface);
}
+void gpu_simulate_until(float time) {
+ g_main_sequence.simulate_until(time, 1.0f / 60.0f);
+}
+
void gpu_shutdown() {
g_main_sequence.shutdown();
} \ No newline at end of file
diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h
index f9c322c..1d79781 100644
--- a/src/gpu/gpu.h
+++ b/src/gpu/gpu.h
@@ -33,6 +33,7 @@ struct RenderPass {
void gpu_init(GLFWwindow *window);
void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat);
+void gpu_simulate_until(float time);
void gpu_shutdown();
// Helper functions (exposed for internal/future use)
diff --git a/src/main.cc b/src/main.cc
index fb0b412..75143c0 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -130,12 +130,15 @@ float* generate_tone(float *buffer, float freq) {
int main(int argc, char **argv) {
bool fullscreen_enabled = false;
+ double seek_time = 0.0;
#ifndef STRIP_ALL
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--fullscreen") == 0) {
fullscreen_enabled = true;
- break;
+ } else if (strcmp(argv[i], "--seek") == 0 && i + 1 < argc) {
+ seek_time = atof(argv[i + 1]);
+ i++;
}
}
#else
@@ -166,12 +169,9 @@ int main(int argc, char **argv) {
double last_beat_time = 0.0;
int beat_count = 0;
- while (!platform_should_close()) {
- platform_poll();
-
- double current_time = platform_get_time();
- if (current_time - last_beat_time > SECONDS_PER_BEAT / 2.0) { // 8th notes
- last_beat_time = current_time;
+ auto update_game_logic = [&](double t) {
+ if (t - last_beat_time > SECONDS_PER_BEAT / 2.0) { // 8th notes
+ last_beat_time = t; // Sync to t
const int step = beat_count % 16;
@@ -203,6 +203,34 @@ int main(int argc, char **argv) {
++beat_count;
}
+ };
+
+#ifndef STRIP_ALL
+ if (seek_time > 0.0) {
+ printf("Seeking to %.2f seconds...\n", seek_time);
+
+ // Simulate audio/game logic
+ // We step at ~60hz
+ const double step = 1.0/60.0;
+ for (double t = 0.0; t < seek_time; t += step) {
+ update_game_logic(t);
+ audio_render_silent((float)step);
+ }
+
+ // Simulate Visuals
+ gpu_simulate_until((float)seek_time);
+ }
+#endif
+
+ // Start real audio
+ audio_start();
+
+ while (!platform_should_close()) {
+ platform_poll();
+
+ double current_time = platform_get_time() + seek_time; // Offset logic time
+
+ update_game_logic(current_time);
int width, height;
glfwGetFramebufferSize(platform_get_window(), &width, &height);
@@ -221,4 +249,4 @@ int main(int argc, char **argv) {
gpu_shutdown();
platform_shutdown();
return 0;
-}
+} \ No newline at end of file
diff --git a/tools/spectool.cc b/tools/spectool.cc
index d9b58be..663056d 100644
--- a/tools/spectool.cc
+++ b/tools/spectool.cc
@@ -133,6 +133,7 @@ int play_spec(const char *in_path) {
platform_init_window(false);
audio_init();
+ audio_start();
Spectrogram spec;
spec.spectral_data_a = spec_data.data();
spec.spectral_data_b =