diff options
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | CLAUDE.md | 2 | ||||
| -rw-r--r-- | GEMINI.md | 55 | ||||
| -rw-r--r-- | PROJECT_CONTEXT.md | 4 | ||||
| -rw-r--r-- | TODO.md | 2 | ||||
| -rw-r--r-- | cmake/DemoCodegen.cmake | 150 | ||||
| -rw-r--r-- | cmake/DemoCommon.cmake | 31 | ||||
| -rw-r--r-- | cmake/DemoLibraries.cmake | 8 | ||||
| -rw-r--r-- | cmake/DemoSourceLists.cmake | 51 | ||||
| -rw-r--r-- | cmake/DemoTests.cmake | 80 | ||||
| -rw-r--r-- | cnn_v1/README.md | 64 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN.md (renamed from doc/CNN.md) | 2 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_BIAS_FIX_2026-02.md (renamed from doc/CNN_BIAS_FIX_2026-02.md) | 0 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_DEBUG.md (renamed from doc/CNN_DEBUG.md) | 0 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_FLATTEN_ANALYSIS.md (renamed from doc/CNN_FLATTEN_ANALYSIS.md) | 6 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_RGBD_GRAYSCALE_SUMMARY.md (renamed from doc/CNN_RGBD_GRAYSCALE_SUMMARY.md) | 0 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_TEST_TOOL.md (renamed from doc/CNN_TEST_TOOL.md) | 0 | ||||
| -rw-r--r-- | cnn_v1/docs/CNN_V1_EFFECT.md (renamed from doc/CNN_EFFECT.md) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_activation.wgsl (renamed from workspaces/main/shaders/cnn/cnn_activation.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_conv1x1.wgsl (renamed from workspaces/main/shaders/cnn/cnn_conv1x1.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_conv3x3.wgsl (renamed from workspaces/main/shaders/cnn/cnn_conv3x3.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_conv5x5.wgsl (renamed from workspaces/main/shaders/cnn/cnn_conv5x5.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_conv7x7.wgsl (renamed from workspaces/main/shaders/cnn/cnn_conv7x7.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_layer.wgsl (renamed from workspaces/main/shaders/cnn/cnn_layer.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/shaders/cnn_weights_generated.wgsl (renamed from workspaces/main/shaders/cnn/cnn_weights_generated.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v1/src/cnn_v1_effect.cc (renamed from src/effects/cnn_effect.cc) | 18 | ||||
| -rw-r--r-- | cnn_v1/src/cnn_v1_effect.h (renamed from src/effects/cnn_effect.h) | 14 | ||||
| -rwxr-xr-x | cnn_v1/training/train_cnn.py (renamed from training/train_cnn.py) | 0 | ||||
| -rw-r--r-- | cnn_v2/README.md | 60 | ||||
| -rw-r--r-- | cnn_v2/docs/CNN_V2.md (renamed from doc/CNN_V2.md) | 0 | ||||
| -rw-r--r-- | cnn_v2/docs/CNN_V2_BINARY_FORMAT.md (renamed from doc/CNN_V2_BINARY_FORMAT.md) | 0 | ||||
| -rw-r--r-- | cnn_v2/docs/CNN_V2_DEBUG_TOOLS.md (renamed from doc/CNN_V2_DEBUG_TOOLS.md) | 0 | ||||
| -rw-r--r-- | cnn_v2/docs/CNN_V2_WEB_TOOL.md (renamed from doc/CNN_V2_WEB_TOOL.md) | 0 | ||||
| -rwxr-xr-x | cnn_v2/scripts/train_cnn_v2_full.sh (renamed from scripts/train_cnn_v2_full.sh) | 10 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_compute.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_compute.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_layer_0.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_layer_0.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_layer_1.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_layer_1.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_layer_2.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_layer_2.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_layer_template.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_layer_template.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/shaders/cnn_v2_static.wgsl (renamed from workspaces/main/shaders/cnn_v2/cnn_v2_static.wgsl) | 0 | ||||
| -rw-r--r-- | cnn_v2/src/cnn_v2_effect.cc (renamed from src/effects/cnn_v2_effect.cc) | 2 | ||||
| -rw-r--r-- | cnn_v2/src/cnn_v2_effect.h (renamed from src/effects/cnn_v2_effect.h) | 0 | ||||
| -rw-r--r-- | cnn_v2/tools/cnn_v2_test/README.md (renamed from tools/cnn_v2_test/README.md) | 0 | ||||
| -rw-r--r-- | cnn_v2/tools/cnn_v2_test/index.html (renamed from tools/cnn_v2_test/index.html) | 39 | ||||
| -rwxr-xr-x | cnn_v2/training/export_cnn_v2_shader.py (renamed from training/export_cnn_v2_shader.py) | 6 | ||||
| -rwxr-xr-x | cnn_v2/training/export_cnn_v2_weights.py (renamed from training/export_cnn_v2_weights.py) | 8 | ||||
| -rwxr-xr-x | cnn_v2/training/gen_identity_weights.py (renamed from training/gen_identity_weights.py) | 6 | ||||
| -rwxr-xr-x | cnn_v2/training/train_cnn_v2.py (renamed from training/train_cnn_v2.py) | 0 | ||||
| -rw-r--r-- | cnn_v3/README.md | 36 | ||||
| -rw-r--r-- | cnn_v3/training/input/photo1.jpg | bin | 0 -> 5813575 bytes | |||
| -rw-r--r-- | cnn_v3/training/input/photo2.jpg | bin | 0 -> 3328126 bytes | |||
| -rw-r--r-- | cnn_v3/training/input/photo3.jpg | bin | 0 -> 723541 bytes | |||
| -rw-r--r-- | cnn_v3/training/input/photo4.jpg | bin | 0 -> 286046 bytes | |||
| -rw-r--r-- | cnn_v3/training/target_1/photo1_out.png | bin | 0 -> 4038843 bytes | |||
| -rw-r--r-- | cnn_v3/training/target_1/photo2_1_out.png | bin | 0 -> 2875939 bytes | |||
| -rw-r--r-- | cnn_v3/training/target_1/photo2_2_out.png | bin | 0 -> 2865857 bytes | |||
| -rw-r--r-- | cnn_v3/training/target_1/photo4_out.png | bin | 0 -> 718912 bytes | |||
| -rw-r--r-- | doc/AUDIO_WAV_DRIFT_BUG.md | 17 | ||||
| -rw-r--r-- | doc/AUXILIARY_TEXTURE_INIT.md | 2 | ||||
| -rw-r--r-- | doc/BUILD.md | 8 | ||||
| -rw-r--r-- | doc/CMAKE_MODULES.md | 22 | ||||
| -rw-r--r-- | doc/COMPLETED.md | 6 | ||||
| -rw-r--r-- | doc/HOWTO.md | 44 | ||||
| -rw-r--r-- | src/app/main.cc | 43 | ||||
| -rw-r--r-- | src/app/test_demo.cc | 4 | ||||
| -rw-r--r-- | src/audio/audio.cc | 34 | ||||
| -rw-r--r-- | src/audio/tracker.cc | 19 | ||||
| -rw-r--r-- | src/effects/flash_cube_effect.cc | 6 | ||||
| -rw-r--r-- | src/effects/gaussian_blur_effect.h | 4 | ||||
| -rw-r--r-- | src/effects/particle_spray_effect.h | 2 | ||||
| -rw-r--r-- | src/effects/particles_effect.h | 2 | ||||
| -rw-r--r-- | src/effects/sdf_test_effect.cc | 4 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 8 | ||||
| -rw-r--r-- | src/gpu/gpu.cc | 1 | ||||
| -rw-r--r-- | src/tests/audio/test_audio_engine.cc | 8 | ||||
| -rw-r--r-- | src/tests/gpu/test_demo_effects.cc | 4 | ||||
| -rw-r--r-- | src/tests/gpu/test_shader_assets.cc | 4 | ||||
| -rw-r--r-- | tools/cnn_test.cc | 2 | ||||
| -rw-r--r-- | tools/common/style.css | 117 | ||||
| -rw-r--r-- | tools/shader_editor/index.html | 42 | ||||
| -rw-r--r-- | tools/spectral_editor/index.html | 1 | ||||
| -rw-r--r-- | tools/spectral_editor/style.css | 106 | ||||
| -rw-r--r-- | tools/timeline_editor/index.html | 528 | ||||
| -rw-r--r-- | tools/timeline_editor/timeline-playback.js | 2 | ||||
| -rw-r--r-- | tools/timeline_editor/timeline-viewport.js | 10 | ||||
| -rw-r--r-- | tools/track_visualizer/index.html | 21 | ||||
| -rw-r--r-- | training/README.md | 2 | ||||
| -rw-r--r-- | workspaces/main/assets.txt | 20 | ||||
| -rw-r--r-- | workspaces/main/timeline.seq | 137 |
89 files changed, 1278 insertions, 613 deletions
@@ -70,3 +70,10 @@ Testing/ training/checkpoints/ checkpoints/ validation_results/ +**/assets_filtered_*.txt +**/assets_assets_*.txt + +# Timeline editor temp files +test_*.seq +*_save.seq +tools/timeline_editor/timeline.seq @@ -12,7 +12,7 @@ # TIER 3: DESIGN DOCS (Load On-Demand by Subsystem) # # Audio: SPECTRAL_BRUSH_EDITOR.md, TRACKER.md, BEAT_TIMING.md -# CNN: CNN.md, CNN_EFFECT.md, CNN_V2*.md, CNN_TEST_TOOL.md, CNN_*ANALYSIS.md, CNN_BIAS_FIX*.md +# CNN: cnn_v1/docs/CNN_V1_EFFECT.md, cnn_v2/docs/CNN_V2*.md # 3D/Graphics: 3D.md, GPU_PROCEDURAL_PHASE4.md, MASKING_SYSTEM.md, SDF_EFFECT_GUIDE.md # Scene: SCENE_FORMAT.md, SEQUENCE.md, WORKSPACE_SYSTEM.md # Build: ASSET_SYSTEM.md, BUILD.md, CMAKE_MODULES.md, SIZE_MEASUREMENT.md @@ -84,48 +84,19 @@ IMPORTANT: - Keep PROJECT_CONTEXT.md focused on current status - Keep TODO.md focused on active/next tasks only -# ============================================ -# CURRENT STATE SNAPSHOT (Gemini-Specific) -# ============================================ -<state_snapshot> - <overall_goal> - Produce a cross-platform (Windows, macOS, Linux) 64-kilobyte demoscene production. This is achieved through a C++ codebase utilizing WebGPU for graphics (with a hybrid SDF/rasterization pipeline) and a real-time procedural audio engine for sound, with a heavy focus on size-optimization at all stages. - </overall_goal> - - <active_constraints> - - All tests passing (36/36 - 100%). - - Strict 64k final binary size target. - - Adherence to project coding style and contribution guidelines is mandatory. - </active_constraints> - - <key_knowledge> - - **Workspace System:** The project is organized into self-contained workspaces (e.g., `workspaces/main`, `workspaces/test`) managed by a `workspace.cfg` file, separating demo-specific content from a `common/` directory that holds shared shaders and resources. Selection is done at build time with `-DDEMO_WORKSPACE=<name>`. - - **Build & Asset Pipeline:** A modular CMake system orchestrates the build. It uses host-native tools (`asset_packer`, `seq_compiler`, `tracker_compiler`) to parse manifest files (`assets.txt`, `timeline.seq`, `music.track`) and compile assets directly into the binary as C++ data, including procedural asset generation. - - **Audio Engine:** A real-time, sample-accurate audio engine with a tracker system for sequencing patterns from `.track` files. It features procedural synthesis from spectrograms (FFT-based IDCT), variable tempo that is decoupled from visual timing, and an abstracted backend for testing and offline rendering (`WavDumpBackend`). - - **Graphics & Rendering:** The renderer uses WebGPU (wgpu-native) and WGSL shaders. It employs a hybrid technique, rasterizing proxy geometry (cubes) and then performing SDF raymarching within the fragment shader. The 3D system supports BVH acceleration, and there's a pipeline for importing OBJ models. - - **Sequence & Timing:** Visuals are defined in `.seq` files using a beat-based timeline that is compiled into physical seconds. Shaders receive a `CommonPostProcessUniforms` buffer containing both physical time (`time`) for constant-speed animations and musical time (`beat_time`, `beat_phase`) for syncing with the audio playback clock. - - **CNN Post-Processing:** The project features a sophisticated CNN post-processing pipeline (CNNv2) for visual stylization. This includes a full Python/PyTorch training toolchain, a binary weight format, and a WebGPU-based validation tool. The network uses 7D parametric static features (RGBD, UV, sin, bias) for rich, position-aware effects. - - **Development Workflow:** There is a strong emphasis on tooling and process, including a visual timeline editor, audio analysis tools, a web-based CNN debugger, strict coding standards enforced by `clang-format`, and a comprehensive pre-commit script (`./scripts/check_all.sh`). - </key_knowledge> +# Role: Senior Systems Engineer (C++ Focus) - <artifact_trail> - - `GEMINI.md`: This file, synchronized with CLAUDE.md structure - - `PROJECT_CONTEXT.md`: Current system status - - `TODO.md`: Active priorities (Task #5 in progress) - </artifact_trail> +## Response Style +- **Extreme Brevity:** Provide direct answers. No "Sure, I can help," "I hope this helps," or "Here is the code." +- **Code-First:** Lead with the solution or the code block. +- **Explain on Demand:** Do not explain *how* the code works unless explicitly asked with "Why?" or "Explain." +- **No Markdown Fluff:** Avoid bolding every other word. Use standard technical formatting. - <recent_actions> - - **File Hierarchy Cleanup:** Major reorganization of the project structure, establishing the `workspaces/` and `common/` directories and eliminating ~100 redundant files (especially shaders). - - **CNNv2 Training Pipeline:** Fixed critical checkpointing bugs and streamlined the output of the training scripts for faster iteration. - - **Effect Render API Refactor:** Simplified the `Effect::render` API and fixed uniform initialization bugs across 19 effects. - - **CNN Shader Testing Tool:** Implemented `tools/cnn_test` for offline GPU-accelerated validation of trained CNN models. - - **Effect Source Hierarchy Cleanup**: Refactored the effects system by splitting `src/gpu/demo_effects.h` into individual header files for each effect, creating 10 new headers, moving class definitions, updating `.cc` files with new includes, fixing missing `#include` statements, creating a common `particle_defs.h`, and cleaning up `demo_effects.h`. Verified by passing all 34 tests. handoff(Gemini): - </recent_actions> +## Technical Preferences +- **C++ Standards:** Default to C++20/C++23 unless specified otherwise. +- **Style:** Prefer Modern C++ (RAII, templates, constexpr, STL) over C-style patterns. +- **Nomenclature:** Use standard engineering terminology (e.g., "O(n) complexity," "pointer aliasing," "cache miss") without defining the terms. - <task_state> - 1. [IN PROGRESS] Task #5: Spectral Brush Editor (Priority 1) - 2. [PENDING] Task #18: 3D System Enhancements (Priority 4) - 3. [RECURRENT] Task #50: WGSL Modularization (Priority 4) - 4. [PENDING] Tracker Humanization & Sample Offset (Priority 3) - </task_state> -</state_snapshot>
\ No newline at end of file +## Interaction Protocol +- If a query is ambiguous, provide the most likely technical solution rather than asking for clarification. +- Treat the user as a peer with expert-level knowledge. diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 63e6f8a..f59eb8b 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -57,9 +57,9 @@ See `TODO.md` for current priorities and active tasks. - `doc/CONTRIBUTING.md` - Development protocols **Technical Reference:** -- Core: `ASSET_SYSTEM.md`, `SEQUENCE.md`, `TRACKER.md`, `3D.md`, `CNN_EFFECT.md`, `CNN_V2.md` +- Core: `ASSET_SYSTEM.md`, `SEQUENCE.md`, `TRACKER.md`, `3D.md`, `cnn_v1/docs/CNN_V1_EFFECT.md`, `cnn_v2/docs/CNN_V2.md` - Formats: `SCENE_FORMAT.md`, `MASKING_SYSTEM.md` -- Tools: `BUILD.md`, `WORKSPACE_SYSTEM.md`, `SIZE_MEASUREMENT.md`, `CNN_TEST_TOOL.md`, `tools/timeline_editor/README.md` +- Tools: `BUILD.md`, `WORKSPACE_SYSTEM.md`, `SIZE_MEASUREMENT.md`, `cnn_v1/docs/CNN_TEST_TOOL.md`, `tools/timeline_editor/README.md` **History:** - `doc/COMPLETED.md` - Completed tasks archive @@ -28,7 +28,7 @@ Self-contained workspaces for parallel demo development. Enhanced CNN post-processing with multi-dimensional feature inputs. -**Design:** `doc/CNN_V2.md` +**Design:** `cnn_v2/docs/CNN_V2.md` **Status:** - ✅ Full implementation complete and validated diff --git a/cmake/DemoCodegen.cmake b/cmake/DemoCodegen.cmake index 272e424..d27e9eb 100644 --- a/cmake/DemoCodegen.cmake +++ b/cmake/DemoCodegen.cmake @@ -53,6 +53,48 @@ function(pack_assets NAME INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME) add_custom_target(${TARGET_NAME} DEPENDS ${OUT_H} ${OUT_CC}) endfunction() +# Pack assets by category (filters by filename pattern) +function(pack_assets_category NAME PATTERN INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME) + set(OUT_H ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/${NAME}.h) + set(OUT_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/${NAME}_data.cc) + + # Get directory of INPUT_TXT for relative path resolution + get_filename_component(INPUT_DIR ${INPUT_TXT} DIRECTORY) + get_filename_component(INPUT_NAME ${INPUT_TXT} NAME_WE) + set(FILTERED_TXT ${INPUT_DIR}/${INPUT_NAME}_${NAME}.txt) + + # Create filtered asset list + file(STRINGS ${INPUT_TXT} ALL_LINES) + set(FILTERED_LINES "") + foreach(LINE ${ALL_LINES}) + string(STRIP "${LINE}" LINE) + if(LINE MATCHES "^#" OR LINE STREQUAL "") + list(APPEND FILTERED_LINES "${LINE}") + else() + string(REGEX REPLACE "^[^,]+,[^,]+,[ ]*([^,]+).*" "\\1" FILENAME "${LINE}") + if(FILENAME MATCHES "${PATTERN}") + list(APPEND FILTERED_LINES "${LINE}") + endif() + endif() + endforeach() + string(REPLACE ";" "\n" FILTERED_CONTENT "${FILTERED_LINES}") + file(WRITE ${FILTERED_TXT} "${FILTERED_CONTENT}\n") + + # Parse filtered list for dependencies (now in same directory as INPUT_TXT) + parse_asset_list(${FILTERED_TXT} ASSET_FILE_DEPS) + + add_custom_command( + OUTPUT ${OUT_H} ${OUT_CC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated + COMMAND ${ASSET_PACKER_CMD} ${FILTERED_TXT} ${OUT_H} ${OUT_CC} + DEPENDS ${ASSET_PACKER_DEPENDS} ${INPUT_TXT} ${ASSET_FILE_DEPS} + COMMENT "Generating ${NAME} assets..." + ) + set(${HEADER_VAR} ${OUT_H} PARENT_SCOPE) + set(${DATA_CC_VAR} ${OUT_CC} PARENT_SCOPE) + add_custom_target(${TARGET_NAME} DEPENDS ${OUT_H} ${OUT_CC}) +endfunction() + # Pack test assets (output to build/src/generated_test/) function(pack_test_assets NAME INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME) set(OUT_H ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test/${NAME}.h) @@ -102,8 +144,112 @@ add_custom_command( ) add_custom_target(generate_tracker_music ALL DEPENDS ${GENERATED_MUSIC_DATA_CC}) -# Asset packing -pack_assets(assets ${WORKSPACE_ASSETS} GEN_DEMO_H GEN_DEMO_CC generate_demo_assets) +# Asset packing (split by category for granular rebuild tracking) +# Generate filtered asset files per category +get_filename_component(ASSETS_DIR ${WORKSPACE_ASSETS} DIRECTORY) +set(FILTERED_SHADERS ${ASSETS_DIR}/assets_filtered_shaders.txt) +set(FILTERED_AUDIO ${ASSETS_DIR}/assets_filtered_audio.txt) +set(FILTERED_MODELS ${ASSETS_DIR}/assets_filtered_models.txt) +set(FILTERED_DATA ${ASSETS_DIR}/assets_filtered_data.txt) + +# Filter assets by category at configure time +file(STRINGS ${WORKSPACE_ASSETS} ALL_ASSET_LINES) +set(SHADERS_LINES "") +set(AUDIO_LINES "") +set(MODELS_LINES "") +set(DATA_LINES "") + +foreach(LINE ${ALL_ASSET_LINES}) + string(STRIP "${LINE}" LINE) + if(LINE MATCHES "^#" OR LINE STREQUAL "") + list(APPEND SHADERS_LINES "${LINE}") + list(APPEND AUDIO_LINES "${LINE}") + list(APPEND MODELS_LINES "${LINE}") + list(APPEND DATA_LINES "${LINE}") + else() + string(REGEX REPLACE "^[^,]+,[^,]+,[ ]*([^,]+).*" "\\1" FILENAME "${LINE}") + if(FILENAME MATCHES "\\.wgsl$") + list(APPEND SHADERS_LINES "${LINE}") + elseif(FILENAME MATCHES "\\.(spec|track)$") + list(APPEND AUDIO_LINES "${LINE}") + elseif(FILENAME MATCHES "\\.obj$") + list(APPEND MODELS_LINES "${LINE}") + elseif(FILENAME MATCHES "\\.(bin|png)$" OR FILENAME MATCHES "PROC\\(") + list(APPEND DATA_LINES "${LINE}") + endif() + endif() +endforeach() + +string(REPLACE ";" "\n" SHADERS_CONTENT "${SHADERS_LINES}") +string(REPLACE ";" "\n" AUDIO_CONTENT "${AUDIO_LINES}") +string(REPLACE ";" "\n" MODELS_CONTENT "${MODELS_LINES}") +string(REPLACE ";" "\n" DATA_CONTENT "${DATA_LINES}") + +file(WRITE ${FILTERED_SHADERS} "${SHADERS_CONTENT}\n") +file(WRITE ${FILTERED_AUDIO} "${AUDIO_CONTENT}\n") +file(WRITE ${FILTERED_MODELS} "${MODELS_CONTENT}\n") +file(WRITE ${FILTERED_DATA} "${DATA_CONTENT}\n") + +# Parse dependencies per category +parse_asset_list(${FILTERED_SHADERS} SHADERS_DEPS) +parse_asset_list(${FILTERED_AUDIO} AUDIO_DEPS) +parse_asset_list(${FILTERED_MODELS} MODELS_DEPS) +parse_asset_list(${FILTERED_DATA} DATA_DEPS) + +# Single unified output (avoids duplicate symbols) +set(GEN_DEMO_H ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/assets.h) +set(GEN_DEMO_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/assets_data.cc) + +# Category-specific targets for granular rebuilds +add_custom_command( + OUTPUT ${GEN_DEMO_H}.shaders_stamp + COMMAND ${CMAKE_COMMAND} -E touch ${GEN_DEMO_H}.shaders_stamp + DEPENDS ${ASSET_PACKER_DEPENDS} ${WORKSPACE_ASSETS} ${SHADERS_DEPS} + COMMENT "Checking shader assets..." +) +add_custom_target(generate_demo_shaders DEPENDS ${GEN_DEMO_H}.shaders_stamp) + +add_custom_command( + OUTPUT ${GEN_DEMO_H}.audio_stamp + COMMAND ${CMAKE_COMMAND} -E touch ${GEN_DEMO_H}.audio_stamp + DEPENDS ${ASSET_PACKER_DEPENDS} ${WORKSPACE_ASSETS} ${AUDIO_DEPS} + COMMENT "Checking audio assets..." +) +add_custom_target(generate_demo_audio DEPENDS ${GEN_DEMO_H}.audio_stamp) + +add_custom_command( + OUTPUT ${GEN_DEMO_H}.models_stamp + COMMAND ${CMAKE_COMMAND} -E touch ${GEN_DEMO_H}.models_stamp + DEPENDS ${ASSET_PACKER_DEPENDS} ${WORKSPACE_ASSETS} ${MODELS_DEPS} + COMMENT "Checking model assets..." +) +add_custom_target(generate_demo_models DEPENDS ${GEN_DEMO_H}.models_stamp) + +add_custom_command( + OUTPUT ${GEN_DEMO_H}.data_stamp + COMMAND ${CMAKE_COMMAND} -E touch ${GEN_DEMO_H}.data_stamp + DEPENDS ${ASSET_PACKER_DEPENDS} ${WORKSPACE_ASSETS} ${DATA_DEPS} + COMMENT "Checking data assets..." +) +add_custom_target(generate_demo_data DEPENDS ${GEN_DEMO_H}.data_stamp) + +# Unified asset generation (triggered when any category changes) +add_custom_command( + OUTPUT ${GEN_DEMO_H} ${GEN_DEMO_CC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated + COMMAND ${ASSET_PACKER_CMD} ${WORKSPACE_ASSETS} ${GEN_DEMO_H} ${GEN_DEMO_CC} + DEPENDS ${ASSET_PACKER_DEPENDS} ${WORKSPACE_ASSETS} + ${GEN_DEMO_H}.shaders_stamp + ${GEN_DEMO_H}.audio_stamp + ${GEN_DEMO_H}.models_stamp + ${GEN_DEMO_H}.data_stamp + COMMENT "Generating unified assets..." +) + +# Combined target for compatibility +add_custom_target(generate_demo_assets DEPENDS ${GEN_DEMO_H} ${GEN_DEMO_CC}) + +# Test assets (unchanged) pack_test_assets(test_assets ${CMAKE_CURRENT_SOURCE_DIR}/workspaces/test/assets.txt GEN_TEST_H GEN_TEST_CC generate_test_assets) # Mark generated files so CMake always checks if they need rebuilding diff --git a/cmake/DemoCommon.cmake b/cmake/DemoCommon.cmake index 1c63b26..401ea05 100644 --- a/cmake/DemoCommon.cmake +++ b/cmake/DemoCommon.cmake @@ -197,3 +197,34 @@ macro(add_demo_test NAME TEST_NAME LABEL) add_test(NAME ${TEST_NAME} COMMAND ${NAME}) set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${LABEL}") endmacro() + +# ============================================================================= +# demo_add_asset_deps(TARGET CATEGORY) +# ============================================================================= +# Adds asset category dependencies to a target +# +# Arguments: +# TARGET - CMake target name +# CATEGORY - Asset category (shaders, audio, models, data, all, test) +# +# Usage: +# demo_add_asset_deps(test_synth audio) +# demo_add_asset_deps(test_shader_compilation shaders) +# demo_add_asset_deps(demo64k all) +function(demo_add_asset_deps TARGET CATEGORY) + if(CATEGORY STREQUAL "all") + add_dependencies(${TARGET} generate_demo_shaders generate_demo_audio generate_demo_models generate_demo_data) + elseif(CATEGORY STREQUAL "test") + add_dependencies(${TARGET} generate_test_assets) + elseif(CATEGORY STREQUAL "shaders") + add_dependencies(${TARGET} generate_demo_shaders) + elseif(CATEGORY STREQUAL "audio") + add_dependencies(${TARGET} generate_demo_audio) + elseif(CATEGORY STREQUAL "models") + add_dependencies(${TARGET} generate_demo_models) + elseif(CATEGORY STREQUAL "data") + add_dependencies(${TARGET} generate_demo_data) + else() + message(FATAL_ERROR "Unknown asset category: ${CATEGORY}") + endif() +endfunction() diff --git a/cmake/DemoLibraries.cmake b/cmake/DemoLibraries.cmake index 3a2207a..f1891fb 100644 --- a/cmake/DemoLibraries.cmake +++ b/cmake/DemoLibraries.cmake @@ -3,8 +3,8 @@ # Utility library add_library(util STATIC ${UTIL_SOURCES}) -add_dependencies(util generate_demo_assets generate_test_assets) target_include_directories(util PUBLIC ${CORE_INCLUDES}) +add_dependencies(util generate_demo_assets) # Procedural generation library add_library(procedural STATIC ${PROCEDURAL_SOURCES}) @@ -12,18 +12,18 @@ target_include_directories(procedural PUBLIC ${CORE_INCLUDES}) # Audio synthesis and processing library add_library(audio STATIC ${AUDIO_SOURCES}) -add_dependencies(audio generate_demo_assets) target_include_directories(audio PUBLIC ${CORE_INCLUDES}) +add_dependencies(audio generate_demo_assets) # 3D rendering library add_library(3d STATIC ${3D_SOURCES}) -add_dependencies(3d generate_demo_assets) target_include_directories(3d PUBLIC ${CORE_INCLUDES}) +add_dependencies(3d generate_demo_assets) # GPU effects library add_library(gpu STATIC ${GPU_SOURCES}) -add_dependencies(gpu generate_demo_assets) target_include_directories(gpu PUBLIC ${CORE_INCLUDES}) +add_dependencies(gpu generate_demo_assets) # Note: Static libraries do not strictly need to link dependencies, # but if they did, PRIVATE would propagate to the executable. diff --git a/cmake/DemoSourceLists.cmake b/cmake/DemoSourceLists.cmake index d4cdd21..2d6cf42 100644 --- a/cmake/DemoSourceLists.cmake +++ b/cmake/DemoSourceLists.cmake @@ -26,24 +26,67 @@ set(PROCEDURAL_SOURCES src/procedural/generator.cc) # Utility sources (unconditional) set(UTIL_SOURCES src/util/asset_manager.cc src/util/file_watcher.cc) +# Common effect sources (shared between headless and normal modes) +set(COMMON_GPU_EFFECTS + src/gpu/effect.cc + src/effects/heptagon_effect.cc + src/effects/particles_effect.cc + src/effects/passthrough_effect.cc + src/effects/moving_ellipse_effect.cc + src/effects/particle_spray_effect.cc + src/effects/gaussian_blur_effect.cc + src/effects/solarize_effect.cc + src/effects/scene1_effect.cc + src/effects/chroma_aberration_effect.cc + src/effects/vignette_effect.cc + cnn_v1/src/cnn_v1_effect.cc + cnn_v2/src/cnn_v2_effect.cc + src/gpu/post_process_helper.cc + src/gpu/shaders.cc + src/effects/hybrid_3d_effect.cc + src/effects/flash_cube_effect.cc + src/effects/theme_modulation_effect.cc + src/effects/fade_effect.cc + src/effects/flash_effect.cc + src/gpu/shader_composer.cc + src/effects/circle_mask_effect.cc + src/effects/rotating_cube_effect.cc + src/gpu/texture_manager.cc + src/gpu/texture_readback.cc + src/effects/sdf_test_effect.cc +) + # GPU sources (conditional: HEADLESS / STRIP_EXTERNAL / NORMAL) demo_set_conditional_sources(GPU_SOURCES # Headless mode: Functional stubs (timeline/audio work) - "src/gpu/headless_gpu.cc;src/gpu/demo_effects.cc;src/gpu/effect.cc;src/effects/heptagon_effect.cc;src/effects/particles_effect.cc;src/effects/passthrough_effect.cc;src/effects/moving_ellipse_effect.cc;src/effects/particle_spray_effect.cc;src/effects/gaussian_blur_effect.cc;src/effects/solarize_effect.cc;src/effects/scene1_effect.cc;src/effects/chroma_aberration_effect.cc;src/effects/vignette_effect.cc;src/effects/cnn_effect.cc;src/effects/cnn_v2_effect.cc;src/gpu/post_process_helper.cc;src/gpu/shaders.cc;src/effects/hybrid_3d_effect.cc;src/effects/flash_cube_effect.cc;src/effects/theme_modulation_effect.cc;src/effects/fade_effect.cc;src/effects/flash_effect.cc;src/gpu/shader_composer.cc;src/effects/circle_mask_effect.cc;src/effects/rotating_cube_effect.cc;src/gpu/texture_manager.cc;src/gpu/texture_readback.cc;src/effects/sdf_test_effect.cc" + "src/gpu/headless_gpu.cc;src/gpu/demo_effects.cc;${COMMON_GPU_EFFECTS}" # Strip mode: Minimal GPU stubs only "src/gpu/stub_gpu.cc" # Normal mode: Full GPU implementation - "src/gpu/gpu.cc;src/gpu/effect.cc;src/effects/heptagon_effect.cc;src/effects/particles_effect.cc;src/effects/passthrough_effect.cc;src/effects/moving_ellipse_effect.cc;src/effects/particle_spray_effect.cc;src/effects/gaussian_blur_effect.cc;src/effects/solarize_effect.cc;src/effects/scene1_effect.cc;src/effects/chroma_aberration_effect.cc;src/effects/vignette_effect.cc;src/effects/cnn_effect.cc;src/effects/cnn_v2_effect.cc;src/gpu/post_process_helper.cc;src/gpu/shaders.cc;src/effects/hybrid_3d_effect.cc;src/effects/flash_cube_effect.cc;src/effects/theme_modulation_effect.cc;src/effects/fade_effect.cc;src/effects/flash_effect.cc;src/gpu/shader_composer.cc;src/effects/circle_mask_effect.cc;src/effects/rotating_cube_effect.cc;src/gpu/texture_manager.cc;src/gpu/texture_readback.cc;src/effects/sdf_test_effect.cc" + "src/gpu/gpu.cc;${COMMON_GPU_EFFECTS}" +) + +# Common 3D sources (shared between headless and normal modes) +set(COMMON_3D_FILES + src/3d/renderer.cc + src/3d/renderer_draw.cc + src/3d/renderer_pipelines.cc + src/3d/renderer_resources.cc + src/3d/renderer_helpers.cc + src/3d/visual_debug.cc + src/3d/bvh.cc + src/3d/physics.cc + src/3d/scene_loader.cc ) # 3D sources (conditional: HEADLESS / STRIP_EXTERNAL / NORMAL) demo_set_conditional_sources(3D_SOURCES # Headless mode: Full 3D (needed for Hybrid3DEffect) - "src/3d/renderer.cc;src/3d/renderer_draw.cc;src/3d/renderer_pipelines.cc;src/3d/renderer_resources.cc;src/3d/renderer_helpers.cc;src/3d/visual_debug.cc;src/3d/bvh.cc;src/3d/physics.cc;src/3d/scene_loader.cc" + "${COMMON_3D_FILES}" # Strip mode: Stub 3D (depends on WebGPU) "src/3d/bvh.cc;src/3d/physics.cc;src/3d/scene_loader.cc" # Normal mode: Full 3D implementation - "src/3d/renderer.cc;src/3d/renderer_draw.cc;src/3d/renderer_pipelines.cc;src/3d/renderer_resources.cc;src/3d/renderer_helpers.cc;src/3d/visual_debug.cc;src/3d/bvh.cc;src/3d/physics.cc;src/3d/scene_loader.cc" + "${COMMON_3D_FILES}" ) # Platform sources (conditional: HEADLESS / STRIP_EXTERNAL / NORMAL) diff --git a/cmake/DemoTests.cmake b/cmake/DemoTests.cmake index fa64284..b511620 100644 --- a/cmake/DemoTests.cmake +++ b/cmake/DemoTests.cmake @@ -3,7 +3,7 @@ add_demo_test(test_window HammingWindowTest audio src/tests/audio/test_window.cc ${GEN_DEMO_CC}) target_link_libraries(test_window PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_window generate_demo_assets) +demo_add_asset_deps(test_window audio) add_demo_test(test_maths MathUtilsTest util src/tests/util/test_maths.cc) @@ -12,71 +12,78 @@ target_link_libraries(test_file_watcher PRIVATE util ${DEMO_LIBS}) add_demo_test(test_synth SynthEngineTest audio src/tests/audio/test_synth.cc ${GEN_DEMO_CC}) target_link_libraries(test_synth PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_synth generate_demo_assets) +demo_add_asset_deps(test_synth audio) add_demo_test(test_dct DctTest audio src/tests/audio/test_dct.cc ${GEN_DEMO_CC}) target_link_libraries(test_dct PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_dct generate_demo_assets) +demo_add_asset_deps(test_dct audio) add_demo_test(test_fft FftTest audio src/tests/audio/test_fft.cc ${GEN_DEMO_CC}) target_link_libraries(test_fft PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_fft generate_demo_assets) +demo_add_asset_deps(test_fft audio) add_demo_test(test_spectral_brush SpectralBrushTest audio src/tests/audio/test_spectral_brush.cc ${GEN_DEMO_CC}) target_link_libraries(test_spectral_brush PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_spectral_brush generate_demo_assets) +demo_add_asset_deps(test_spectral_brush audio) add_demo_test(test_audio_gen AudioGenTest audio src/tests/audio/test_audio_gen.cc ${GEN_DEMO_CC}) target_link_libraries(test_audio_gen PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_audio_gen generate_demo_assets) +demo_add_asset_deps(test_audio_gen audio) add_demo_test(test_audio_backend AudioBackendTest audio src/tests/audio/test_audio_backend.cc ${GEN_DEMO_CC}) target_link_libraries(test_audio_backend PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_audio_backend generate_demo_assets) +demo_add_asset_deps(test_audio_backend audio) add_demo_test(test_silent_backend SilentBackendTest audio src/tests/audio/test_silent_backend.cc src/tests/common/audio_test_fixture.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_silent_backend PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_silent_backend generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_silent_backend audio) +add_dependencies(test_silent_backend generate_tracker_music) add_demo_test(test_mock_backend MockAudioBackendTest audio src/tests/audio/test_mock_backend.cc src/audio/backend/mock_audio_backend.cc ${GEN_DEMO_CC}) target_link_libraries(test_mock_backend PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_mock_backend generate_demo_assets) +demo_add_asset_deps(test_mock_backend audio) add_demo_test(test_wav_dump WavDumpBackendTest audio src/tests/audio/test_wav_dump.cc src/tests/common/audio_test_fixture.cc src/audio/backend/wav_dump_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_wav_dump PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_wav_dump generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_wav_dump audio) +add_dependencies(test_wav_dump generate_tracker_music) add_demo_test(test_jittered_audio JitteredAudioBackendTest audio src/tests/audio/test_jittered_audio.cc src/audio/backend/jittered_audio_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_jittered_audio PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_jittered_audio generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_jittered_audio audio) +add_dependencies(test_jittered_audio generate_tracker_music) add_demo_test(test_tracker_timing TrackerTimingTest audio src/tests/audio/test_tracker_timing.cc src/tests/common/audio_test_fixture.cc src/audio/backend/mock_audio_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_tracker_timing PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_tracker_timing generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_tracker_timing audio) +add_dependencies(test_tracker_timing generate_tracker_music) add_demo_test(test_variable_tempo VariableTempoTest audio src/tests/audio/test_variable_tempo.cc src/tests/common/audio_test_fixture.cc src/audio/backend/mock_audio_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_variable_tempo PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_variable_tempo generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_variable_tempo audio) +add_dependencies(test_variable_tempo generate_tracker_music) add_demo_test(test_tracker TrackerSystemTest audio src/tests/audio/test_tracker.cc src/tests/common/audio_test_fixture.cc ${GEN_DEMO_CC} ${GENERATED_TEST_DEMO_MUSIC_CC}) target_link_libraries(test_tracker PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_tracker generate_demo_assets generate_test_demo_music) +demo_add_asset_deps(test_tracker audio) +add_dependencies(test_tracker generate_test_demo_music) add_demo_test(test_audio_engine AudioEngineTest audio src/tests/audio/test_audio_engine.cc src/tests/common/audio_test_fixture.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_audio_engine PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_audio_engine generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_audio_engine audio) +add_dependencies(test_audio_engine generate_tracker_music) add_demo_test(test_shader_assets ShaderAssetValidation gpu src/tests/gpu/test_shader_assets.cc ${GEN_DEMO_CC}) target_link_libraries(test_shader_assets PRIVATE util procedural ${DEMO_LIBS}) -add_dependencies(test_shader_assets generate_demo_assets) +demo_add_asset_deps(test_shader_assets shaders) add_demo_test(test_shader_compilation ShaderCompilationTest gpu src/tests/gpu/test_shader_compilation.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_shader_compilation PRIVATE gpu util procedural ${DEMO_LIBS}) -add_dependencies(test_shader_compilation generate_demo_assets) +demo_add_asset_deps(test_shader_compilation shaders) add_demo_test(test_noise_functions NoiseFunctionsTest gpu src/tests/gpu/test_noise_functions.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_noise_functions PRIVATE gpu util procedural ${DEMO_LIBS}) -add_dependencies(test_noise_functions generate_demo_assets) +demo_add_asset_deps(test_noise_functions shaders) add_demo_test(test_uniform_helper UniformHelperTest gpu src/tests/gpu/test_uniform_helper.cc) target_link_libraries(test_uniform_helper PRIVATE gpu util ${DEMO_LIBS}) @@ -84,18 +91,20 @@ target_link_libraries(test_uniform_helper PRIVATE gpu util ${DEMO_LIBS}) add_demo_executable(test_spectool src/tests/gpu/test_spectool.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_compile_definitions(test_spectool PRIVATE DEMO_BUILD_TOOLS) target_link_libraries(test_spectool PRIVATE audio util procedural ${DEMO_LIBS}) -add_dependencies(test_spectool generate_tracker_music generate_demo_assets) +demo_add_asset_deps(test_spectool audio) +add_dependencies(test_spectool generate_tracker_music) add_demo_test(test_assets AssetManagerTest assets src/tests/assets/test_assets.cc ${GEN_TEST_CC}) target_include_directories(test_assets PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test) target_compile_definitions(test_assets PRIVATE USE_TEST_ASSETS) target_link_libraries(test_assets PRIVATE util procedural ${DEMO_LIBS}) -add_dependencies(test_assets generate_test_assets) +demo_add_asset_deps(test_assets test) set_source_files_properties(src/tests/assets/test_assets.cc PROPERTIES COMPILE_DEFINITIONS "USE_TEST_ASSETS") add_demo_test(test_sequence SequenceSystemTest assets src/tests/assets/test_sequence.cc ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${PLATFORM_SOURCES}) target_link_libraries(test_sequence PRIVATE 3d gpu util procedural ${DEMO_LIBS}) -add_dependencies(test_sequence generate_timeline generate_demo_assets) +demo_add_asset_deps(test_sequence all) +add_dependencies(test_sequence generate_timeline) add_demo_test(test_procedural ProceduralGenTest util src/tests/util/test_procedural.cc) target_link_libraries(test_procedural PRIVATE procedural ${DEMO_LIBS}) @@ -109,27 +118,30 @@ add_demo_test(test_shader_composer ShaderComposerTest gpu src/tests/gpu/test_sha target_compile_definitions(test_shader_composer PRIVATE USE_TEST_ASSETS) target_include_directories(test_shader_composer PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test ${CORE_INCLUDES}) target_link_libraries(test_shader_composer PRIVATE gpu util procedural ${DEMO_LIBS}) -add_dependencies(test_shader_composer generate_test_assets) +demo_add_asset_deps(test_shader_composer test) add_demo_executable(test_3d_render src/tests/3d/test_3d_render.cc src/tests/common/test_3d_helpers.cc ${PLATFORM_SOURCES} ${GENERATED_TIMELINE_CC} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_3d_render PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_3d_render generate_timeline generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_3d_render all) +add_dependencies(test_3d_render generate_timeline generate_tracker_music) add_demo_executable(test_3d_physics src/tests/3d/test_3d_physics.cc src/tests/common/test_3d_helpers.cc ${PLATFORM_SOURCES} ${GENERATED_TIMELINE_CC} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_3d_physics PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_3d_physics generate_timeline generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_3d_physics all) +add_dependencies(test_3d_physics generate_timeline generate_tracker_music) add_demo_executable(test_mesh src/tests/3d/test_mesh.cc src/tests/common/test_3d_helpers.cc ${PLATFORM_SOURCES} ${GENERATED_TIMELINE_CC} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_mesh PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_mesh generate_timeline generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_mesh all) +add_dependencies(test_mesh generate_timeline generate_tracker_music) add_demo_executable(test_platform src/tests/util/test_platform.cc ${PLATFORM_SOURCES}) target_link_libraries(test_platform PRIVATE util ${DEMO_LIBS}) add_demo_executable(test_scene_loader src/tests/3d/test_scene_loader.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_scene_loader PRIVATE 3d util procedural ${DEMO_LIBS}) -add_dependencies(test_scene_loader generate_demo_assets) +demo_add_asset_deps(test_scene_loader models) add_test(NAME SceneLoaderTest COMMAND test_scene_loader) set_tests_properties(SceneLoaderTest PROPERTIES LABELS "3d") @@ -144,7 +156,8 @@ add_demo_test(test_effect_base EffectBaseTest gpu ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_effect_base PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_effect_base generate_timeline generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_effect_base shaders) +add_dependencies(test_effect_base generate_timeline generate_tracker_music) # GPU Effects Test Infrastructure (Phase 2.1: Effect Classes) add_demo_test(test_demo_effects DemoEffectsTest gpu @@ -157,7 +170,8 @@ add_demo_test(test_demo_effects DemoEffectsTest gpu ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_demo_effects PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_demo_effects generate_timeline generate_demo_assets generate_tracker_music) +demo_add_asset_deps(test_demo_effects shaders) +add_dependencies(test_demo_effects generate_timeline generate_tracker_music) # GPU Effects Test Infrastructure (Phase 2.2: Post-Process Utilities) add_demo_test(test_post_process_helper PostProcessHelperTest gpu @@ -167,7 +181,7 @@ add_demo_test(test_post_process_helper PostProcessHelperTest gpu ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_post_process_helper PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_post_process_helper generate_demo_assets) +demo_add_asset_deps(test_post_process_helper shaders) # TextureManager tests add_demo_test(test_texture_manager TextureManagerTest gpu @@ -176,7 +190,7 @@ add_demo_test(test_texture_manager TextureManagerTest gpu ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_texture_manager PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_texture_manager generate_demo_assets) +demo_add_asset_deps(test_texture_manager shaders) # GPU Procedural Texture Test add_demo_test(test_gpu_procedural GpuProceduralTest gpu @@ -184,7 +198,7 @@ add_demo_test(test_gpu_procedural GpuProceduralTest gpu ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_gpu_procedural PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_gpu_procedural generate_demo_assets) +demo_add_asset_deps(test_gpu_procedural shaders) # CNN shader testing tool (only when STRIP_ALL is OFF) if(NOT DEMO_STRIP_ALL) @@ -204,7 +218,7 @@ if(NOT DEMO_STRIP_ALL) target_link_libraries(cnn_test PRIVATE gpu util procedural ${DEMO_LIBS}) - add_dependencies(cnn_test generate_demo_assets) + demo_add_asset_deps(cnn_test shaders) # Define STB_IMAGE macros target_compile_definitions(cnn_test PRIVATE @@ -218,7 +232,7 @@ add_demo_test(test_gpu_composite GpuCompositeTest gpu ${PLATFORM_SOURCES} ${GEN_DEMO_CC}) target_link_libraries(test_gpu_composite PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) -add_dependencies(test_gpu_composite generate_demo_assets) +demo_add_asset_deps(test_gpu_composite shaders) # Subsystem test targets diff --git a/cnn_v1/README.md b/cnn_v1/README.md new file mode 100644 index 0000000..052f22a --- /dev/null +++ b/cnn_v1/README.md @@ -0,0 +1,64 @@ +# CNN v1: Original Post-Processing Neural Network + +**Architecture:** 3-layer convolution, generated shader weights +**Status:** Active (used in timeline), legacy architecture + +## Overview + +Original CNN implementation with per-layer WGSL shaders. Supports multiple kernel sizes (1×1, 3×3, 5×5, 7×7) with generated weight arrays. + +**For new work, use CNN v2** (`cnn_v2/`) which provides: +- Storage buffer architecture (~3.2 KB vs generated WGSL) +- 7D static features (RGBD + UV + sin + bias) +- Sigmoid activation with stable training +- Dynamic layer configuration + +## Quick Reference + +**Training:** +```bash +./cnn_v1/training/train_cnn.py --input training/input --target training/output \ + --layers 3 --kernel_sizes 3,5,3 --epochs 5000 +``` + +**Integration:** +- **C++:** `cnn_v1/src/cnn_effect.{h,cc}` +- **Assets:** `workspaces/main/assets.txt` (lines 40-46) +- **Timeline:** `workspaces/main/timeline.seq` (CNNEffect) + +## Documentation + +- [CNN.md](docs/CNN.md) - Architecture overview +- [CNN_V1_EFFECT.md](docs/CNN_V1_EFFECT.md) - Implementation details +- [CNN_TEST_TOOL.md](docs/CNN_TEST_TOOL.md) - Testing guide +- [CNN_DEBUG.md](docs/CNN_DEBUG.md) - Debugging notes + +## Directory Structure + +``` +cnn_v1/ +├── README.md # This file +├── src/ +│ ├── cnn_effect.h # Effect header +│ └── cnn_effect.cc # Effect implementation +├── shaders/ # WGSL shaders (7 files) +├── training/ # Python training script +└── docs/ # Documentation (7 markdown files) +``` + +## Differences from CNN v2 + +| Feature | CNN v1 | CNN v2 | +|---------|--------|--------| +| Architecture | Generated WGSL weights | Storage buffer weights | +| Input Features | 4D (RGBA/prev layer) | 12D (4D + 8D static) | +| Activation | ReLU | Sigmoid + ReLU | +| Size | ~Variable (WGSL gen) | ~3.2 KB (binary) | +| Training | Full-image | Patch-based (default) | +| Layer Config | Compile-time | Runtime (dynamic) | + +## Migration Notes + +CNN v1 remains in the timeline for historical validation. For new effects or experiments, use CNN v2's enhanced feature set and compact binary format. + +See `cnn_v2/docs/CNN_V2.md` for CNN v2 architecture details. diff --git a/doc/CNN.md b/cnn_v1/docs/CNN.md index 2dc3362..5d9a667 100644 --- a/doc/CNN.md +++ b/cnn_v1/docs/CNN.md @@ -8,7 +8,7 @@ Have the input 3d scene be processed by a multi-layer CNN trained on the side. Input: some rendered scene. Output: 'stylized' scene with CNN post-processing. -**See `doc/CNN_EFFECT.md` for implementation details, usage, and API reference.** +**See `CNN_V1_EFFECT.md` for implementation details, usage, and API reference.** ## Shader implementation diff --git a/doc/CNN_BIAS_FIX_2026-02.md b/cnn_v1/docs/CNN_BIAS_FIX_2026-02.md index 26db8eb..26db8eb 100644 --- a/doc/CNN_BIAS_FIX_2026-02.md +++ b/cnn_v1/docs/CNN_BIAS_FIX_2026-02.md diff --git a/doc/CNN_DEBUG.md b/cnn_v1/docs/CNN_DEBUG.md index ba220a0..ba220a0 100644 --- a/doc/CNN_DEBUG.md +++ b/cnn_v1/docs/CNN_DEBUG.md diff --git a/doc/CNN_FLATTEN_ANALYSIS.md b/cnn_v1/docs/CNN_FLATTEN_ANALYSIS.md index bf63c5d..8664157 100644 --- a/doc/CNN_FLATTEN_ANALYSIS.md +++ b/cnn_v1/docs/CNN_FLATTEN_ANALYSIS.md @@ -183,7 +183,7 @@ These yield better size/performance than shader architecture changes. ## References -- `doc/CNN_EFFECT.md` - CNN implementation details -- `doc/CNN.md` - High-level CNN design -- `src/effects/cnn_effect.cc` - Current implementation +- `CNN_V1_EFFECT.md` - CNN implementation details +- `CNN.md` - High-level CNN design +- `../src/cnn_effect.cc` - Current implementation - `workspaces/main/shaders/cnn_*.wgsl` - Shader snippets diff --git a/doc/CNN_RGBD_GRAYSCALE_SUMMARY.md b/cnn_v1/docs/CNN_RGBD_GRAYSCALE_SUMMARY.md index 3439f2c..3439f2c 100644 --- a/doc/CNN_RGBD_GRAYSCALE_SUMMARY.md +++ b/cnn_v1/docs/CNN_RGBD_GRAYSCALE_SUMMARY.md diff --git a/doc/CNN_TEST_TOOL.md b/cnn_v1/docs/CNN_TEST_TOOL.md index 4307894..4307894 100644 --- a/doc/CNN_TEST_TOOL.md +++ b/cnn_v1/docs/CNN_TEST_TOOL.md diff --git a/doc/CNN_EFFECT.md b/cnn_v1/docs/CNN_V1_EFFECT.md index 40f095e..40f095e 100644 --- a/doc/CNN_EFFECT.md +++ b/cnn_v1/docs/CNN_V1_EFFECT.md diff --git a/workspaces/main/shaders/cnn/cnn_activation.wgsl b/cnn_v1/shaders/cnn_activation.wgsl index 4fe771e..4fe771e 100644 --- a/workspaces/main/shaders/cnn/cnn_activation.wgsl +++ b/cnn_v1/shaders/cnn_activation.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_conv1x1.wgsl b/cnn_v1/shaders/cnn_conv1x1.wgsl index f77cfa8..f77cfa8 100644 --- a/workspaces/main/shaders/cnn/cnn_conv1x1.wgsl +++ b/cnn_v1/shaders/cnn_conv1x1.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_conv3x3.wgsl b/cnn_v1/shaders/cnn_conv3x3.wgsl index f7d11b1..f7d11b1 100644 --- a/workspaces/main/shaders/cnn/cnn_conv3x3.wgsl +++ b/cnn_v1/shaders/cnn_conv3x3.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_conv5x5.wgsl b/cnn_v1/shaders/cnn_conv5x5.wgsl index 9328d75..9328d75 100644 --- a/workspaces/main/shaders/cnn/cnn_conv5x5.wgsl +++ b/cnn_v1/shaders/cnn_conv5x5.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_conv7x7.wgsl b/cnn_v1/shaders/cnn_conv7x7.wgsl index e68d644..e68d644 100644 --- a/workspaces/main/shaders/cnn/cnn_conv7x7.wgsl +++ b/cnn_v1/shaders/cnn_conv7x7.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_layer.wgsl b/cnn_v1/shaders/cnn_layer.wgsl index cbd1686..cbd1686 100644 --- a/workspaces/main/shaders/cnn/cnn_layer.wgsl +++ b/cnn_v1/shaders/cnn_layer.wgsl diff --git a/workspaces/main/shaders/cnn/cnn_weights_generated.wgsl b/cnn_v1/shaders/cnn_weights_generated.wgsl index 510f86f..510f86f 100644 --- a/workspaces/main/shaders/cnn/cnn_weights_generated.wgsl +++ b/cnn_v1/shaders/cnn_weights_generated.wgsl diff --git a/src/effects/cnn_effect.cc b/cnn_v1/src/cnn_v1_effect.cc index 49c5239..1f44619 100644 --- a/src/effects/cnn_effect.cc +++ b/cnn_v1/src/cnn_v1_effect.cc @@ -1,7 +1,7 @@ // CNN post-processing effect implementation // Neural network-based stylization with modular WGSL -#include "effects/cnn_effect.h" +#include "cnn_v1_effect.h" #include "gpu/bind_group_builder.h" #include "gpu/effect.h" #include "gpu/pipeline_builder.h" @@ -33,7 +33,7 @@ static WGPURenderPipeline create_cnn_pipeline(WGPUDevice device, return pipeline; } -CNNEffect::CNNEffect(const GpuContext& ctx) +CNNv1Effect::CNNv1Effect(const GpuContext& ctx) : PostProcessEffect(ctx), layer_index_(0), total_layers_(1), blend_amount_(1.0f), input_view_(nullptr), original_view_(nullptr), bind_group_(nullptr) { @@ -41,7 +41,7 @@ CNNEffect::CNNEffect(const GpuContext& ctx) create_cnn_pipeline(ctx_.device, ctx_.format, cnn_layer_shader_wgsl); } -CNNEffect::CNNEffect(const GpuContext& ctx, const CNNEffectParams& params) +CNNv1Effect::CNNv1Effect(const GpuContext& ctx, const CNNv1EffectParams& params) : PostProcessEffect(ctx), layer_index_(params.layer_index), total_layers_(params.total_layers), blend_amount_(params.blend_amount), input_view_(nullptr), original_view_(nullptr), bind_group_(nullptr) { @@ -49,7 +49,7 @@ CNNEffect::CNNEffect(const GpuContext& ctx, const CNNEffectParams& params) create_cnn_pipeline(ctx_.device, ctx_.format, cnn_layer_shader_wgsl); } -void CNNEffect::init(MainSequence* demo) { +void CNNv1Effect::init(MainSequence* demo) { PostProcessEffect::init(demo); demo_ = demo; params_buffer_.init(ctx_.device); @@ -62,11 +62,11 @@ void CNNEffect::init(MainSequence* demo) { // Initialize uniforms BEFORE any bind group creation uniforms_.update(ctx_.queue, get_common_uniforms()); - CNNLayerParams params = {layer_index_, blend_amount_, {0.0f, 0.0f}}; + CNNv1LayerParams params = {layer_index_, blend_amount_, {0.0f, 0.0f}}; params_buffer_.update(ctx_.queue, params); } -void CNNEffect::resize(int width, int height) { +void CNNv1Effect::resize(int width, int height) { if (width == width_ && height == height_) return; @@ -78,7 +78,7 @@ void CNNEffect::resize(int width, int height) { } } -void CNNEffect::render(WGPURenderPassEncoder pass, +void CNNv1Effect::render(WGPURenderPassEncoder pass, const CommonPostProcessUniforms& uniforms) { if (!bind_group_) { fprintf(stderr, "CNN render: no bind_group\n"); @@ -90,7 +90,7 @@ void CNNEffect::render(WGPURenderPassEncoder pass, effective_blend = blend_amount_ * uniforms.beat_phase * beat_scale_; } - CNNLayerParams params = {layer_index_, effective_blend, {0.0f, 0.0f}}; + CNNv1LayerParams params = {layer_index_, effective_blend, {0.0f, 0.0f}}; params_buffer_.update(ctx_.queue, params); wgpuRenderPassEncoderSetPipeline(pass, pipeline_); @@ -98,7 +98,7 @@ void CNNEffect::render(WGPURenderPassEncoder pass, wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); } -void CNNEffect::update_bind_group(WGPUTextureView input_view) { +void CNNv1Effect::update_bind_group(WGPUTextureView input_view) { input_view_ = input_view; // Update common uniforms (CRITICAL for UV calculation!) diff --git a/src/effects/cnn_effect.h b/cnn_v1/src/cnn_v1_effect.h index cdcd656..e820275 100644 --- a/src/effects/cnn_effect.h +++ b/cnn_v1/src/cnn_v1_effect.h @@ -5,23 +5,23 @@ #include "gpu/effect.h" #include "gpu/uniform_helper.h" -struct CNNLayerParams { +struct CNNv1LayerParams { int layer_index; float blend_amount; // Blend: mix(input, output, blend_amount) float _pad[2]; }; -static_assert(sizeof(CNNLayerParams) == 16); +static_assert(sizeof(CNNv1LayerParams) == 16); -struct CNNEffectParams { +struct CNNv1EffectParams { int layer_index = 0; // Which layer to render (0-based) int total_layers = 1; // Total number of layers in the CNN float blend_amount = 1.0f; // Final blend with original input }; -class CNNEffect : public PostProcessEffect { +class CNNv1Effect : public PostProcessEffect { public: - explicit CNNEffect(const GpuContext& ctx); - explicit CNNEffect(const GpuContext& ctx, const CNNEffectParams& params); + explicit CNNv1Effect(const GpuContext& ctx); + explicit CNNv1Effect(const GpuContext& ctx, const CNNv1EffectParams& params); void init(MainSequence* demo) override; void resize(int width, int height) override; @@ -47,7 +47,7 @@ class CNNEffect : public PostProcessEffect { float beat_scale_ = 1.0f; WGPUTextureView input_view_; WGPUTextureView original_view_; - UniformBuffer<CNNLayerParams> params_buffer_; + UniformBuffer<CNNv1LayerParams> params_buffer_; WGPUBindGroup bind_group_; MainSequence* demo_ = nullptr; }; diff --git a/training/train_cnn.py b/cnn_v1/training/train_cnn.py index 4171dcb..4171dcb 100755 --- a/training/train_cnn.py +++ b/cnn_v1/training/train_cnn.py diff --git a/cnn_v2/README.md b/cnn_v2/README.md new file mode 100644 index 0000000..ef0cf44 --- /dev/null +++ b/cnn_v2/README.md @@ -0,0 +1,60 @@ +# CNN v2: Parametric Post-Processing Neural Network + +**Architecture:** 3-layer compute, storage buffer (~3.2 KB) +**Features:** 7D static (RGBD + UV + sin + bias), sigmoid activation + +## Quick Start + +```bash +./cnn_v2/scripts/train_cnn_v2_full.sh +``` + +## Documentation + +- [CNN_V2.md](docs/CNN_V2.md) - Architecture and implementation details +- [CNN_V2_BINARY_FORMAT.md](docs/CNN_V2_BINARY_FORMAT.md) - Weight format specification +- [CNN_V2_WEB_TOOL.md](docs/CNN_V2_WEB_TOOL.md) - Validation tool documentation +- [CNN_V2_DEBUG_TOOLS.md](docs/CNN_V2_DEBUG_TOOLS.md) - Debugging and analysis tools + +## Integration + +- **C++:** `cnn_v2/src/cnn_v2_effect.{h,cc}` +- **Assets:** `workspaces/main/assets.txt` (lines 47-49) +- **Test:** `src/tests/gpu/test_demo_effects.cc` (line 93) + +## Directory Structure + +``` +cnn_v2/ +├── README.md # This file +├── src/ +│ ├── cnn_v2_effect.h # Effect header +│ └── cnn_v2_effect.cc # Effect implementation +├── shaders/ # WGSL shaders (6 files) +├── weights/ # Binary weights (3 files) +├── training/ # Python training scripts (4 files) +├── scripts/ # Shell scripts (train_cnn_v2_full.sh) +├── tools/ # Validation tools (HTML) +└── docs/ # Documentation (4 markdown files) +``` + +## Training Pipeline + +1. **Train model:** `./cnn_v2/scripts/train_cnn_v2_full.sh` +2. **Export weights:** Automatic (binary format, ~3.2 KB) +3. **Validate:** HTML tool at `cnn_v2/tools/cnn_v2_test/index.html` + +For detailed training options: `./cnn_v2/scripts/train_cnn_v2_full.sh --help` + +## Key Features + +- **Parametric static features:** 7D input (RGBD + UV + sin encoding + bias) +- **Storage buffer architecture:** Dynamic layer count, compact binary format +- **Sigmoid activation:** Smooth gradients, prevents training collapse +- **Patch-based training:** Sample-efficient, focuses on salient regions +- **Sub-10KB target:** Achieved with 3-layer model (~3.2 KB) + +## Next Steps + +- **8-bit quantization:** 2× size reduction (~1.6 KB) via quantization-aware training (QAT) +- **CNN v3:** U-Net architecture for enhanced quality (separate directory) diff --git a/doc/CNN_V2.md b/cnn_v2/docs/CNN_V2.md index b7fd6f8..b7fd6f8 100644 --- a/doc/CNN_V2.md +++ b/cnn_v2/docs/CNN_V2.md diff --git a/doc/CNN_V2_BINARY_FORMAT.md b/cnn_v2/docs/CNN_V2_BINARY_FORMAT.md index 59c859d..59c859d 100644 --- a/doc/CNN_V2_BINARY_FORMAT.md +++ b/cnn_v2/docs/CNN_V2_BINARY_FORMAT.md diff --git a/doc/CNN_V2_DEBUG_TOOLS.md b/cnn_v2/docs/CNN_V2_DEBUG_TOOLS.md index 8d1289a..8d1289a 100644 --- a/doc/CNN_V2_DEBUG_TOOLS.md +++ b/cnn_v2/docs/CNN_V2_DEBUG_TOOLS.md diff --git a/doc/CNN_V2_WEB_TOOL.md b/cnn_v2/docs/CNN_V2_WEB_TOOL.md index b6f5b0b..b6f5b0b 100644 --- a/doc/CNN_V2_WEB_TOOL.md +++ b/cnn_v2/docs/CNN_V2_WEB_TOOL.md diff --git a/scripts/train_cnn_v2_full.sh b/cnn_v2/scripts/train_cnn_v2_full.sh index 078ea28..a21c1ac 100755 --- a/scripts/train_cnn_v2_full.sh +++ b/cnn_v2/scripts/train_cnn_v2_full.sh @@ -53,7 +53,7 @@ cd "$PROJECT_ROOT" # Helper functions export_weights() { - python3 training/export_cnn_v2_weights.py "$1" --output-weights "$2" --quiet + python3 "$SCRIPT_DIR/../training/export_cnn_v2_weights.py" "$1" --output-weights "$2" --quiet } find_latest_checkpoint() { @@ -64,6 +64,10 @@ build_target() { cmake --build build -j4 --target "$1" > /dev/null 2>&1 } +# Path resolution for running from any directory +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + # Default configuration INPUT_DIR="training/input" TARGET_DIR="training/target_1" @@ -82,7 +86,7 @@ MIP_LEVEL=0 GRAYSCALE_LOSS=false FULL_IMAGE_MODE=false IMAGE_SIZE=256 -OUTPUT_WEIGHTS="workspaces/main/weights/cnn_v2_weights.bin" +OUTPUT_WEIGHTS="${PROJECT_ROOT}/workspaces/main/weights/cnn_v2_weights.bin" # Parse arguments VALIDATE_ONLY=false @@ -306,7 +310,7 @@ if [ "$VALIDATE_ONLY" = false ]; then # Step 1: Train model echo "[1/4] Training CNN v2 model..." -python3 training/train_cnn_v2.py \ +python3 "$SCRIPT_DIR/../training/train_cnn_v2.py" \ --input "$INPUT_DIR" \ --target "$TARGET_DIR" \ $TRAINING_MODE_ARGS \ diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_compute.wgsl b/cnn_v2/shaders/cnn_v2_compute.wgsl index cdbfd74..cdbfd74 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_compute.wgsl +++ b/cnn_v2/shaders/cnn_v2_compute.wgsl diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_0.wgsl b/cnn_v2/shaders/cnn_v2_layer_0.wgsl index 8e14957..8e14957 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_0.wgsl +++ b/cnn_v2/shaders/cnn_v2_layer_0.wgsl diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_1.wgsl b/cnn_v2/shaders/cnn_v2_layer_1.wgsl index f490d13..f490d13 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_1.wgsl +++ b/cnn_v2/shaders/cnn_v2_layer_1.wgsl diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_2.wgsl b/cnn_v2/shaders/cnn_v2_layer_2.wgsl index 2f9836a..2f9836a 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_2.wgsl +++ b/cnn_v2/shaders/cnn_v2_layer_2.wgsl diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_template.wgsl b/cnn_v2/shaders/cnn_v2_layer_template.wgsl index 1bf6819..1bf6819 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_layer_template.wgsl +++ b/cnn_v2/shaders/cnn_v2_layer_template.wgsl diff --git a/workspaces/main/shaders/cnn_v2/cnn_v2_static.wgsl b/cnn_v2/shaders/cnn_v2_static.wgsl index 309e832..309e832 100644 --- a/workspaces/main/shaders/cnn_v2/cnn_v2_static.wgsl +++ b/cnn_v2/shaders/cnn_v2_static.wgsl diff --git a/src/effects/cnn_v2_effect.cc b/cnn_v2/src/cnn_v2_effect.cc index 7127aae..60538d4 100644 --- a/src/effects/cnn_v2_effect.cc +++ b/cnn_v2/src/cnn_v2_effect.cc @@ -1,6 +1,6 @@ // CNN v2 Effect Implementation -#include "effects/cnn_v2_effect.h" +#include "cnn_v2_effect.h" #if defined(USE_TEST_ASSETS) #include "test_assets.h" diff --git a/src/effects/cnn_v2_effect.h b/cnn_v2/src/cnn_v2_effect.h index 7960b4f..7960b4f 100644 --- a/src/effects/cnn_v2_effect.h +++ b/cnn_v2/src/cnn_v2_effect.h diff --git a/tools/cnn_v2_test/README.md b/cnn_v2/tools/cnn_v2_test/README.md index d41a00f..d41a00f 100644 --- a/tools/cnn_v2_test/README.md +++ b/cnn_v2/tools/cnn_v2_test/README.md diff --git a/tools/cnn_v2_test/index.html b/cnn_v2/tools/cnn_v2_test/index.html index e226d0c..84702d5 100644 --- a/tools/cnn_v2_test/index.html +++ b/cnn_v2/tools/cnn_v2_test/index.html @@ -32,32 +32,21 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CNN v2 Testing Tool</title> + <link rel="stylesheet" href="../common/style.css"> <style> - * { margin: 0; padding: 0; box-sizing: border-box; } body { - font-family: 'Courier New', monospace; - background: #1a1a1a; - color: #e0e0e0; display: flex; flex-direction: column; height: 100vh; - overflow: hidden; } .header { - background: #2a2a2a; padding: 16px; border-bottom: 1px solid #404040; - display: flex; - align-items: center; gap: 24px; - flex-wrap: wrap; } h1 { font-size: 18px; } .controls { - display: flex; gap: 16px; - align-items: center; - flex-wrap: wrap; } .control-group { display: flex; @@ -66,7 +55,7 @@ } .control-group label { font-size: 12px; } input[type="range"] { width: 120px; } - input[type="number"] { width: 60px; background: #1a1a1a; color: #e0e0e0; border: 1px solid #404040; padding: 4px; } + input[type="number"] { width: 60px; padding: 4px; } .drop-zone { border: 3px dashed #606060; padding: 20px; @@ -80,18 +69,10 @@ color: #4a9eff; } button { - background: #1a1a1a; - border: 1px solid #404040; - color: #e0e0e0; padding: 6px 12px; font-size: 12px; - font-family: 'Courier New', monospace; - cursor: pointer; - transition: all 0.2s; - border-radius: 4px; } button:hover { border-color: #606060; background: #252525; } - button:disabled { opacity: 0.3; cursor: not-allowed; } video { display: none; } .drop-zone:hover { border-color: #4a9eff; background: #2a3545; } .drop-zone.active { border-color: #4a9eff; background: #1a2a3a; } @@ -120,7 +101,6 @@ padding: 24px; overflow: auto; position: relative; - background: #1a1a1a; } .video-controls-float { position: absolute; @@ -185,7 +165,6 @@ padding: 16px; } .panel { - border: 1px solid #404040; border-radius: 4px; overflow: hidden; } @@ -228,28 +207,14 @@ margin-bottom: 12px; } .layer-buttons button { - background: #1a1a1a; - border: 1px solid #404040; - color: #e0e0e0; padding: 6px 12px; font-size: 10px; - font-family: 'Courier New', monospace; - cursor: pointer; - transition: all 0.2s; - } - .layer-buttons button:hover { - border-color: #606060; - background: #252525; } .layer-buttons button.active { background: #4a9eff; border-color: #4a9eff; color: #1a1a1a; } - .layer-buttons button:disabled { - opacity: 0.3; - cursor: not-allowed; - } .layer-buttons button:disabled:hover { border-color: #404040; background: #1a1a1a; diff --git a/training/export_cnn_v2_shader.py b/cnn_v2/training/export_cnn_v2_shader.py index 1c74ad0..8692a62 100755 --- a/training/export_cnn_v2_shader.py +++ b/cnn_v2/training/export_cnn_v2_shader.py @@ -13,6 +13,10 @@ import numpy as np import torch from pathlib import Path +# Path resolution for running from any directory +SCRIPT_DIR = Path(__file__).parent +PROJECT_ROOT = SCRIPT_DIR.parent.parent + def export_layer_shader(layer_idx, weights, kernel_size, output_dir, mip_level=0, is_output_layer=False): """Generate WGSL compute shader for a single CNN layer. @@ -203,7 +207,7 @@ def export_checkpoint(checkpoint_path, output_dir): def main(): parser = argparse.ArgumentParser(description='Export CNN v2 checkpoint to WGSL shaders') parser.add_argument('checkpoint', type=str, help='Path to checkpoint .pth file') - parser.add_argument('--output-dir', type=str, default='workspaces/main/shaders', + parser.add_argument('--output-dir', type=str, default=str(PROJECT_ROOT / 'workspaces/main/shaders'), help='Output directory for shaders') args = parser.parse_args() diff --git a/training/export_cnn_v2_weights.py b/cnn_v2/training/export_cnn_v2_weights.py index f64bd8d..d66b980 100755 --- a/training/export_cnn_v2_weights.py +++ b/cnn_v2/training/export_cnn_v2_weights.py @@ -11,6 +11,10 @@ import torch import struct from pathlib import Path +# Path resolution for running from any directory +SCRIPT_DIR = Path(__file__).parent +PROJECT_ROOT = SCRIPT_DIR.parent.parent + def export_weights_binary(checkpoint_path, output_path, quiet=False): """Export CNN v2 weights to binary format. @@ -261,9 +265,9 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) { def main(): parser = argparse.ArgumentParser(description='Export CNN v2 weights to binary format') parser.add_argument('checkpoint', type=str, help='Path to checkpoint .pth file') - parser.add_argument('--output-weights', type=str, default='workspaces/main/weights/cnn_v2_weights.bin', + parser.add_argument('--output-weights', type=str, default=str(PROJECT_ROOT / 'workspaces/main/weights/cnn_v2_weights.bin'), help='Output binary weights file') - parser.add_argument('--output-shader', type=str, default='workspaces/main/shaders', + parser.add_argument('--output-shader', type=str, default=str(PROJECT_ROOT / 'workspaces/main/shaders'), help='Output directory for shader template') parser.add_argument('--quiet', action='store_true', help='Suppress detailed output') diff --git a/training/gen_identity_weights.py b/cnn_v2/training/gen_identity_weights.py index 7865d68..08eecc6 100755 --- a/training/gen_identity_weights.py +++ b/cnn_v2/training/gen_identity_weights.py @@ -21,6 +21,10 @@ import numpy as np import struct from pathlib import Path +# Path resolution for running from any directory +SCRIPT_DIR = Path(__file__).parent +PROJECT_ROOT = SCRIPT_DIR.parent.parent + def generate_identity_weights(output_path, kernel_size=1, mip_level=0, mix=False, p47=False): """Generate identity weights: output = input (ignores static features). @@ -149,7 +153,7 @@ def generate_identity_weights(output_path, kernel_size=1, mip_level=0, mix=False def main(): parser = argparse.ArgumentParser(description='Generate identity CNN v2 weights') parser.add_argument('output', type=str, nargs='?', - default='workspaces/main/weights/cnn_v2_identity.bin', + default=str(PROJECT_ROOT / 'workspaces/main/weights/cnn_v2_identity.bin'), help='Output .bin file path') parser.add_argument('--kernel-size', type=int, default=1, help='Kernel size (default: 1×1)') diff --git a/training/train_cnn_v2.py b/cnn_v2/training/train_cnn_v2.py index 9e5df2f..9e5df2f 100755 --- a/training/train_cnn_v2.py +++ b/cnn_v2/training/train_cnn_v2.py diff --git a/cnn_v3/README.md b/cnn_v3/README.md new file mode 100644 index 0000000..fdbf648 --- /dev/null +++ b/cnn_v3/README.md @@ -0,0 +1,36 @@ +# CNN v3 + +Enhanced CNN post-processing with next-generation features. + +## Directory Structure + +``` +cnn_v3/ +├── docs/ # Documentation and design notes +├── scripts/ # Training and build automation scripts +├── shaders/ # WGSL compute shaders +├── src/ # C++ implementation +├── tools/ # Testing and validation tools +├── training/ # Training pipeline +│ ├── input/ # Source images for training +│ ├── target_1/ # Style 1 target images +│ └── target_2/ # Style 2 target images +└── weights/ # Trained model weights (binary format) +``` + +## Training Data + +Training images are tracked in the repository: +- `training/input/` - Original input images +- `training/target_1/` - First style transformation targets +- `training/target_2/` - Second style transformation targets + +Multiple target directories allow training different stylistic transformations from the same input set. + +Add images directly to these directories and commit them. + +## Status + +**TODO:** Define CNN v3 architecture and feature set. + +See `cnn_v2/` for reference implementation. diff --git a/cnn_v3/training/input/photo1.jpg b/cnn_v3/training/input/photo1.jpg Binary files differnew file mode 100644 index 0000000..1c1c2b2 --- /dev/null +++ b/cnn_v3/training/input/photo1.jpg diff --git a/cnn_v3/training/input/photo2.jpg b/cnn_v3/training/input/photo2.jpg Binary files differnew file mode 100644 index 0000000..a662fa8 --- /dev/null +++ b/cnn_v3/training/input/photo2.jpg diff --git a/cnn_v3/training/input/photo3.jpg b/cnn_v3/training/input/photo3.jpg Binary files differnew file mode 100644 index 0000000..703645e --- /dev/null +++ b/cnn_v3/training/input/photo3.jpg diff --git a/cnn_v3/training/input/photo4.jpg b/cnn_v3/training/input/photo4.jpg Binary files differnew file mode 100644 index 0000000..5035993 --- /dev/null +++ b/cnn_v3/training/input/photo4.jpg diff --git a/cnn_v3/training/target_1/photo1_out.png b/cnn_v3/training/target_1/photo1_out.png Binary files differnew file mode 100644 index 0000000..36a36a7 --- /dev/null +++ b/cnn_v3/training/target_1/photo1_out.png diff --git a/cnn_v3/training/target_1/photo2_1_out.png b/cnn_v3/training/target_1/photo2_1_out.png Binary files differnew file mode 100644 index 0000000..5398232 --- /dev/null +++ b/cnn_v3/training/target_1/photo2_1_out.png diff --git a/cnn_v3/training/target_1/photo2_2_out.png b/cnn_v3/training/target_1/photo2_2_out.png Binary files differnew file mode 100644 index 0000000..b0a76bd --- /dev/null +++ b/cnn_v3/training/target_1/photo2_2_out.png diff --git a/cnn_v3/training/target_1/photo4_out.png b/cnn_v3/training/target_1/photo4_out.png Binary files differnew file mode 100644 index 0000000..56f6274 --- /dev/null +++ b/cnn_v3/training/target_1/photo4_out.png diff --git a/doc/AUDIO_WAV_DRIFT_BUG.md b/doc/AUDIO_WAV_DRIFT_BUG.md index e22f4fa..050dd49 100644 --- a/doc/AUDIO_WAV_DRIFT_BUG.md +++ b/doc/AUDIO_WAV_DRIFT_BUG.md @@ -1,7 +1,8 @@ # Audio WAV Drift Bug Investigation **Date:** 2026-02-15 -**Status:** ROOT CAUSE IDENTIFIED +**Status:** ACCEPTABLE (to be continued) +**Current State:** -150ms drift at beat 64b, no glitches ## Problem Statement @@ -163,8 +164,18 @@ Eliminates cumulative truncation error. 1. ✅ Measure WAV sample positions directly (Python script) 2. ✅ Add render tracking debug output 3. ✅ Confirm over-rendering (366ms per 10s) -4. ⏳ Implement fix -5. ⏳ Verify corrected WAV alignment in viewer +4. ✅ Implement partial fix (bypass ring buffer, direct render) +5. ⚠️ Current result: -150ms drift at beat 64b (acceptable, needs further work) + +## Current Implementation (main.cc:286-308) + +**WAV dump now bypasses ring buffer entirely:** +1. **Frame accumulator**: Calculates exact frames per update (no truncation) +2. **Direct render**: Calls `synth_render()` directly with exact frame count +3. **No ring buffer**: Eliminates buffer management complexity +4. **Result**: No glitches, but -150ms drift remains + +**Remaining issue:** Drift persists despite direct rendering. Likely related to tempo scaling or audio engine state management. Acceptable for now. ## Notes diff --git a/doc/AUXILIARY_TEXTURE_INIT.md b/doc/AUXILIARY_TEXTURE_INIT.md index 9cac70b..036cbf7 100644 --- a/doc/AUXILIARY_TEXTURE_INIT.md +++ b/doc/AUXILIARY_TEXTURE_INIT.md @@ -18,7 +18,7 @@ entry.seq->resize(width, height); // Too late - textures already created **Affected:** - CircleMaskEffect (circle_mask texture) -- CNNEffect (captured_frame texture) +- CNNv1Effect (captured_frame texture) - RotatingCubeEffect (consumer, hardcoded resolution in uniforms) --- diff --git a/doc/BUILD.md b/doc/BUILD.md index d3434f4..fd0c3d9 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -95,9 +95,11 @@ Use Xcode Metal debugger for shader performance analysis. ## Build System Internals **Asset Dependency Tracking:** -- CMake tracks 42 demo + 17 test assets -- Editing shaders/audio/sequences auto-triggers rebuild -- Asset lists parsed to extract individual file dependencies +- CMake tracks 42 demo + 17 test assets split into 4 categories +- **Granular rebuilds:** Changing a shader only rebuilds shader-dependent targets +- **Categories:** `shaders` (.wgsl), `audio` (.spec, .track), `models` (.obj), `data` (.bin, .png, PROC) +- Asset lists parsed at configure time to extract category-specific file dependencies +- Unified output (`assets_data.cc`) avoids duplicate symbols while preserving granular tracking **Header Organization:** - `asset_manager_dcl.h`: Forward declarations diff --git a/doc/CMAKE_MODULES.md b/doc/CMAKE_MODULES.md index 2ea7d00..9f71d91 100644 --- a/doc/CMAKE_MODULES.md +++ b/doc/CMAKE_MODULES.md @@ -90,6 +90,18 @@ Creates an executable for the demo (legacy macro). ### `add_demo_test(NAME TEST_NAME LABEL SOURCES...)` Creates a test executable and registers it with CTest (legacy macro). +### `demo_add_asset_deps(TARGET CATEGORY)` +Adds asset category dependencies to a target for granular rebuilds. + +**Categories:** `shaders`, `audio`, `models`, `data`, `all`, `test` + +**Example:** +```cmake +demo_add_asset_deps(test_synth audio) # Only depends on audio assets +demo_add_asset_deps(test_shader_compilation shaders) # Only depends on shaders +demo_add_asset_deps(demo64k all) # Depends on all asset categories +``` + --- ## Conditional Inclusion @@ -107,12 +119,13 @@ This reduces parse time when building without tests/tools. ## Adding New Components ### New Effect -- Add sources to `cmake/DemoSourceLists.cmake` (GPU_SOURCES list) -- No other CMake changes needed +- Add sources to `cmake/DemoSourceLists.cmake` (`COMMON_GPU_EFFECTS` list) +- No other CMake changes needed (automatically included in headless and normal modes) ### New Test -- Add to `cmake/DemoTests.cmake` using `demo_add_test_with_deps()` -- Use LINK and DEPENDS parameters for libraries/assets +- Add to `cmake/DemoTests.cmake` using `add_demo_test()` +- Use `demo_add_asset_deps()` to specify asset category dependencies (e.g., `shaders`, `audio`) +- This enables granular rebuilds—only changed asset categories trigger test recompilation ### New Library - Add to `cmake/DemoLibraries.cmake` with appropriate dependencies @@ -132,6 +145,7 @@ This reduces parse time when building without tests/tools. 4. **Reusability:** Shared macros—eliminate 200+ lines of repetition 5. **Clarity:** Top-level CMakeLists.txt is 54-line roadmap 6. **Scalability:** Easy to add new tests/tools/libraries without bloating main file +7. **Granular Rebuilds:** Asset categories enable 3-5× faster incremental builds for typical changes --- diff --git a/doc/COMPLETED.md b/doc/COMPLETED.md index 55fac50..8d30cca 100644 --- a/doc/COMPLETED.md +++ b/doc/COMPLETED.md @@ -67,7 +67,7 @@ Use `read @doc/archive/FILENAME.md` to access archived documents. - **Changes**: - Added `get_common_uniforms()` helper to Effect base class - Refactored all render()/compute() signatures from 5 parameters to single `CommonPostProcessUniforms&` - - Fixed uninitialized uniforms in CircleMaskEffect and CNNEffect + - Fixed uninitialized uniforms in CircleMaskEffect and CNNv1Effect - Updated 19 effect implementations + headers - Fixed WGSL syntax error in FlashEffect (u.audio_intensity → audio_intensity) - **Impact**: @@ -93,7 +93,7 @@ Use `read @doc/archive/FILENAME.md` to access archived documents. - All 36 tests pass (100%) - Processes 64×64 test image successfully - Ready for ground-truth validation vs Python training script - - Documented in `doc/CNN_TEST_TOOL.md` + - Documented in `cnn_v1/docs/CNN_TEST_TOOL.md` ## Recently Completed (February 10, 2026) @@ -103,7 +103,7 @@ Use `read @doc/archive/FILENAME.md` to access archived documents. - Created `BindGroupLayoutBuilder` and `BindGroupBuilder` for declarative bind group creation - Created `RenderPipelineBuilder` to simplify pipeline setup with ShaderComposer integration - Created `SamplerCache` singleton to deduplicate sampler instances - - Refactored `post_process_helper.cc`, `cnn_effect.cc`, `rotating_cube_effect.cc` + - Refactored `post_process_helper.cc`, `cnn_v1_effect.cc`, `rotating_cube_effect.cc` - **Result**: - Bind group creation: 19 instances reduced from 14→4 lines each - Pipeline creation: 30-50 lines reduced to 8 lines diff --git a/doc/HOWTO.md b/doc/HOWTO.md index 0dc9ec7..4cafaa2 100644 --- a/doc/HOWTO.md +++ b/doc/HOWTO.md @@ -100,7 +100,7 @@ make run_util_tests # Utility tests Extracts patches at salient points, trains on center pixels only (matches WGSL sliding window): ```bash # Train with 32×32 patches at detected corners/edges -./training/train_cnn.py \ +./cnn_v1/training/train_cnn.py \ --input training/input/ --target training/output/ \ --patch-size 32 --patches-per-image 64 --detector harris \ --layers 3 --kernel_sizes 3,5,3 --epochs 5000 --batch_size 16 \ @@ -117,7 +117,7 @@ Extracts patches at salient points, trains on center pixels only (matches WGSL s ### Full-Image Processes entire image with sliding window (matches WGSL): ```bash -./training/train_cnn.py \ +./cnn_v1/training/train_cnn.py \ --input training/input/ --target training/output/ \ --layers 3 --kernel_sizes 3,5,3 --epochs 10000 --batch_size 8 \ --checkpoint-every 1000 @@ -126,10 +126,10 @@ Processes entire image with sliding window (matches WGSL): ### Export & Validation ```bash # Generate shaders from checkpoint -./training/train_cnn.py --export-only checkpoints/checkpoint_epoch_5000.pth +./cnn_v1/training/train_cnn.py --export-only checkpoints/checkpoint_epoch_5000.pth # Generate ground truth (sliding window, no tiling) -./training/train_cnn.py --infer input.png \ +./cnn_v1/training/train_cnn.py --infer input.png \ --export-only checkpoints/checkpoint_epoch_5000.pth \ --output ground_truth.png ``` @@ -145,31 +145,31 @@ Enhanced CNN with parametric static features (7D input: RGBD + UV + sin encoding **Complete Pipeline** (recommended): ```bash # Train → Export → Build → Validate (default config) -./scripts/train_cnn_v2_full.sh +./cnn_v2/scripts/train_cnn_v2_full.sh # Rapid debug (1 layer, 3×3, 5 epochs) -./scripts/train_cnn_v2_full.sh --num-layers 1 --kernel-sizes 3 --epochs 5 --output-weights test.bin +./cnn_v2/scripts/train_cnn_v2_full.sh --num-layers 1 --kernel-sizes 3 --epochs 5 --output-weights test.bin # Custom training parameters -./scripts/train_cnn_v2_full.sh --epochs 500 --batch-size 32 --checkpoint-every 100 +./cnn_v2/scripts/train_cnn_v2_full.sh --epochs 500 --batch-size 32 --checkpoint-every 100 # Custom architecture -./scripts/train_cnn_v2_full.sh --kernel-sizes 3,5,3 --num-layers 3 --mip-level 1 +./cnn_v2/scripts/train_cnn_v2_full.sh --kernel-sizes 3,5,3 --num-layers 3 --mip-level 1 # Custom output path -./scripts/train_cnn_v2_full.sh --output-weights workspaces/test/cnn_weights.bin +./cnn_v2/scripts/train_cnn_v2_full.sh --output-weights workspaces/test/cnn_weights.bin # Grayscale loss (compute loss on luminance instead of RGBA) -./scripts/train_cnn_v2_full.sh --grayscale-loss +./cnn_v2/scripts/train_cnn_v2_full.sh --grayscale-loss # Custom directories -./scripts/train_cnn_v2_full.sh --input training/input --target training/target_2 +./cnn_v2/scripts/train_cnn_v2_full.sh --input training/input --target training/target_2 # Full-image mode (instead of patch-based) -./scripts/train_cnn_v2_full.sh --full-image --image-size 256 +./cnn_v2/scripts/train_cnn_v2_full.sh --full-image --image-size 256 # See all options -./scripts/train_cnn_v2_full.sh --help +./cnn_v2/scripts/train_cnn_v2_full.sh --help ``` **Defaults:** 200 epochs, 3×3 kernels, 8→4→4 channels, batch-size 16, patch-based (8×8, harris detector). @@ -184,33 +184,33 @@ Enhanced CNN with parametric static features (7D input: RGBD + UV + sin encoding **Validation Only** (skip training): ```bash # Use latest checkpoint -./scripts/train_cnn_v2_full.sh --validate +./cnn_v2/scripts/train_cnn_v2_full.sh --validate # Use specific checkpoint -./scripts/train_cnn_v2_full.sh --validate checkpoints/checkpoint_epoch_50.pth +./cnn_v2/scripts/train_cnn_v2_full.sh --validate checkpoints/checkpoint_epoch_50.pth ``` **Manual Training:** ```bash # Default config -./training/train_cnn_v2.py \ +./cnn_v2/training/train_cnn_v2.py \ --input training/input/ --target training/target_2/ \ --epochs 100 --batch-size 16 --checkpoint-every 5 # Custom architecture (per-layer kernel sizes) -./training/train_cnn_v2.py \ +./cnn_v2/training/train_cnn_v2.py \ --input training/input/ --target training/target_2/ \ --kernel-sizes 1,3,5 \ --epochs 5000 --batch-size 16 # Mip-level for p0-p3 features (0=original, 1=half, 2=quarter, 3=eighth) -./training/train_cnn_v2.py \ +./cnn_v2/training/train_cnn_v2.py \ --input training/input/ --target training/target_2/ \ --mip-level 1 \ --epochs 100 --batch-size 16 # Grayscale loss (compute loss on luminance Y = 0.299*R + 0.587*G + 0.114*B) -./training/train_cnn_v2.py \ +./cnn_v2/training/train_cnn_v2.py \ --input training/input/ --target training/target_2/ \ --grayscale-loss \ --epochs 100 --batch-size 16 @@ -236,7 +236,7 @@ Use `--quiet` for streamlined output in scripts (used automatically by train_cnn ``` -**Validation:** Use HTML tool (`tools/cnn_v2_test/index.html`) for CNN v2 validation. See `doc/CNN_V2_WEB_TOOL.md`. +**Validation:** Use HTML tool (`cnn_v2/tools/cnn_v2_test/index.html`) for CNN v2 validation. See `cnn_v2/docs/CNN_V2_WEB_TOOL.md`. --- @@ -323,11 +323,11 @@ See `doc/ASSET_SYSTEM.md` and `doc/WORKSPACE_SYSTEM.md`. **Status:** - **CNN v2:** ✅ Fully functional, matches CNNv2Effect -- **CNN v1:** ⚠️ Produces incorrect output, use CNNEffect in demo for validation +- **CNN v1:** ⚠️ Produces incorrect output, use CNNv1Effect in demo for validation **Note:** `--weights` loads layer count and kernel sizes from the binary file, overriding `--layers` and forcing CNN v2. -See `doc/CNN_TEST_TOOL.md` for full documentation. +See `cnn_v1/docs/CNN_TEST_TOOL.md` for full documentation. --- 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/app/test_demo.cc b/src/app/test_demo.cc index 5775e74..ff2c105 100644 --- a/src/app/test_demo.cc +++ b/src/app/test_demo.cc @@ -20,8 +20,8 @@ extern float GetDemoDuration(); extern void LoadTimeline(MainSequence& main_seq, const GpuContext& ctx); // Inline peak meter effect for debugging audio-visual sync -#include "effects/cnn_effect.h" -#include "effects/cnn_v2_effect.h" +#include "../../cnn_v1/src/cnn_v1_effect.h" +#include "../../cnn_v2/src/cnn_v2_effect.h" #include "gpu/post_process_helper.h" #include "gpu/shader_composer.h" diff --git a/src/audio/audio.cc b/src/audio/audio.cc index f5bc4ab..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,21 +97,18 @@ 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; if (chunk_frames <= 0) return; - static int64_t g_total_render_calls = 0; - static int64_t g_total_frames_rendered = 0; - const int64_t frames_before = g_ring_buffer.get_total_written() / RING_BUFFER_CHANNELS; - // Keep rendering small chunks until buffer is full enough while (true) { // First, try to flush any pending samples from previous partial writes @@ -228,19 +225,6 @@ void audio_render_ahead(float music_time, float dt, float target_fill) { } } } - - // DEBUG: Track actual frames rendered vs expected - const int64_t frames_after = g_ring_buffer.get_total_written() / RING_BUFFER_CHANNELS; - const int64_t actual_rendered = frames_after - frames_before; - g_total_render_calls++; - g_total_frames_rendered += actual_rendered; - - if (g_total_render_calls % 600 == 0) { // Every 10 seconds at 60fps - const float expected_frames = g_total_render_calls * (float)(chunk_frames); - const float drift_ms = (expected_frames - g_total_frames_rendered) / RING_BUFFER_SAMPLE_RATE * 1000.0f; - printf("[RENDER_DRIFT] calls=%lld expect=%.1f actual=%lld drift=%.2fms\n", - g_total_render_calls, expected_frames, g_total_frames_rendered, drift_ms); - } } float audio_get_playback_time() { diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc index 00c31e9..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) @@ -324,14 +325,6 @@ void tracker_update(float music_time_sec, float dt_music_sec) { } } - // DEBUG: Track kick/snare timing for drift investigation - if (event.sample_id == 0 || event.sample_id == 1) { // Assuming kick=0, snare=1 - const char* name = (event.sample_id == 0) ? "KICK " : "SNARE"; - const float delta_ms = (event_music_time - music_time_sec) * 1000.0f; - printf("[DRIFT] %s: music=%.4f expect=%.4f delta=%.2fms offset=%d\n", - name, music_time_sec, event_music_time, delta_ms, sample_offset); - } - trigger_note_event(event, sample_offset, volume_mult); active.next_event_idx++; } 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..6b22f3f 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -18,8 +18,8 @@ // Individual Effect Headers #include "effects/chroma_aberration_effect.h" #include "effects/circle_mask_effect.h" -#include "effects/cnn_effect.h" -#include "effects/cnn_v2_effect.h" +#include "../../cnn_v1/src/cnn_v1_effect.h" +#include "../../cnn_v2/src/cnn_v2_effect.h" #include "effects/distort_effect.h" #include "effects/fade_effect.h" #include "effects/flash_cube_effect.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/audio/test_audio_engine.cc b/src/tests/audio/test_audio_engine.cc index 3f0ad4d..3d23a5c 100644 --- a/src/tests/audio/test_audio_engine.cc +++ b/src/tests/audio/test_audio_engine.cc @@ -65,20 +65,17 @@ void test_audio_engine_manual_resource_loading() { // Manually preload first few samples res_mgr->preload(0); res_mgr->preload(1); - res_mgr->preload(2); const int after_preload = res_mgr->get_loaded_count(); printf(" Samples loaded after manual preload: %d\n", after_preload); - assert(after_preload == 3); // Should have 3 samples loaded + assert(after_preload == 2); // Should have 2 samples loaded // Verify samples are accessible const Spectrogram* spec0 = res_mgr->get_spectrogram(0); const Spectrogram* spec1 = res_mgr->get_spectrogram(1); - const Spectrogram* spec2 = res_mgr->get_spectrogram(2); assert(spec0 != nullptr); assert(spec1 != nullptr); - assert(spec2 != nullptr); printf(" ✓ AudioEngine manual resource loading test passed\n"); } @@ -97,10 +94,9 @@ void test_audio_engine_reset() { // Manually load some samples res_mgr->preload(0); res_mgr->preload(1); - res_mgr->preload(2); const int loaded_before_reset = res_mgr->get_loaded_count(); - assert(loaded_before_reset == 3); + assert(loaded_before_reset == 2); // Reset engine fixture.engine().reset(); diff --git a/src/tests/gpu/test_demo_effects.cc b/src/tests/gpu/test_demo_effects.cc index ec78c10..8726e55 100644 --- a/src/tests/gpu/test_demo_effects.cc +++ b/src/tests/gpu/test_demo_effects.cc @@ -12,7 +12,7 @@ #include "../common/effect_test_helpers.h" #include "../common/webgpu_test_fixture.h" -#include "effects/cnn_effect.h" +#include "../../../cnn_v1/src/cnn_v1_effect.h" #include "gpu/demo_effects.h" #include "gpu/effect.h" #include <cassert> @@ -89,7 +89,7 @@ static void test_post_process_effects() { {"ThemeModulationEffect", std::make_shared<ThemeModulationEffect>(fixture.ctx())}, {"VignetteEffect", std::make_shared<VignetteEffect>(fixture.ctx())}, - {"CNNEffect", std::make_shared<CNNEffect>(fixture.ctx())}, + {"CNNv1Effect", std::make_shared<CNNv1Effect>(fixture.ctx())}, {"CNNv2Effect", std::make_shared<CNNv2Effect>(fixture.ctx())}, }; 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", diff --git a/tools/cnn_test.cc b/tools/cnn_test.cc index 7d060ae..137d235 100644 --- a/tools/cnn_test.cc +++ b/tools/cnn_test.cc @@ -5,7 +5,7 @@ #error "cnn_test requires STRIP_ALL=OFF (tool builds only)" #endif -#include "effects/cnn_effect.h" +#include "../cnn_v1/src/cnn_v1_effect.h" #include "generated/assets.h" #include "gpu/bind_group_builder.h" #include "gpu/gpu.h" diff --git a/tools/common/style.css b/tools/common/style.css new file mode 100644 index 0000000..1ba4bad --- /dev/null +++ b/tools/common/style.css @@ -0,0 +1,117 @@ +:root { + --bg-dark: #1e1e1e; + --bg-medium: #252526; + --bg-light: #3c3c3c; + --text-primary: #d4d4d4; + --text-muted: #858585; + --accent-blue: #0e639c; + --accent-blue-hover: #1177bb; + --accent-green: #4ec9b0; + --accent-orange: #ce9178; + --accent-red: #f48771; + --border-color: #858585; + --gap: 10px; + --radius: 4px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + overflow: hidden; +} + +button, .btn, .file-label { + background: var(--accent-blue); + color: white; + border: none; + padding: 10px 20px; + border-radius: var(--radius); + cursor: pointer; + font-size: 14px; + display: inline-block; + text-align: center; +} + +button:hover, .btn:hover, .file-label:hover { + background: var(--accent-blue-hover); +} + +button:disabled, .btn:disabled { + background: var(--bg-light); + cursor: not-allowed; +} + +input[type="file"] { + display: none; +} + +input, select { + background: var(--bg-light); + border: 1px solid var(--border-color); + border-radius: var(--radius); + color: var(--text-primary); + padding: 8px; + font-size: 14px; +} + +.container { + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; +} + +.header { + background: var(--bg-medium); + padding: 15px; + border-bottom: 1px solid var(--border-color); + display: flex; + align-items: center; + gap: 20px; + flex-wrap: wrap; +} + +h1 { + color: var(--accent-green); + font-size: 18px; + white-space: nowrap; +} + +.controls { + display: flex; + gap: var(--gap); + flex-wrap: wrap; + align-items: center; +} + +.panel { + background: var(--bg-medium); + border: 1px solid var(--border-color); + border-radius: var(--radius); + padding: 15px; +} + +.error-message { + background: #5a1d1d; + color: var(--accent-red); + padding: 10px; + border-radius: var(--radius); + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + margin: 10px 0; +} + +.success-message { + background: #1e5231; + color: #89d185; + padding: 10px; + border-radius: var(--radius); + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + margin: 10px 0; +} diff --git a/tools/shader_editor/index.html b/tools/shader_editor/index.html index bad0abb..d93a595 100644 --- a/tools/shader_editor/index.html +++ b/tools/shader_editor/index.html @@ -4,26 +4,8 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WGSL Shader Editor</title> + <link rel="stylesheet" href="../common/style.css"> <style> -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", monospace; - background: #1e1e1e; - color: #d4d4d4; - overflow: hidden; - height: 100vh; -} - -.container { - display: flex; - height: 100vh; -} - .preview-pane { flex: 0 0 57%; background: #252526; @@ -89,26 +71,13 @@ body { } .control-group button { - background: #0e639c; - color: #fff; - border: none; padding: 6px 12px; - border-radius: 3px; - cursor: pointer; font-size: 13px; } -.control-group button:hover { - background: #1177bb; -} - .control-group input[type="number"], .control-group select { - background: #3c3c3c; - color: #d4d4d4; - border: 1px solid #3e3e42; padding: 4px 8px; - border-radius: 3px; font-size: 13px; } @@ -153,19 +122,10 @@ body { } .editor-header button { - background: #0e639c; - color: #fff; - border: none; padding: 6px 12px; - border-radius: 3px; - cursor: pointer; font-size: 13px; } -.editor-header button:hover { - background: #1177bb; -} - .editor-container { flex: 1; position: relative; diff --git a/tools/spectral_editor/index.html b/tools/spectral_editor/index.html index 75658ae..2d5f3e5 100644 --- a/tools/spectral_editor/index.html +++ b/tools/spectral_editor/index.html @@ -4,6 +4,7 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Spectral Brush Editor</title> + <link rel="stylesheet" href="../common/style.css"> <link rel="stylesheet" href="style.css"> </head> <body> diff --git a/tools/spectral_editor/style.css b/tools/spectral_editor/style.css index 48f7463..87fb54e 100644 --- a/tools/spectral_editor/style.css +++ b/tools/spectral_editor/style.css @@ -1,18 +1,4 @@ -/* Spectral Brush Editor Styles */ - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: #1e1e1e; - color: #d4d4d4; - overflow: hidden; - height: 100vh; -} +/* Spectral Brush Editor Specific Styles */ #app { display: flex; @@ -20,41 +6,12 @@ body { height: 100vh; } -/* Header */ -header { - background: #252526; - padding: 12px 20px; - border-bottom: 1px solid #3e3e42; - display: flex; - justify-content: space-between; - align-items: center; -} - -header h1 { - font-size: 18px; - font-weight: 600; - color: #cccccc; -} - -.header-controls { - display: flex; - align-items: center; - gap: 15px; -} - -.file-info { - font-size: 13px; - color: #858585; -} - -/* Main content area */ .main-content { display: flex; flex: 1; overflow: hidden; } -/* Canvas container (80% width) */ .canvas-container { flex: 1; position: relative; @@ -89,7 +46,6 @@ header h1 { display: none; } -/* Mini spectrum viewer (bottom-right overlay) */ .spectrum-viewer { position: absolute; bottom: 10px; @@ -99,12 +55,8 @@ header h1 { background: rgba(30, 30, 30, 0.9); border: 1px solid #3e3e42; border-radius: 3px; - display: block; /* Always visible */ - pointer-events: none; /* Don't interfere with mouse events */ -} - -.spectrum-viewer.active { - display: block; /* Keep for backward compatibility */ + display: block; + pointer-events: none; } #spectrumCanvas { @@ -123,7 +75,6 @@ header h1 { color: #858585; } -/* Toolbar (20% width) */ .toolbar { width: 250px; background: #252526; @@ -155,16 +106,6 @@ header h1 { transition: background 0.2s; } -.btn-toolbar:hover { - background: #1177bb; -} - -.btn-toolbar:disabled { - background: #3e3e42; - color: #858585; - cursor: not-allowed; -} - .btn-toolbar.btn-danger { background: #a82d2d; } @@ -199,7 +140,6 @@ header h1 { border-color: #0e639c; } -/* Point info panel */ .point-info { margin-top: 10px; padding: 10px; @@ -224,7 +164,6 @@ header h1 { font-family: monospace; } -/* Control panel (bottom) */ .control-panel { background: #252526; border-top: 1px solid #3e3e42; @@ -314,16 +253,6 @@ header h1 { transition: background 0.2s; } -.btn-playback:hover:not(:disabled) { - background: #1177bb; -} - -.btn-playback:disabled { - background: #3e3e42; - color: #858585; - cursor: not-allowed; -} - .btn-playback kbd { background: rgba(255, 255, 255, 0.1); padding: 2px 5px; @@ -331,7 +260,6 @@ header h1 { font-size: 11px; } -/* Action bar (bottom) */ .action-bar { background: #2d2d30; border-top: 1px solid #3e3e42; @@ -365,11 +293,6 @@ header h1 { border-color: #0e639c; } -.btn-action:disabled { - color: #858585; - cursor: not-allowed; -} - .btn-primary { padding: 6px 16px; background: #0e639c; @@ -381,17 +304,11 @@ header h1 { transition: background 0.2s; } -.btn-primary:hover { - background: #1177bb; -} - -/* Icon styling */ .icon { font-size: 14px; line-height: 1; } -/* Modal */ .modal { position: fixed; z-index: 1000; @@ -490,7 +407,6 @@ header h1 { color: #cccccc; } -/* Scrollbar styling */ ::-webkit-scrollbar { width: 10px; height: 10px; @@ -509,7 +425,6 @@ header h1 { background: #4e4e52; } -/* Waveform intensity viewer */ .waveform-container { position: relative; height: 120px; @@ -570,24 +485,9 @@ header h1 { transition: background 0.2s; } -.btn-copy:hover, .btn-snap:hover { - background: #1177bb; -} - -.btn-copy:active, .btn-snap:active { - background: #0d5a8f; -} - .spectrogram-wrapper { flex: 1; position: relative; overflow: hidden; z-index: 1; } - -#spectrogramCanvas { - width: 100%; - height: 100%; - display: block; - cursor: crosshair; -} diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html index d1c759d..c5e0264 100644 --- a/tools/timeline_editor/index.html +++ b/tools/timeline_editor/index.html @@ -4,107 +4,466 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Timeline Editor - timeline.seq</title> + <link rel="stylesheet" href="../common/style.css"> <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' fill='%231e1e1e'/><rect x='10' y='30' width='15' height='40' fill='%234ec9b0'/><rect x='30' y='20' width='15' height='60' fill='%234ec9b0'/><rect x='50' y='35' width='15' height='30' fill='%234ec9b0'/><rect x='70' y='15' width='15' height='70' fill='%234ec9b0'/></svg>"> <style> - :root { - --bg-dark: #1e1e1e; - --bg-medium: #252526; - --bg-light: #3c3c3c; - --text-primary: #d4d4d4; - --text-muted: #858585; - --accent-blue: #0e639c; - --accent-blue-hover: #1177bb; - --accent-green: #4ec9b0; - --accent-orange: #ce9178; - --accent-red: #f48771; - --border-color: #858585; - --gap: 10px; - --radius: 4px; + body { + padding: 20px; + min-height: 100vh; } - * { margin: 0; padding: 0; box-sizing: border-box; } - body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: var(--bg-dark); color: var(--text-primary); padding: 20px; min-height: 100vh; } - .container { max-width: 100%; width: 100%; margin: 0 auto; } + .container { + max-width: 100%; + width: 100%; + margin: 0 auto; + } + + header { + background: var(--bg-medium); + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + flex-wrap: wrap; + } + + .zoom-controls { + display: flex; + gap: var(--gap); + flex-wrap: wrap; + align-items: center; + margin-bottom: var(--gap); + } + + .checkbox-label { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; + } - header { background: var(--bg-medium); padding: 20px; border-radius: 8px; margin-bottom: 20px; display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; } - h1 { color: var(--accent-green); white-space: nowrap; } - .controls { display: flex; gap: var(--gap); flex-wrap: wrap; align-items: center; } - .zoom-controls { display: flex; gap: var(--gap); flex-wrap: wrap; align-items: center; margin-bottom: var(--gap); } + .checkbox-label input[type="checkbox"] { + cursor: pointer; + } - button, .file-label { background: var(--accent-blue); color: white; border: none; padding: 10px 20px; border-radius: var(--radius); cursor: pointer; font-size: 14px; display: inline-block; } - button:hover, .file-label:hover { background: var(--accent-blue-hover); } - button:disabled { background: var(--bg-light); cursor: not-allowed; } - input[type="file"] { display: none; } + .timeline-container { + background: var(--bg-medium); + border-radius: 8px; + position: relative; + height: calc(100vh - 280px); + min-height: 500px; + display: flex; + flex-direction: column; + } - .checkbox-label { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; } - .checkbox-label input[type="checkbox"] { cursor: pointer; } + .timeline-content { + flex: 1; + overflow: auto; + position: relative; + padding: 0 20px 20px 20px; + scrollbar-width: none; + -ms-overflow-style: none; + } - .timeline-container { background: var(--bg-medium); border-radius: 8px; position: relative; height: calc(100vh - 280px); min-height: 500px; display: flex; flex-direction: column; } - .timeline-content { flex: 1; overflow: auto; position: relative; padding: 0 20px 20px 20px; scrollbar-width: none; -ms-overflow-style: none; } - .timeline-content::-webkit-scrollbar { display: none; } - .timeline { position: relative; min-height: 100%; } + .timeline-content::-webkit-scrollbar { + display: none; + } - .sticky-header { position: sticky; top: 0; background: var(--bg-medium); z-index: 100; padding: 20px 20px 10px 20px; border-bottom: 2px solid var(--bg-light); flex-shrink: 0; } - .waveform-container { position: relative; height: 80px; overflow: hidden; background: rgba(0, 0, 0, 0.3); border-radius: var(--radius); cursor: crosshair; } - #cpuLoadCanvas { position: absolute; left: 0; bottom: 0; height: 10px; display: block; z-index: 1; } - #waveformCanvas { position: absolute; left: 0; top: 0; height: 80px; display: block; z-index: 2; } - .waveform-cursor { position: absolute; top: 0; bottom: 0; width: 1px; background: rgba(78, 201, 176, 0.6); pointer-events: none; z-index: 3; display: none; } - .waveform-tooltip { position: absolute; background: rgba(30, 30, 30, 0.95); color: var(--text-primary); padding: 6px 10px; border-radius: 4px; font-size: 12px; pointer-events: none; z-index: 4; display: none; white-space: nowrap; border: 1px solid var(--border-color); box-shadow: 0 2px 8px rgba(0,0,0,0.3); } + .timeline { + position: relative; + min-height: 100%; + } - .playback-indicator { position: absolute; top: 0; bottom: 0; left: 20px; width: 2px; background: var(--accent-red); box-shadow: 0 0 4px rgba(244, 135, 113, 0.8); pointer-events: none; z-index: 110; display: none; } + .sticky-header { + position: sticky; + top: 0; + background: var(--bg-medium); + z-index: 100; + padding: 20px 20px 10px 20px; + border-bottom: 2px solid var(--bg-light); + flex-shrink: 0; + } - .time-markers { position: relative; height: 30px; margin-top: var(--gap); border-bottom: 1px solid var(--bg-light); } - .time-marker { position: absolute; top: 0; font-size: 12px; color: var(--text-muted); } - .time-marker::before { content: ''; position: absolute; left: 0; top: 20px; width: 1px; height: 10px; background: var(--bg-light); } - .time-marker::after { content: ''; position: absolute; left: 0; top: 30px; width: 1px; height: 10000px; background: rgba(60, 60, 60, 0.2); pointer-events: none; } + .waveform-container { + position: relative; + height: 80px; + overflow: hidden; + background: rgba(0, 0, 0, 0.5); + border-radius: var(--radius); + cursor: crosshair; + } + + #cpuLoadCanvas { + position: absolute; + left: 0; + bottom: 0; + height: 10px; + display: block; + z-index: 1; + } + + #waveformCanvas { + position: absolute; + left: 0; + top: 0; + height: 80px; + display: block; + z-index: 2; + } + + .waveform-cursor { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: rgba(78, 201, 176, 0.6); + pointer-events: none; + z-index: 3; + display: none; + } + + .waveform-tooltip { + position: absolute; + background: rgba(30, 30, 30, 0.95); + color: var(--text-primary); + padding: 6px 10px; + border-radius: 4px; + font-size: 12px; + pointer-events: none; + z-index: 4; + display: none; + white-space: nowrap; + border: 1px solid var(--border-color); + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + } + + .playback-indicator { + position: absolute; + top: 0; + bottom: 0; + left: 20px; + width: 2px; + background: var(--accent-red); + box-shadow: 0 0 4px rgba(244, 135, 113, 0.8); + pointer-events: none; + z-index: 110; + display: none; + } + + .time-markers { + position: relative; + height: 30px; + margin-top: var(--gap); + border-bottom: 1px solid var(--bg-light); + } + + .time-marker { + position: absolute; + top: 0; + font-size: 12px; + color: var(--text-muted); + } + + .time-marker::before { + content: ''; + position: absolute; + left: 0; + top: 20px; + width: 1px; + height: 10px; + background: var(--bg-light); + } + + .time-marker::after { + content: ''; + position: absolute; + left: 0; + top: 30px; + width: 1px; + height: 10000px; + background: rgba(100, 100, 60, 0.9); + pointer-events: none; + } + + .sequence { + position: absolute; + background: #264f78; + border: 2px solid var(--accent-blue); + border-radius: var(--radius); + padding: 8px; + cursor: move; + min-height: 40px; + transition: box-shadow 0.2s; + } + + .sequence:hover { + box-shadow: 0 0 10px rgba(14, 99, 156, 0.5); + } + + .sequence.selected { + border-color: var(--accent-green); + box-shadow: 0 0 10px rgba(78, 201, 176, 0.5); + } + + .sequence.collapsed { + overflow: hidden !important; + background: #1a3a4a !important; + } + + .sequence.collapsed .sequence-name { + display: none !important; + } + + .sequence.active-playing { + border-color: var(--accent-green); + background: #2a5f4a; + } + + .sequence.active-flash { + animation: sequenceFlash 0.6s ease-out; + } - .sequence { position: absolute; background: #264f78; border: 2px solid var(--accent-blue); border-radius: var(--radius); padding: 8px; cursor: move; min-height: 40px; transition: box-shadow 0.2s; } - .sequence:hover { box-shadow: 0 0 10px rgba(14, 99, 156, 0.5); } - .sequence.selected { border-color: var(--accent-green); box-shadow: 0 0 10px rgba(78, 201, 176, 0.5); } - .sequence.collapsed { overflow: hidden !important; background: #1a3a4a !important; } - .sequence.collapsed .sequence-name { display: none !important; } - .sequence.active-playing { border-color: var(--accent-green); background: #2a5f4a; } - .sequence.active-flash { animation: sequenceFlash 0.6s ease-out; } @keyframes sequenceFlash { - 0% { box-shadow: 0 0 20px rgba(78, 201, 176, 0.8); border-color: var(--accent-green); } - 100% { box-shadow: 0 0 10px rgba(14, 99, 156, 0.5); border-color: var(--accent-blue); } + 0% { + box-shadow: 0 0 20px rgba(78, 201, 176, 0.8); + border-color: var(--accent-green); + } + 100% { + box-shadow: 0 0 10px rgba(14, 99, 156, 0.5); + border-color: var(--accent-blue); + } } - .sequence-header { position: absolute; top: 0; left: 0; right: 0; padding: 8px; z-index: 5; cursor: move; user-select: none; } - .sequence-header-name { font-size: 14px; font-weight: bold; color: #ffffff; } - .sequence:not(.collapsed) .sequence-header-name { display: none; } - .sequence-name { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24px; font-weight: bold; color: #ffffff; text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.9), -1px -1px 4px rgba(0, 0, 0, 0.7); pointer-events: none; white-space: nowrap; opacity: 1; transition: opacity 0.3s ease; z-index: 10; } - .sequence.hovered .sequence-name { opacity: 0; } + .sequence-header { + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 8px; + z-index: 5; + cursor: move; + user-select: none; + } - .effect { position: absolute; background: #3a3d41; border: 1px solid var(--border-color); border-radius: 3px; padding: 4px 8px; cursor: move; font-size: 11px; transition: box-shadow 0.2s; display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - .effect:hover { box-shadow: 0 0 8px rgba(133, 133, 133, 0.5); background: #45484d; } - .effect.selected { border-color: var(--accent-orange); box-shadow: 0 0 8px rgba(206, 145, 120, 0.5); } - .effect.conflict { background: #4a1d1d; border-color: var(--accent-red); box-shadow: 0 0 8px rgba(244, 135, 113, 0.6); } - .effect.conflict:hover { background: #5a2424; } - .effect-handle { position: absolute; top: 0; width: 6px; height: 100%; background: rgba(78, 201, 176, 0.8); cursor: ew-resize; display: none; z-index: 10; } - .effect.selected .effect-handle { display: block; } - .effect-handle.left { left: 0; border-radius: 3px 0 0 3px; } - .effect-handle.right { right: 0; border-radius: 0 3px 3px 0; } - .effect-handle:hover { background: var(--accent-green); width: 8px; } + .sequence-header-name { + font-size: 14px; + font-weight: bold; + color: #ffffff; + } - .properties-panel { position: fixed; bottom: 20px; left: 20px; width: 350px; max-height: 80vh; background: var(--bg-medium); padding: 15px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); z-index: 1000; overflow-y: auto; transition: transform 0.3s ease; } - .properties-panel.collapsed { transform: translateY(calc(100% + 40px)); } - .panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid var(--bg-light); } - .panel-header h2 { margin: 0; color: var(--accent-green); font-size: 16px; } - .panel-toggle { background: transparent; border: 1px solid var(--border-color); color: var(--text-primary); padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 12px; } - .panel-toggle:hover { background: var(--bg-light); } - .panel-collapse-btn { position: fixed; bottom: 20px; left: 20px; background: var(--bg-medium); border: 1px solid var(--border-color); color: var(--text-primary); padding: 8px 12px; border-radius: var(--radius); cursor: pointer; z-index: 999; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); display: none; } - .panel-collapse-btn:hover { background: var(--bg-light); } - .panel-collapse-btn.visible { display: block; } + .sequence:not(.collapsed) .sequence-header-name { + display: none; + } - .property-group { margin-bottom: 15px; } - .property-group label { display: block; margin-bottom: 5px; color: var(--text-muted); font-size: 14px; } - .property-group input, .property-group select { width: 100%; padding: 8px; background: var(--bg-light); border: 1px solid var(--border-color); border-radius: var(--radius); color: var(--text-primary); font-size: 14px; } + .sequence-name { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 24px; + font-weight: bold; + color: #ffffff; + text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.9), -1px -1px 4px rgba(0, 0, 0, 0.7); + pointer-events: none; + white-space: nowrap; + opacity: 1; + transition: opacity 0.3s ease; + z-index: 10; + } - .stats { background: var(--bg-dark); padding: 10px; border-radius: var(--radius); margin-top: 10px; font-size: 12px; color: var(--text-muted); } - #messageArea { position: fixed; top: 80px; right: 20px; z-index: 2000; max-width: 400px; } - .error { background: #5a1d1d; color: var(--accent-red); padding: 10px; border-radius: var(--radius); box-shadow: 0 2px 8px rgba(0,0,0,0.3); } - .success { background: #1e5231; color: #89d185; padding: 10px; border-radius: var(--radius); box-shadow: 0 2px 8px rgba(0,0,0,0.3); } + .sequence.hovered .sequence-name { + opacity: 0; + } + + .effect { + position: absolute; + background: #3a3d41; + border: 1px solid var(--border-color); + border-radius: 3px; + padding: 4px 8px; + cursor: move; + font-size: 11px; + transition: box-shadow 0.2s; + display: flex; + align-items: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .effect:hover { + box-shadow: 0 0 8px rgba(133, 133, 133, 0.5); + background: #45484d; + } + + .effect.selected { + border-color: var(--accent-orange); + box-shadow: 0 0 8px rgba(206, 145, 120, 0.5); + } + + .effect.conflict { + background: #4a1d1d; + border-color: var(--accent-red); + box-shadow: 0 0 8px rgba(244, 135, 113, 0.6); + } + + .effect.conflict:hover { + background: #5a2424; + } + + .effect-handle { + position: absolute; + top: 0; + width: 6px; + height: 100%; + background: rgba(78, 201, 176, 0.8); + cursor: ew-resize; + display: none; + z-index: 10; + } + + .effect.selected .effect-handle { + display: block; + } + + .effect-handle.left { + left: 0; + border-radius: 3px 0 0 3px; + } + + .effect-handle.right { + right: 0; + border-radius: 0 3px 3px 0; + } + + .effect-handle:hover { + background: var(--accent-green); + width: 8px; + } + + .properties-panel { + position: fixed; + bottom: 20px; + left: 20px; + width: 350px; + max-height: 80vh; + background: var(--bg-medium); + padding: 15px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); + z-index: 1000; + overflow-y: auto; + transition: transform 0.3s ease; + } + + .properties-panel.collapsed { + transform: translateY(calc(100% + 40px)); + } + + .panel-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid var(--bg-light); + } + + .panel-header h2 { + margin: 0; + color: var(--accent-green); + font-size: 16px; + } + + .panel-toggle { + background: transparent; + border: 1px solid var(--border-color); + color: var(--text-primary); + padding: 4px 8px; + border-radius: 3px; + cursor: pointer; + font-size: 12px; + } + + .panel-toggle:hover { + background: var(--bg-light); + } + + .panel-collapse-btn { + position: fixed; + bottom: 20px; + left: 20px; + background: var(--bg-medium); + border: 1px solid var(--border-color); + color: var(--text-primary); + padding: 8px 12px; + border-radius: var(--radius); + cursor: pointer; + z-index: 999; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + display: none; + } + + .panel-collapse-btn:hover { + background: var(--bg-light); + } + + .panel-collapse-btn.visible { + display: block; + } + + .property-group { + margin-bottom: 15px; + } + + .property-group label { + display: block; + margin-bottom: 5px; + color: var(--text-muted); + font-size: 14px; + } + + .property-group input, + .property-group select { + width: 100%; + } + + .stats { + background: var(--bg-dark); + padding: 10px; + border-radius: var(--radius); + margin-top: 10px; + font-size: 12px; + color: var(--text-muted); + } + + #messageArea { + position: fixed; + top: 80px; + right: 20px; + z-index: 2000; + max-width: 400px; + } + + .error { + background: #5a1d1d; + color: var(--accent-red); + padding: 10px; + border-radius: var(--radius); + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + } + + .success { + background: #1e5231; + color: #89d185; + padding: 10px; + border-radius: var(--radius); + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + } </style> </head> <body> @@ -151,7 +510,7 @@ <div id="messageArea"></div> - <div class="timeline-container"> + <div class="timeline-container" id="timelineContainer"> <div class="playback-indicator" id="playbackIndicator"></div> <div class="sticky-header"> <div class="waveform-container" id="waveformContainer"> @@ -220,6 +579,7 @@ // DOM const dom = { timeline: document.getElementById('timeline'), + timelineContainer: document.getElementById('timelineContainer'), timelineContent: document.getElementById('timelineContent'), fileInput: document.getElementById('fileInput'), saveBtn: document.getElementById('saveBtn'), @@ -503,7 +863,6 @@ seqDiv.addEventListener('mousedown', e => startDrag(e, 'sequence', seqIndex)); seqDiv.addEventListener('click', e => { e.stopPropagation(); selectItem('sequence', seqIndex); }); seqDiv.addEventListener('dblclick', e => { e.stopPropagation(); e.preventDefault(); seq._collapsed = !seq._collapsed; renderTimeline(); }); - seqDiv.addEventListener('wheel', e => viewportController.handleWheel(e), { passive: false }); dom.timeline.appendChild(seqDiv); if (!seq._collapsed) { const conflicts = detectConflicts(seq); @@ -534,7 +893,6 @@ if (!e.target.classList.contains('effect-handle')) { e.stopPropagation(); startDrag(e, 'effect', seqIndex, effectIndex); } }); effectDiv.addEventListener('click', e => { e.stopPropagation(); selectItem('effect', seqIndex, effectIndex); }); - effectDiv.addEventListener('wheel', e => viewportController.handleWheel(e), { passive: false }); dom.timeline.appendChild(effectDiv); }); } diff --git a/tools/timeline_editor/timeline-playback.js b/tools/timeline_editor/timeline-playback.js index 8c84877..a1c50ab 100644 --- a/tools/timeline_editor/timeline-playback.js +++ b/tools/timeline_editor/timeline-playback.js @@ -168,7 +168,7 @@ export class PlaybackController { ctx.stroke(); // Beat markers - ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)'; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.50)'; ctx.lineWidth = 1; for (let beat = 0; beat <= maxTimeBeats; beat++) { const x = beat * this.state.pixelsPerBeat; diff --git a/tools/timeline_editor/timeline-viewport.js b/tools/timeline_editor/timeline-viewport.js index 196368e..dcedb45 100644 --- a/tools/timeline_editor/timeline-viewport.js +++ b/tools/timeline_editor/timeline-viewport.js @@ -22,17 +22,15 @@ export class ViewportController { // Scroll sync this.dom.timelineContent.addEventListener('scroll', () => this.handleScroll()); - // Wheel handling + // Wheel handling - capture at container level to override all child elements const wheelHandler = e => this.handleWheel(e); - this.dom.timelineContent.addEventListener('wheel', wheelHandler, { passive: false }); - this.dom.waveformContainer.addEventListener('wheel', wheelHandler, { passive: false }); + this.dom.timelineContainer.addEventListener('wheel', wheelHandler, { passive: false, capture: true }); - // Prevent wheel bubbling from UI containers + // Prevent wheel bubbling from UI containers outside timeline document.querySelector('header').addEventListener('wheel', e => e.stopPropagation()); this.dom.propertiesPanel.addEventListener('wheel', e => e.stopPropagation()); document.querySelector('.zoom-controls').addEventListener('wheel', e => e.stopPropagation()); document.querySelector('.stats').addEventListener('wheel', e => e.stopPropagation()); - document.getElementById('timeMarkers').addEventListener('wheel', e => e.stopPropagation()); // Waveform hover tracking this.dom.waveformContainer.addEventListener('mouseenter', () => this.showWaveformCursor()); @@ -148,7 +146,7 @@ export class ViewportController { const rect = this.dom.waveformContainer.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const scrollLeft = this.dom.timelineContent.scrollLeft; - const timeBeats = (scrollLeft + mouseX - this.TIMELINE_LEFT_PADDING) / this.state.pixelsPerBeat; + const timeBeats = (scrollLeft + mouseX) / this.state.pixelsPerBeat; const timeSeconds = timeBeats * this.state.secondsPerBeat; // Position cursor diff --git a/tools/track_visualizer/index.html b/tools/track_visualizer/index.html index 4a613ec..d1e7480 100644 --- a/tools/track_visualizer/index.html +++ b/tools/track_visualizer/index.html @@ -4,18 +4,8 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Music Track Visualizer</title> + <link rel="stylesheet" href="../common/style.css"> <style> - * { - margin: 0; - padding: 0; - box-sizing: border-box; - } - body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: #1e1e1e; - color: #d4d4d4; - overflow: hidden; - } #controls { padding: 15px; background: #2d2d2d; @@ -27,16 +17,8 @@ } button, input[type="file"] { padding: 8px 16px; - background: #0e639c; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; font-size: 14px; } - button:hover { - background: #1177bb; - } input[type="file"] { padding: 6px 12px; } @@ -58,7 +40,6 @@ width: 100%; height: calc(100vh - 70px); overflow: auto; - background: #1e1e1e; } #timeline-canvas { display: block; diff --git a/training/README.md b/training/README.md index e78b471..bddf4d5 100644 --- a/training/README.md +++ b/training/README.md @@ -174,6 +174,6 @@ pip install torch torchvision pillow opencv-python numpy ## References -- **CNN Effect:** `doc/CNN_EFFECT.md` +- **CNN Effect:** `cnn_v1/docs/CNN_V1_EFFECT.md` - **Timeline:** `doc/SEQUENCE.md` - **HOWTO:** `doc/HOWTO.md` diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt index 972cc8b..189e965 100644 --- a/workspaces/main/assets.txt +++ b/workspaces/main/assets.txt @@ -37,16 +37,16 @@ SHADER_PASSTHROUGH, NONE, ../../common/shaders/passthrough.wgsl, "Passthrough Sh SHADER_ELLIPSE, NONE, shaders/ellipse.wgsl, "Ellipse Shader" SHADER_PARTICLE_SPRAY_COMPUTE, NONE, shaders/particle_spray_compute.wgsl, "Particle Spray Compute" SHADER_GAUSSIAN_BLUR, NONE, shaders/gaussian_blur.wgsl, "Gaussian Blur Shader" -SHADER_CNN_ACTIVATION, NONE, shaders/cnn/cnn_activation.wgsl, "CNN Activation Functions" -SHADER_CNN_CONV1X1, NONE, shaders/cnn/cnn_conv1x1.wgsl, "CNN 1x1 Convolution" -SHADER_CNN_CONV3X3, NONE, shaders/cnn/cnn_conv3x3.wgsl, "CNN 3x3 Convolution" -SHADER_CNN_CONV5X5, NONE, shaders/cnn/cnn_conv5x5.wgsl, "CNN 5x5 Convolution" -SHADER_CNN_CONV7X7, NONE, shaders/cnn/cnn_conv7x7.wgsl, "CNN 7x7 Convolution" -SHADER_CNN_WEIGHTS, NONE, shaders/cnn/cnn_weights_generated.wgsl, "CNN Weights (Generated)" -SHADER_CNN_LAYER, NONE, shaders/cnn/cnn_layer.wgsl, "CNN Layer Shader" -SHADER_CNN_V2_STATIC, NONE, shaders/cnn_v2/cnn_v2_static.wgsl, "CNN v2 Static Features" -SHADER_CNN_V2_COMPUTE, NONE, shaders/cnn_v2/cnn_v2_compute.wgsl, "CNN v2 Compute (Storage Buffer)" -WEIGHTS_CNN_V2, NONE, weights/cnn_v2_weights.bin, "CNN v2 Binary Weights" +SHADER_CNN_ACTIVATION, NONE, ../../cnn_v1/shaders/cnn_activation.wgsl, "CNN Activation Functions" +SHADER_CNN_CONV1X1, NONE, ../../cnn_v1/shaders/cnn_conv1x1.wgsl, "CNN 1x1 Convolution" +SHADER_CNN_CONV3X3, NONE, ../../cnn_v1/shaders/cnn_conv3x3.wgsl, "CNN 3x3 Convolution" +SHADER_CNN_CONV5X5, NONE, ../../cnn_v1/shaders/cnn_conv5x5.wgsl, "CNN 5x5 Convolution" +SHADER_CNN_CONV7X7, NONE, ../../cnn_v1/shaders/cnn_conv7x7.wgsl, "CNN 7x7 Convolution" +SHADER_CNN_WEIGHTS, NONE, ../../cnn_v1/shaders/cnn_weights_generated.wgsl, "CNN Weights (Generated)" +SHADER_CNN_LAYER, NONE, ../../cnn_v1/shaders/cnn_layer.wgsl, "CNN Layer Shader" +SHADER_CNN_V2_STATIC, NONE, ../../cnn_v2/shaders/cnn_v2_static.wgsl, "CNN v2 Static Features" +SHADER_CNN_V2_COMPUTE, NONE, ../../cnn_v2/shaders/cnn_v2_compute.wgsl, "CNN v2 Compute (Storage Buffer)" +WEIGHTS_CNN_V2, NONE, ../../cnn_v2/weights/cnn_v2_weights.bin, "CNN v2 Binary Weights" SHADER_SOLARIZE, NONE, shaders/solarize.wgsl, "Solarize Shader" SHADER_DISTORT, NONE, shaders/distort.wgsl, "Distort Shader" SHADER_CHROMA_ABERRATION, NONE, shaders/chroma_aberration.wgsl, "Chroma Aberration Shader" diff --git a/workspaces/main/timeline.seq b/workspaces/main/timeline.seq index 3e9052b..b4663bb 100644 --- a/workspaces/main/timeline.seq +++ b/workspaces/main/timeline.seq @@ -3,100 +3,95 @@ # BPM 90 SEQUENCE 0.00 0 - EFFECT - FlashCubeEffect 0.00 4.88 - EFFECT + FlashEffect 0.00 2.00 color=1.0,0.5,0.5 decay=0.95 - EFFECT + FadeEffect 0.20 2.00 - EFFECT + SolarizeEffect 0.00 4.00 - EFFECT + VignetteEffect 0.00 5.00 radius=0.6 softness=0.1 + EFFECT - FlashCubeEffect 0.00 4.00 +# EFFECT + FlashEffect 0.00 2.00 color=1.0,0.5,0.5 decay=0.95 +# EFFECT + FadeEffect 2.00 4.00 +# EFFECT + SolarizeEffect 0.00 4.00 + EFFECT + VignetteEffect 0.00 4.00 radius=0.6 softness=0.1 -SEQUENCE 5.00 0 "rotating cube" - EFFECT + CircleMaskEffect 0.00 8.00 0.50 - EFFECT + RotatingCubeEffect 0.00 8.00 - EFFECT + GaussianBlurEffect 2.00 4.00 strength=1.0 - EFFECT + GaussianBlurEffect 6.00 8.00 strength=2.0 +SEQUENCE 4.00 0 "rotating cube" + EFFECT + CircleMaskEffect 0.00 4.00 0.50 + EFFECT + RotatingCubeEffect 0.00 4.00 + EFFECT + GaussianBlurEffect 1.00 4.00 strength=1.0 -SEQUENCE 12.00 0 - EFFECT - FlashCubeEffect 0.22 2.90 +SEQUENCE 8.00 0 "Flash Cube" + EFFECT - FlashCubeEffect 0.00 4.02 EFFECT + FlashEffect 0.00 0.40 -SEQUENCE 14.00 1 "spray" - EFFECT + ParticleSprayEffect 0.00 4.00 - EFFECT + ParticlesEffect 0.00 6.00 +SEQUENCE 12.00 1 "spray" + EFFECT + ParticleSprayEffect 0.00 2.00 + EFFECT + ParticlesEffect 2.00 4.00 EFFECT = GaussianBlurEffect 0.00 4.00 strength=3.0 -SEQUENCE 17.00 2 "Hybrid3D" +SEQUENCE 16.00 2 "Hybrid3D + CNN" EFFECT + ThemeModulationEffect 0.00 4.00 - EFFECT + HeptagonEffect 0.40 4.00 - EFFECT + ParticleSprayEffect 0.00 4.00 - EFFECT = ParticlesEffect 0.00 4.00 + EFFECT + HeptagonEffect 0.00 4.00 + EFFECT + ParticleSprayEffect 0.00 2.00 + EFFECT = ParticlesEffect 2.00 4.00 EFFECT + Hybrid3DEffect 0.00 4.00 - EFFECT + GaussianBlurEffect 0.00 4.00 - EFFECT + CNNEffect 0.00 4.00 layers=3 blend=.9 + EFFECT + CNNv1Effect 0.00 4.00 layers=3 blend=.9 -SEQUENCE 21.00 0 "CNN effect" - EFFECT + HeptagonEffect 0.00 22.00 - EFFECT + Scene1Effect 0.00 24.00 - EFFECT + CNNEffect 2.00 24.00 layers=3 blend=.5 +SEQUENCE 20.00 0 "CNN effect" + EFFECT + HeptagonEffect 0.00 8.00 + EFFECT + Scene1Effect 0.00 8.00 + EFFECT + CNNv1Effect 6.00 8.00 layers=3 blend=.5 -SEQUENCE 44.00 0 "buggy" - EFFECT + HeptagonEffect 0.00 0.40 - EFFECT + FadeEffect 0.22 2.02 +SEQUENCE 28.00 0 "buggy" + EFFECT + HeptagonEffect 0.00 2.00 + EFFECT + FadeEffect 0.00 2.00 -SEQUENCE 44.00 3 "Seq-8" - EFFECT + ThemeModulationEffect 0.00 8.00 - EFFECT = HeptagonEffect 0.00 8.00 +SEQUENCE 30.00 3 "Seq-8" + EFFECT + ThemeModulationEffect 0.00 10.00 + EFFECT = HeptagonEffect 0.00 10.00 EFFECT + GaussianBlurEffect 0.00 10.00 strength=1.5 EFFECT + ChromaAberrationEffect 0.00 10.00 offset=0.03 angle=0.785 EFFECT + SolarizeEffect 0.00 10.00 -SEQUENCE 46.00 2 - EFFECT - FlashCubeEffect 0.40 3.00 +SEQUENCE 40.00 2 + EFFECT - FlashCubeEffect 0.00 4.00 EFFECT + HeptagonEffect 0.00 4.00 EFFECT + ParticleSprayEffect 0.00 4.00 - EFFECT + ParticlesEffect 0.00 4.00 -SEQUENCE 46.00 2 "Fade" - EFFECT - FlashCubeEffect 0.40 3.00 - EFFECT + FlashEffect 0.00 2.00 +SEQUENCE 44.00 2 "Fade" + EFFECT - FlashCubeEffect 0.00 2.00 + EFFECT + FlashEffect 1.00 2.00 -SEQUENCE 48.00 10 - EFFECT - FlashCubeEffect 0.40 3.00 - EFFECT + GaussianBlurEffect 0.00 4.00 - EFFECT + FlashEffect 0.00 0.40 - EFFECT = FlashEffect 1.00 0.40 +SEQUENCE 46.00 10 + EFFECT - FlashCubeEffect 0.00 3.00 + EFFECT + GaussianBlurEffect 0.00 3.00 + EFFECT + FlashEffect 0.00 3.00 -SEQUENCE 51.00 1 - EFFECT + ThemeModulationEffect 0.00 16.00 - EFFECT + HeptagonEffect 0.40 4.00 - EFFECT + ParticleSprayEffect 0.00 16.00 - EFFECT + Hybrid3DEffect 0.00 16.12 - EFFECT + GaussianBlurEffect 0.00 16.00 - EFFECT + ChromaAberrationEffect 0.00 16.28 - EFFECT + SolarizeEffect 0.00 15.76 +SEQUENCE 49.00 1 + EFFECT + ThemeModulationEffect 0.00 8.00 + EFFECT + HeptagonEffect 0.00 8.00 + EFFECT + ParticleSprayEffect 0.00 8.00 + EFFECT + Hybrid3DEffect 0.00 8.00 + EFFECT + GaussianBlurEffect 0.00 8.00 + EFFECT + ChromaAberrationEffect 0.00 8.00 -SEQUENCE 66.00 0 - EFFECT + ThemeModulationEffect 0.00 6.00 - EFFECT + VignetteEffect 0.00 6.00 radius=0.6 softness=0.3 - EFFECT + SolarizeEffect 0.00 6.00 +SEQUENCE 57.00 0 + EFFECT + ThemeModulationEffect 0.00 7.00 + EFFECT + VignetteEffect 0.00 7.00 radius=0.6 softness=0.3 + EFFECT + SolarizeEffect 0.00 7.00 -SEQUENCE 71.00 0 - EFFECT + ThemeModulationEffect 0.00 8.00 - EFFECT + HeptagonEffect 0.40 4.00 - EFFECT + GaussianBlurEffect 0.00 16.00 +SEQUENCE 64.00 0 + EFFECT + ThemeModulationEffect 0.00 4.00 + EFFECT + HeptagonEffect 0.00 4.00 + EFFECT + GaussianBlurEffect 0.00 4.00 EFFECT + SolarizeEffect 0.00 4.00 -SEQUENCE 85.00 0 "double hepta!" - EFFECT + ThemeModulationEffect 0.00 12.00 - EFFECT = HeptagonEffect 0.40 4.00 - EFFECT + Hybrid3DEffect 0.00 8.00 - EFFECT + ParticleSprayEffect 0.00 11.00 - EFFECT + HeptagonEffect 0.00 16.00 - EFFECT + ChromaAberrationEffect 0.00 15.00 - EFFECT + GaussianBlurEffect 0.00 16.00 +SEQUENCE 68.00 0 "double hepta!" + EFFECT + ThemeModulationEffect 0.00 4.00 + EFFECT = HeptagonEffect 0.00 4.00 + EFFECT + Hybrid3DEffect 0.00 4.00 + EFFECT + ParticleSprayEffect 0.00 4.00 + EFFECT + HeptagonEffect 0.00 4.00 + EFFECT + ChromaAberrationEffect 0.00 4.00 + EFFECT + GaussianBlurEffect 0.00 4.00 -SEQUENCE 100.00 0 - EFFECT + ThemeModulationEffect 0.00 8.00 - EFFECT + HeptagonEffect 0.00 19.00 - EFFECT + ChromaAberrationEffect 0.00 18.00 - EFFECT + GaussianBlurEffect 0.00 16.00 +SEQUENCE 72.00 0 "The End" + EFFECT + ThemeModulationEffect 0.00 7.00 + EFFECT + HeptagonEffect 0.00 7.00 + EFFECT + ChromaAberrationEffect 0.00 7.00 + EFFECT + GaussianBlurEffect 0.00 7.00 |
