diff options
| -rw-r--r-- | CMakeLists.txt | 45 | ||||
| -rw-r--r-- | GEMINI.md | 1 | ||||
| -rw-r--r-- | HOWTO.md | 45 | ||||
| -rw-r--r-- | PROJECT_CONTEXT.md | 1 | ||||
| -rw-r--r-- | assets/final/assets.txt | 0 | ||||
| -rw-r--r-- | src/main.cc | 7 | ||||
| -rw-r--r-- | tools/asset_packer.cc | 125 |
7 files changed, 220 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 69e51c6..1d06033 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,7 @@ if(DEMO_BUILD_TESTS) endif() option(DEMO_BUILD_TOOLS "Build tools" OFF) + if(DEMO_BUILD_TOOLS) add_executable(spectool tools/spectool.cc @@ -161,4 +162,46 @@ if(DEMO_BUILD_TOOLS) tools/specview.cc ) target_include_directories(specview PRIVATE src) -endif()
\ No newline at end of file + + add_executable(asset_packer + tools/asset_packer.cc + ) +endif() + +# Configure asset generation +set(ASSETS_TXT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/assets/final/assets.txt) +set(GENERATED_ASSETS_H ${CMAKE_CURRENT_BINARY_DIR}/src/assets.h) +set(GENERATED_ASSETS_DATA_CC ${CMAKE_CURRENT_BINARY_DIR}/src/assets_data.cc) + +add_custom_command( + OUTPUT ${GENERATED_ASSETS_H} ${GENERATED_ASSETS_DATA_CC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/src + COMMAND asset_packer ${ASSETS_TXT_PATH} ${GENERATED_ASSETS_H} ${GENERATED_ASSETS_DATA_CC} + DEPENDS tools/asset_packer.cc ${ASSETS_TXT_PATH} + COMMENT "Generating assets.h and assets_data.cc" +) + +add_custom_target(generate_assets ALL + DEPENDS ${GENERATED_ASSETS_H} ${GENERATED_ASSETS_DATA_CC} +) + +add_executable(demo64k + src/main.cc + src/platform.cc + src/gpu/gpu.cc + src/audio/audio.cc + src/audio/fdct.cc + src/audio/idct.cc + src/audio/window.cc + src/audio/synth.cc + third_party/glfw3webgpu/glfw3webgpu.c + ${GENERATED_ASSETS_DATA_CC} +) + +# Ensure demo64k depends on the generated assets +add_dependencies(demo64k generate_assets) + +target_link_libraries(demo64k PRIVATE ${DEMO_LIBS}) + +# Include generated assets header for compilation +target_include_directories(demo64k PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
\ No newline at end of file @@ -1,5 +1,6 @@ # Project Context @PROJECT_CONTEXT.md +@ASSET_SYSTEM.md @BUILD.md @FETCH_DEPS.md @CONTRIBUTING.md @@ -119,8 +119,47 @@ The executable will be located at `build/specview`. ./build/specview path/to/input.spec ``` -## References and links +### Asset Management System -drum-kit: https://drive.google.com/file/d/13tc7XjkMg-tigvje5qpp6XazK-VcOjoc/view -(got from https://www.reddit.com/r/Drumkits/) +This system allows embedding binary assets directly into the demo executable. +#### Defining Assets + +Assets are defined in `assets/final/assets.txt`. Each line specifies: +* `ASSET_NAME`: The identifier for the asset in C++ (e.g., `SAMPLE_142`). +* `filename.ext`: The path to the asset file (relative to `assets/final/`). +* `NONE`: Compression type (currently only `NONE` is supported). +* `"Description"`: An optional description. + +Example `assets/final/assets.txt` entry: +``` +SAMPLE_142, sample_142.spec, NONE, "A drum kick sample" +``` + +#### Building with Assets + +The `asset_packer` tool processes `assets/final/assets.txt` and generates two files: +* `build/src/assets.h`: Contains the `AssetId` enum and `GetAsset` function declaration. +* `build/src/assets_data.cc`: Contains the binary data for each asset. + +These files are automatically generated as part of the normal build process when `demo64k` is built. To trigger generation, simply run: + +```bash +cmake -S . -B build +cmake --build build +``` + +#### Accessing Assets in Code + +Include `assets.h` and use the `GetAsset` function: + +```cpp +#include "assets.h" + +// ... +size_t asset_size; +const uint8_t* my_asset = GetAsset(AssetId::ASSET_SAMPLE_142, &asset_size); +// ... +// For lazy decompression (scaffolding only): +// DropAsset(AssetId::ASSET_SAMPLE_142, my_asset); +``` diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 2fb533c..28f86f8 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -36,6 +36,7 @@ Incoming tasks in no particular order: - [x] 8. add a #define STRIP_ALL to remove all unnecessary code for the final build (for instance, command-line args parsing, or unnecessary options, constant parameters to function calls, etc.) +- [ ] 9. work on the compact in-line and off-line asset system (@ASSET_SYSTEM.md) - 9. work on the compact in-line and off-line asset system (@ASSET_SYSTEM.md) ## Session Decisions and Current State diff --git a/assets/final/assets.txt b/assets/final/assets.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/assets/final/assets.txt diff --git a/src/main.cc b/src/main.cc index 31e1c58..bcf6015 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,6 @@ #include "audio/audio.h" #include "audio/synth.h" +#include "assets.h" // Include generated asset header #include "gpu/gpu.h" #include "platform.h" #include "util/math.h" @@ -54,6 +55,12 @@ int main(int argc, char **argv) { const Spectrogram spec = {g_spec_buffer_a, g_spec_buffer_b, SPEC_FRAMES}; int tone_id = synth_register_spectrogram(&spec); + // Dummy call to ensure asset system is linked + size_t dummy_size; + const uint8_t* dummy_asset = GetAsset(AssetId::ASSET_NULL_ASSET, &dummy_size); + (void)dummy_asset; + (void)dummy_size; + double last_beat_time = 0.0; int beat_count = 0; diff --git a/tools/asset_packer.cc b/tools/asset_packer.cc new file mode 100644 index 0000000..8b06829 --- /dev/null +++ b/tools/asset_packer.cc @@ -0,0 +1,125 @@ +#include <fstream> +#include <iostream> +#include <map> +#include <string> +#include <vector> + +int main(int argc, char *argv[]) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] + << " <assets.txt_path> <output_assets_h_path> " + "<output_assets_data_cc_path>\n"; + return 1; + } + + std::string assets_txt_path = argv[1]; + std::string output_assets_h_path = argv[2]; + std::string output_assets_data_cc_path = argv[3]; + + std::ifstream assets_txt_file(assets_txt_path); + if (!assets_txt_file.is_open()) { + std::cerr << "Error: Could not open assets.txt at " << assets_txt_path + << "\n"; + return 1; + } + + std::ofstream assets_h_file(output_assets_h_path); + if (!assets_h_file.is_open()) { + std::cerr << "Error: Could not open output assets.h at " + << output_assets_h_path << "\n"; + return 1; + } + + std::ofstream assets_data_cc_file(output_assets_data_cc_path); + if (!assets_data_cc_file.is_open()) { + std::cerr << "Error: Could not open output assets_data.cc at " + << output_assets_data_cc_path << "\n"; + return 1; + } + + // Generate assets.h + assets_h_file << "#pragma once\n"; + assets_h_file << "#include <cstdint>\n\n"; + assets_h_file << "enum class AssetId : uint16_t {\n"; + + // Generate assets_data.cc header + assets_data_cc_file << "#include \"assets.h\"\n\n"; + assets_data_cc_file << "#include <vector>\n\n"; + + std::string line; + int asset_id_counter = 0; + std::vector<std::string> asset_names; + + while (std::getline(assets_txt_file, line)) { + if (line.empty() || line[0] == '#') + continue; // Skip empty lines and comments + + size_t first_comma = line.find(','); + if (first_comma == std::string::npos) { + std::cerr << "Warning: Skipping malformed line in assets.txt: " << line + << "\n"; + continue; + } + + std::string asset_name = line.substr(0, first_comma); + asset_name.erase(0, asset_name.find_first_not_of(" \t\r\n")); + asset_name.erase(asset_name.find_last_not_of(" \t\r\n") + 1); + + if (asset_name.empty()) { + std::cerr << "Warning: Skipping line with empty asset name: " << line + << "\n"; + continue; + } + + asset_names.push_back(asset_name); + + // Add to assets.h enum + assets_h_file << " ASSET_" << asset_name << " = " << asset_id_counter + << ",\n"; + + // Placeholder for assets_data.cc (empty data for now) + assets_data_cc_file << "const uint8_t ASSET_DATA_" << asset_name + << "[] = {};\n"; + assets_data_cc_file << "const size_t ASSET_SIZE_" << asset_name + << " = 0;\n\n"; + + asset_id_counter++; + } + + assets_h_file << "};\n\n"; + + // Generate GetAsset function declaration in assets.h + assets_h_file + << "const uint8_t *GetAsset(AssetId asset_id, size_t *out_size = nullptr);\n"; + assets_h_file << "void DropAsset(AssetId asset_id, const uint8_t *asset); // For lazy " + "decompression scaffolding\n"; + assets_h_file.close(); + + // Generate GetAsset function implementation in assets_data.cc + assets_data_cc_file << "const uint8_t *GetAsset(AssetId asset_id, size_t *out_size) {\n"; + assets_data_cc_file << " if (out_size) *out_size = 0;\n"; // Default for now + assets_data_cc_file << " switch (asset_id) {\n"; + for (const std::string &name : asset_names) { + assets_data_cc_file << " case AssetId::ASSET_" << name << ":\n"; + assets_data_cc_file << " if (out_size) *out_size = ASSET_SIZE_" << name << ";\n"; + assets_data_cc_file << " return ASSET_DATA_" << name << ";\n"; + } + assets_data_cc_file << " default:\n"; + assets_data_cc_file << " return nullptr;\n"; + assets_data_cc_file << " }\n"; + assets_data_cc_file << "}\n\n"; + + // Dummy DropAsset implementation + assets_data_cc_file << "void DropAsset(AssetId asset_id, const uint8_t *asset) {\n"; + assets_data_cc_file << " (void)asset_id;\n"; + assets_data_cc_file << " (void)asset;\n"; + assets_data_cc_file << " // No-op for now, actual implementation for lazy decompression goes here\n"; + assets_data_cc_file << "}\n"; + + assets_data_cc_file.close(); + + std::cout << "Asset packer successfully generated: " << output_assets_h_path + << " and " << output_assets_data_cc_path << "\n"; + + return 0; +} |
