summaryrefslogtreecommitdiff
path: root/src/audio/spectrogram_resource_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/spectrogram_resource_manager.cc')
-rw-r--r--src/audio/spectrogram_resource_manager.cc228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/audio/spectrogram_resource_manager.cc b/src/audio/spectrogram_resource_manager.cc
new file mode 100644
index 0000000..dbed09e
--- /dev/null
+++ b/src/audio/spectrogram_resource_manager.cc
@@ -0,0 +1,228 @@
+// 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 <cstring>
+#include <vector>
+
+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<float> note_data = generate_note_spectrogram(resource->proc_params, &note_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
+}