// This file is part of the 64k demo project. // SpectrogramResourceManager implementation. #include "spectrogram_resource_manager.h" #include "audio/audio.h" #include "procedural/generator.h" #include "util/debug.h" #include #include void SpectrogramResourceManager::init() { for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) { resources_[i].owned_data = nullptr; resources_[i].asset_id = AssetId::ASSET_LAST_ID; resources_[i].state = UNREGISTERED; #if defined(DEMO_ENABLE_CACHE_EVICTION) resources_[i].last_access_time = 0.0f; #endif } loaded_count_ = 0; } void SpectrogramResourceManager::shutdown() { // Free all owned memory (procedural spectrograms) for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) { if (resources_[i].owned_data != nullptr) { delete[] resources_[i].owned_data; resources_[i].owned_data = nullptr; } } loaded_count_ = 0; } void SpectrogramResourceManager::reset() { // Clear state but keep registrations (useful for seeking) // Don't free memory, just mark as unloaded for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) { if (resources_[i].state == LOADED) { resources_[i].state = REGISTERED; } } loaded_count_ = 0; } void SpectrogramResourceManager::register_asset(int sample_id, AssetId asset_id) { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return; } Resource& resource = resources_[sample_id]; resource.asset_id = asset_id; resource.state = REGISTERED; #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr] Registered asset sample_id=%d, asset_id=%d\n", sample_id, (int)asset_id); #endif } void SpectrogramResourceManager::register_procedural(int sample_id, const NoteParams& params) { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return; } Resource& resource = resources_[sample_id]; resource.asset_id = AssetId::ASSET_LAST_ID; // Mark as procedural resource.proc_params = params; resource.state = REGISTERED; #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr] Registered procedural sample_id=%d, freq=%.2f\n", sample_id, params.base_freq); #endif } const Spectrogram* SpectrogramResourceManager::get_or_load(int sample_id) { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return nullptr; } Resource& resource = resources_[sample_id]; // Already loaded? if (resource.state == LOADED) { #if defined(DEMO_ENABLE_CACHE_EVICTION) resource.last_access_time = 0.0f; // TODO: Get actual time #endif return &resource.spec; } // Need to load if (resource.state == REGISTERED) { if (resource.asset_id != AssetId::ASSET_LAST_ID) { load_asset(&resource); } else { load_procedural(&resource); } resource.state = LOADED; loaded_count_++; } return (resource.state == LOADED) ? &resource.spec : nullptr; } void SpectrogramResourceManager::preload(int sample_id) { // Just call get_or_load to trigger loading get_or_load(sample_id); } void SpectrogramResourceManager::preload_range(int start_id, int end_id) { for (int i = start_id; i <= end_id && i < MAX_SPECTROGRAM_RESOURCES; ++i) { preload(i); } } const Spectrogram* SpectrogramResourceManager::get_spectrogram(int sample_id) const { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return nullptr; } const Resource& resource = resources_[sample_id]; return (resource.state == LOADED) ? &resource.spec : nullptr; } bool SpectrogramResourceManager::is_loaded(int sample_id) const { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return false; } return resources_[sample_id].state == LOADED; } int SpectrogramResourceManager::get_loaded_count() const { return loaded_count_; } #if defined(DEMO_ENABLE_CACHE_EVICTION) void SpectrogramResourceManager::release(int sample_id) { if (sample_id < 0 || sample_id >= MAX_SPECTROGRAM_RESOURCES) { return; } Resource& resource = resources_[sample_id]; if (resource.state == LOADED && resource.owned_data != nullptr) { delete[] resource.owned_data; resource.owned_data = nullptr; resource.state = EVICTED; loaded_count_--; #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr] Released sample_id=%d\n", sample_id); #endif } } void SpectrogramResourceManager::release_all() { for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) { release(i); } } void SpectrogramResourceManager::try_evict_lru(float current_time) { // Find least recently used loaded resource int lru_id = -1; float oldest_time = current_time; for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) { if (resources_[i].state == LOADED && resources_[i].last_access_time < oldest_time) { oldest_time = resources_[i].last_access_time; lru_id = i; } } // Evict if not accessed in last 10 seconds if (lru_id != -1 && (current_time - oldest_time) > 10.0f) { release(lru_id); } } #endif /* defined(DEMO_ENABLE_CACHE_EVICTION) */ void SpectrogramResourceManager::load_asset(Resource* resource) { size_t size; const uint8_t* data = GetAsset(resource->asset_id, &size); if (data == nullptr || size < sizeof(SpecHeader)) { #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr ERROR] Failed to load asset %d\n", (int)resource->asset_id); #endif return; } const SpecHeader* header = (const SpecHeader*)data; const float* spectral_data = (const float*)(data + sizeof(SpecHeader)); resource->spec.spectral_data_a = spectral_data; resource->spec.spectral_data_b = spectral_data; resource->spec.num_frames = header->num_frames; resource->owned_data = nullptr; // Asset data is not owned #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr] Loaded asset %d: %d frames\n", (int)resource->asset_id, header->num_frames); #endif } void SpectrogramResourceManager::load_procedural(Resource* resource) { int note_frames = 0; std::vector note_data = generate_note_spectrogram(resource->proc_params, ¬e_frames); if (note_frames <= 0 || note_data.empty()) { #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr ERROR] Failed to generate procedural note\n"); #endif return; } // Allocate persistent storage resource->owned_data = new float[note_data.size()]; memcpy(resource->owned_data, note_data.data(), note_data.size() * sizeof(float)); resource->spec.spectral_data_a = resource->owned_data; resource->spec.spectral_data_b = resource->owned_data; resource->spec.num_frames = note_frames; #if defined(DEBUG_LOG_ASSETS) DEBUG_ASSETS("[ResourceMgr] Generated procedural: %d frames, freq=%.2f\n", note_frames, resource->proc_params.base_freq); #endif }