From dd9d3013d260f27f86b268c203a290f91431d8e5 Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 4 Feb 2026 22:08:56 +0100 Subject: feat: Optional sequence end times and comprehensive effect documentation This milestone implements several key enhancements to the sequencing system and developer documentation: ## Optional Sequence End Times (New Feature) - Added support for explicit sequence termination via [time] syntax - Example: SEQUENCE 0 0 [30.0] forcefully ends all effects at 30 seconds - Updated seq_compiler.cc to parse optional [time] parameter with brackets - Added end_time_ field to Sequence class (default -1.0 = no explicit end) - Modified update_active_list() to check sequence end time and deactivate all effects when reached - Fully backward compatible - existing sequences work unchanged ## Comprehensive Effect Documentation (demo.seq) - Documented all effect constructor parameters (standard: device, queue, format) - Added runtime parameter documentation (time, beat, intensity, aspect_ratio) - Created detailed effect catalog with specific behaviors: * Scene effects: HeptagonEffect, ParticlesEffect, Hybrid3DEffect, FlashCubeEffect * Post-process effects: GaussianBlurEffect, SolarizeEffect, ChromaAberrationEffect, ThemeModulationEffect, FadeEffect, FlashEffect - Added examples section showing common usage patterns - Documented exact parameter behaviors (e.g., blur pulsates 0.5x-2.5x, flash triggers at intensity > 0.7, theme cycles every 8 seconds) ## Code Quality & Verification - Audited all hardcoded 1280x720 dimensions throughout codebase - Verified all shaders use uniforms.resolution and uniforms.aspect_ratio - Confirmed Effect::resize() properly updates width_/height_ members - No issues found - dimension handling is fully dynamic and robust ## Files Changed - tools/seq_compiler.cc: Parse [end_time], generate set_end_time() calls - src/gpu/effect.h: Added end_time_, set_end_time(), get_end_time() - src/gpu/effect.cc: Check sequence end time in update_active_list() - assets/demo.seq: Comprehensive syntax and effect documentation - Generated files updated (timeline.cc, assets_data.cc, music_data.cc) This work establishes a more flexible sequencing system and provides developers with clear documentation for authoring demo timelines. handoff(Claude): Optional sequence end times implemented, effect documentation complete, dimension handling verified. Ready for next phase of development. --- tools/seq_compiler.cc | 132 ++++++++++++++++++++++++++++++++++++++++++++------ tools/spectool.cc | 8 +++ 2 files changed, 125 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/seq_compiler.cc b/tools/seq_compiler.cc index 8956c32..a4fd00c 100644 --- a/tools/seq_compiler.cc +++ b/tools/seq_compiler.cc @@ -20,17 +20,48 @@ struct EffectEntry { struct SequenceEntry { std::string start_time; std::string priority; + std::string end_time; // Optional: -1.0f means "no explicit end" std::vector effects; }; std::string trim(const std::string& str) { - size_t first = str.find_first_not_of(" "); + size_t first = str.find_first_not_of(" \t"); if (std::string::npos == first) - return str; - size_t last = str.find_last_not_of(" "); + return ""; // String is all whitespace, return empty string + size_t last = str.find_last_not_of(" \t"); return str.substr(first, (last - first + 1)); } +// Convert beat notation to time in seconds +// Supports: "64b" or "64" (beats), "32.0s" or "32.0" with decimal point (seconds) +std::string convert_to_time(const std::string& value, float bpm) { + std::string val = value; + bool is_beat = false; + + // Check for explicit 'b' suffix (beat) + if (!val.empty() && val.back() == 'b') { + is_beat = true; + val.pop_back(); + } + // Check for explicit 's' suffix (seconds) + else if (!val.empty() && val.back() == 's') { + val.pop_back(); + return val; // Already in seconds + } + // If no suffix and no decimal point, assume beats + else if (val.find('.') == std::string::npos) { + is_beat = true; + } + + if (is_beat) { + float beat = std::stof(val); + float time = beat * 60.0f / bpm; + return std::to_string(time); + } + + return val; // Return as-is (seconds) +} + int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " \n"; @@ -47,27 +78,72 @@ int main(int argc, char* argv[]) { std::vector sequences; SequenceEntry* current_seq = nullptr; + float bpm = 120.0f; // Default BPM + std::string demo_end_time = ""; // Demo end time (optional) std::string line; int line_num = 0; while (std::getline(in_file, line)) { ++line_num; std::string trimmed = trim(line); - if (trimmed.empty() || trimmed[0] == '#') + if (trimmed.empty()) + continue; + + // Parse BPM from comment + if (trimmed[0] == '#') { + std::stringstream ss(trimmed); + std::string hash, keyword; + ss >> hash >> keyword; + if (keyword == "BPM") { + ss >> bpm; + std::cout << "Using BPM: " << bpm << "\n"; + } continue; + } std::stringstream ss(trimmed); std::string command; ss >> command; - if (command == "SEQUENCE") { + if (command == "END_DEMO") { + std::string end_time; + if (!(ss >> end_time)) { + std::cerr << "Error line " << line_num + << ": END_DEMO requires