// This file is part of the 64k demo project. // It serves as the application entry point. // Orchestrates platform initialization, main loop, and subsystem coordination. #include "3d/renderer.h" #include "audio/audio.h" #include "audio/gen.h" #include "audio/synth.h" #include "audio/tracker.h" #include "generated/assets.h" // Include generated asset header #include "gpu/gpu.h" #include "platform.h" #include "util/math.h" #include #include #include #include #include #define SPEC_FRAMES 16 struct SpecHeader { char magic[4]; int32_t version; int32_t dct_size; int32_t num_frames; }; int register_spec_asset(AssetId id) { size_t size; const uint8_t* data = GetAsset(id, &size); if (!data || size < sizeof(SpecHeader)) return -1; const SpecHeader* header = (const SpecHeader*)data; const float* spectral_data = (const float*)(data + sizeof(SpecHeader)); Spectrogram spec; spec.spectral_data_a = spectral_data; spec.spectral_data_b = spectral_data; // No double-buffer for static assets spec.num_frames = header->num_frames; return synth_register_spectrogram(&spec); } static float* g_spec_buffer_a[SPEC_FRAMES * DCT_SIZE] = {0}; static float* g_spec_buffer_b[SPEC_FRAMES * DCT_SIZE] = {0}; // Global storage for the melody to ensure it persists // Global storage for the melody to ensure it persists // std::vector g_melody_data; // Tracker now handles melody generation // int generate_melody() { ... } // Replaced by tracker float* generate_tone(float* buffer, float freq) { if (buffer == nullptr) { buffer = (float*)calloc(SPEC_FRAMES * DCT_SIZE, sizeof(float)); } else { memset(buffer, 0, SPEC_FRAMES * DCT_SIZE * sizeof(float)); } for (int frame = 0; frame < SPEC_FRAMES; ++frame) { float* spec_frame = buffer + frame * DCT_SIZE; float amplitude = 1000. * powf(1.0f - (float)frame / SPEC_FRAMES, 2.0f); int bin = (int)(freq / (32000.0f / 2.0f) * DCT_SIZE); if (bin > 0 && bin < DCT_SIZE) { spec_frame[bin] = amplitude; } } return buffer; } int main(int argc, char** argv) { PlatformState platform_state = {}; bool fullscreen_enabled = false; float seek_time = 0.0f; int* width_ptr = nullptr; int* height_ptr = nullptr; int custom_width, custom_height; #if !defined(STRIP_ALL) for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "--fullscreen") == 0) { fullscreen_enabled = true; } else if (strcmp(argv[i], "--seek") == 0 && i + 1 < argc) { seek_time = atof(argv[i + 1]); ++i; } else if (strcmp(argv[i], "--resolution") == 0 && i + 1 < argc) { const char* res_str = argv[++i]; if (sscanf(res_str, "%dx%d", &custom_width, &custom_height) == 2) { width_ptr = &custom_width; height_ptr = &custom_height; } } else if (strcmp(argv[i], "--debug") == 0) { Renderer3D::SetDebugEnabled(true); } } #else (void)argc; (void)argv; fullscreen_enabled = true; #endif /* STRIP_ALL */ platform_init(&platform_state, fullscreen_enabled, width_ptr, height_ptr); gpu_init(&platform_state); audio_init(); synth_init(); tracker_init(); int kick_id = register_spec_asset(AssetId::ASSET_KICK_1); int snare_id = register_spec_asset(AssetId::ASSET_SNARE_1); int hihat_id = register_spec_asset(AssetId::ASSET_HIHAT_1); // Still keep the dynamic tone for bass (can be integrated into tracker too) const float* g_spec_buffer_a = generate_tone(nullptr, 110.0f); // A2 const float* g_spec_buffer_b = generate_tone(nullptr, 110.0f); const Spectrogram bass_spec = {g_spec_buffer_a, g_spec_buffer_b, SPEC_FRAMES}; int bass_id = synth_register_spectrogram(&bass_spec); // Generate and play melody (replaced by tracker) // int melody_id = generate_melody(); // synth_trigger_voice(melody_id, 0.6f, 0.0f); double last_beat_time = 0.0; int beat_count = 0; auto update_game_logic = [&](double t) { if (t - last_beat_time > (60.0f / g_tracker_score.bpm) / 2.0) { // 8th notes last_beat_time = t; // Sync to t const int step = beat_count % 16; // Kick on 1, 9, 11, 14... if (step == 0 || step == 8 || step == 10 || step == 13) { synth_trigger_voice(kick_id, 0.6f, 0.0f); } // Snare on 4, 12 if (step == 4 || step == 12) { synth_trigger_voice(snare_id, 0.48f, step & 8 ? -1.0f : 1.0f); } // Hihat on every offbeat if (step % 2 == 1) { synth_trigger_voice(hihat_id, 0.3f, 0.3f); } // Bass pattern if (step % 4 == 0) { float* back_buffer = synth_begin_update(bass_id); if (back_buffer) { float bass_freq = (step < 8) ? 110.0f : 164.82f; // A3 then E3 generate_tone(back_buffer, bass_freq); synth_commit_update(bass_id); } synth_trigger_voice(bass_id, 0.9f, 1.2f); } ++beat_count; } tracker_update((float)t); }; #if !defined(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 /* !defined(STRIP_ALL) */ // Start real audio audio_start(); int last_width = platform_state.width; int last_height = platform_state.height; while (!platform_should_close(&platform_state)) { platform_poll(&platform_state); if (platform_state.width != last_width || platform_state.height != last_height) { last_width = platform_state.width; last_height = platform_state.height; gpu_resize(last_width, last_height); } double current_time = platform_get_time() + seek_time; // Offset logic time update_game_logic(current_time); float aspect_ratio = platform_get_aspect_ratio(&platform_state); // Adjusted multiplier for visuals (preventing constant 1.0 saturation) float raw_peak = synth_get_output_peak(); float visual_peak = fminf(raw_peak * 8.0f, 1.0f); // float beat = fmodf((float)current_time * DEMO_BPM / 60.0f, 1.0f); // Use // tracker BPM float beat = fmodf((float)current_time * g_tracker_score.bpm / 60.0f, 1.0f); gpu_draw(visual_peak, aspect_ratio, (float)current_time, beat); audio_update(); } audio_shutdown(); gpu_shutdown(); platform_shutdown(&platform_state); return 0; }