summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-05-20 23:12:08 +0200
committerskal <pascal.massimino@gmail.com>2026-05-20 23:21:59 +0200
commitca8acd5e7c0556bee7cb21f5ff280c5fd1f47801 (patch)
tree3811886d12f075ba196f2a7ba2d05c0d5b43574b /src
parenta91f89c8ea15665853176c05597760d0fcf6e0df (diff)
fix: audio & effects cleanup — dead code removal, simplifications
Diffstat (limited to 'src')
-rw-r--r--src/audio/audio_engine.cc7
-rw-r--r--src/audio/spectrogram_resource_manager.cc89
-rw-r--r--src/audio/spectrogram_resource_manager.h3
-rw-r--r--src/audio/tracker.cc241
-rw-r--r--src/audio/tracker.h6
-rw-r--r--src/effects/peak_meter_effect.cc19
-rw-r--r--src/effects/peak_meter_effect.h6
-rw-r--r--src/effects/rotating_cube_effect.cc15
-rw-r--r--src/effects/rotating_cube_effect.h6
-rw-r--r--src/tests/audio/test_audio_engine.cc33
10 files changed, 157 insertions, 268 deletions
diff --git a/src/audio/audio_engine.cc b/src/audio/audio_engine.cc
index c184324..1d6659d 100644
--- a/src/audio/audio_engine.cc
+++ b/src/audio/audio_engine.cc
@@ -13,7 +13,7 @@ void AudioEngine::init() {
// Initialize in correct order (synth first, then tracker)
synth_init();
resource_mgr_.init();
- tracker_init();
+ tracker_init(&resource_mgr_);
// Initialize sample-to-synth-id mapping
for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
@@ -48,8 +48,8 @@ void AudioEngine::reset() {
}
synth_init(); // Re-init synth (clears all state)
- ::tracker_init(); // Re-register all spectrograms (synth slots now clean)
resource_mgr_.reset();
+ ::tracker_init(&resource_mgr_); // Re-register all spectrograms
// Clear sample-to-synth mapping
for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
@@ -171,7 +171,8 @@ void AudioEngine::seek(float target_time) {
synth_init();
// 2. Re-init tracker: re-registers all spectrograms with now-clean synth slots
- ::tracker_init();
+ resource_mgr_.reset();
+ ::tracker_init(&resource_mgr_);
// 3. Clear sample-to-synth mapping (will be re-registered on demand)
for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
diff --git a/src/audio/spectrogram_resource_manager.cc b/src/audio/spectrogram_resource_manager.cc
index c0fb0a3..f182869 100644
--- a/src/audio/spectrogram_resource_manager.cc
+++ b/src/audio/spectrogram_resource_manager.cc
@@ -3,11 +3,17 @@
#include "spectrogram_resource_manager.h"
#include "audio/audio.h"
+#include "audio/dct.h"
#include "procedural/generator.h"
#include "util/debug.h"
#include <cstring>
#include <vector>
+#if !defined(STRIP_ALL)
+#include "audio/mp3_sample.h"
+#include "audio/window.h"
+#endif
+
void SpectrogramResourceManager::init() {
for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
resources_[i].owned_data = nullptr;
@@ -32,10 +38,13 @@ void SpectrogramResourceManager::shutdown() {
}
void SpectrogramResourceManager::reset() {
- // Clear state but keep registrations (useful for seeking)
- // Don't free memory, just mark as unloaded
+ // Clear state and free owned memory, keep registrations
for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
if (resources_[i].state == LOADED) {
+ if (resources_[i].owned_data) {
+ delete[] resources_[i].owned_data;
+ resources_[i].owned_data = nullptr;
+ }
resources_[i].state = REGISTERED;
}
}
@@ -50,7 +59,9 @@ void SpectrogramResourceManager::register_asset(int sample_id,
Resource& resource = resources_[sample_id];
resource.asset_id = asset_id;
- resource.state = REGISTERED;
+ if (resource.state == UNREGISTERED) {
+ resource.state = REGISTERED;
+ }
#if defined(DEBUG_LOG_ASSETS)
DEBUG_ASSETS("[ResourceMgr] Registered asset sample_id=%d, asset_id=%d\n",
@@ -67,7 +78,9 @@ void SpectrogramResourceManager::register_procedural(int sample_id,
Resource& resource = resources_[sample_id];
resource.asset_id = AssetId::ASSET_LAST_ID; // Mark as procedural
resource.proc_params = params;
- resource.state = REGISTERED;
+ if (resource.state == UNREGISTERED) {
+ resource.state = REGISTERED;
+ }
#if defined(DEBUG_LOG_ASSETS)
DEBUG_ASSETS("[ResourceMgr] Registered procedural sample_id=%d, freq=%.2f\n",
@@ -184,12 +197,19 @@ void SpectrogramResourceManager::try_evict_lru(float current_time) {
void SpectrogramResourceManager::load_asset(Resource* resource) {
size_t size;
const uint8_t* data = GetAsset(resource->asset_id, &size);
+ if (data == nullptr || size == 0) {
+ return;
+ }
- 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);
+#if !defined(STRIP_ALL)
+ // MP3 assets: decode to spectrogram via OLA analysis
+ if (GetAssetType(resource->asset_id) == AssetType::MP3) {
+ load_mp3(resource, data, size);
+ return;
+ }
#endif
+
+ if (size < sizeof(SpecHeader)) {
return;
}
@@ -201,12 +221,57 @@ void SpectrogramResourceManager::load_asset(Resource* resource) {
resource->spec.num_frames = header->num_frames;
resource->spec.version = header->version;
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
+#if !defined(STRIP_ALL)
+void SpectrogramResourceManager::load_mp3(Resource* resource,
+ const uint8_t* data, size_t size) {
+ Mp3Decoder* dec = mp3_open(data, size);
+ if (!dec)
+ return;
+
+ float window[DCT_SIZE];
+ hann_window_512(window);
+
+ std::vector<float> spec_data;
+ float pcm_buf[DCT_SIZE];
+ float pcm_chunk[DCT_SIZE];
+ float dct_chunk[DCT_SIZE];
+
+ memset(pcm_buf, 0, sizeof(pcm_buf));
+
+ for (;;) {
+ memmove(pcm_buf, pcm_buf + OLA_HOP_SIZE, OLA_HOP_SIZE * sizeof(float));
+ const int decoded = mp3_decode(dec, OLA_HOP_SIZE, pcm_buf + OLA_HOP_SIZE);
+ if (decoded < OLA_HOP_SIZE) {
+ memset(pcm_buf + OLA_HOP_SIZE + decoded, 0,
+ (OLA_HOP_SIZE - decoded) * sizeof(float));
+ }
+
+ for (int i = 0; i < DCT_SIZE; ++i)
+ pcm_chunk[i] = pcm_buf[i] * window[i];
+ fdct_512(pcm_chunk, dct_chunk);
+ spec_data.insert(spec_data.end(), dct_chunk, dct_chunk + DCT_SIZE);
+
+ if (decoded == 0)
+ break;
+ }
+
+ mp3_close(dec);
+ if (spec_data.empty())
+ return;
+
+ const int num_frames = (int)(spec_data.size() / DCT_SIZE);
+ resource->owned_data = new float[spec_data.size()];
+ memcpy(resource->owned_data, spec_data.data(),
+ spec_data.size() * sizeof(float));
+
+ resource->spec.spectral_data_a = resource->owned_data;
+ resource->spec.spectral_data_b = resource->owned_data;
+ resource->spec.num_frames = num_frames;
+ resource->spec.version = SPEC_VERSION_V2_OLA;
}
+#endif
void SpectrogramResourceManager::load_procedural(Resource* resource) {
int note_frames = 0;
diff --git a/src/audio/spectrogram_resource_manager.h b/src/audio/spectrogram_resource_manager.h
index 45faa37..687f91e 100644
--- a/src/audio/spectrogram_resource_manager.h
+++ b/src/audio/spectrogram_resource_manager.h
@@ -68,6 +68,9 @@ class SpectrogramResourceManager {
// Load implementation
void load_asset(Resource* resource);
void load_procedural(Resource* resource);
+#if !defined(STRIP_ALL)
+ void load_mp3(Resource* resource, const uint8_t* data, size_t size);
+#endif
Resource resources_[MAX_SPECTROGRAM_RESOURCES];
int loaded_count_ = 0;
diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc
index a511a62..e634333 100644
--- a/src/audio/tracker.cc
+++ b/src/audio/tracker.cc
@@ -1,5 +1,6 @@
#include "tracker.h"
#include "audio.h"
+#include "audio/spectrogram_resource_manager.h"
#include "audio/synth.h"
#include "ring_buffer.h"
#include "util/asset_manager.h"
@@ -7,13 +8,6 @@
#include "util/fatal_error.h"
#include <cstring>
#include <random>
-#include <vector>
-
-#if !defined(STRIP_ALL)
-#include "audio/dct.h"
-#include "audio/mp3_sample.h"
-#include "audio/window.h"
-#endif // !defined(STRIP_ALL)
static uint32_t g_last_trigger_idx = 0;
@@ -27,201 +21,76 @@ struct ActivePattern {
static ActivePattern g_active_patterns[MAX_SPECTROGRAMS];
-struct ManagedSpectrogram {
- int synth_id;
- float* data;
- bool active;
-};
-
-// Simple pool for dynamic spectrograms (now for individual notes)
-static ManagedSpectrogram g_spec_pool[MAX_SPECTROGRAMS];
-static int g_next_pool_slot = 0; // Round-robin allocation
-
// CACHE: Pre-registered synth_ids for all samples (indexed by sample_id)
-// This eliminates redundant spectrogram generation and registration
-static int g_sample_synth_cache[256]; // Max 256 unique samples
-static bool g_cache_initialized = false;
-
-// Forward declarations
-static int get_free_pool_slot();
-
-#if !defined(STRIP_ALL)
-// Decode an in-memory MP3 blob to a heap-allocated spectrogram (caller owns).
-// Uses OLA analysis: 512-sample Hann window, OLA_HOP_SIZE advance per frame.
-// Returns nullptr on error. Sets *out_num_frames to frame count.
-static float* convert_mp3_to_spectrogram(const uint8_t* data, size_t size,
- int* out_num_frames) {
- *out_num_frames = 0;
- Mp3Decoder* dec = mp3_open(data, size);
- if (!dec)
- return nullptr;
+static int g_sample_synth_cache[MAX_SPECTROGRAM_RESOURCES];
- float window[DCT_SIZE];
- hann_window_512(window);
-
- std::vector<float> spec_data;
- float pcm_buf[DCT_SIZE];
- float pcm_chunk[DCT_SIZE];
- float dct_chunk[DCT_SIZE];
-
- // Sliding-window OLA analysis: advance OLA_HOP_SIZE samples per frame.
- // First iteration: pcm_buf is zero-initialized (silence before signal start).
- memset(pcm_buf, 0, sizeof(pcm_buf));
-
- for (;;) {
- // Slide left by OLA_HOP_SIZE; fill right half with new samples.
- memmove(pcm_buf, pcm_buf + OLA_HOP_SIZE, OLA_HOP_SIZE * sizeof(float));
- const int decoded = mp3_decode(dec, OLA_HOP_SIZE, pcm_buf + OLA_HOP_SIZE);
- if (decoded < OLA_HOP_SIZE) {
- memset(pcm_buf + OLA_HOP_SIZE + decoded, 0,
- (OLA_HOP_SIZE - decoded) * sizeof(float));
- }
-
- // Window + DCT the current 512-sample frame.
- for (int i = 0; i < DCT_SIZE; ++i)
- pcm_chunk[i] = pcm_buf[i] * window[i];
- fdct_512(pcm_chunk, dct_chunk);
- spec_data.insert(spec_data.end(), dct_chunk, dct_chunk + DCT_SIZE);
-
- if (decoded == 0)
- break;
+static int get_free_pattern_slot() {
+ for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
+ if (!g_active_patterns[i].active)
+ return i;
}
-
- mp3_close(dec);
- if (spec_data.empty())
- return nullptr;
-
- const int num_frames = (int)(spec_data.size() / DCT_SIZE);
- float* result = new float[spec_data.size()];
- memcpy(result, spec_data.data(), spec_data.size() * sizeof(float));
- *out_num_frames = num_frames;
- return result;
+ return -1; // No free slots
}
-#endif // !defined(STRIP_ALL)
-void tracker_init() {
+void tracker_init(SpectrogramResourceManager* resource_mgr) {
g_last_trigger_idx = 0;
- g_next_pool_slot = 0;
-
- // Free any previously allocated generated note data to prevent leaks.
- // Must run before the pool is zeroed (relies on .active/.data still set).
- if (g_cache_initialized) {
- for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
- if (g_spec_pool[i].data != nullptr && g_spec_pool[i].active) {
- delete[] g_spec_pool[i].data;
- }
- }
- }
for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
- g_spec_pool[i].synth_id = -1;
- g_spec_pool[i].data = nullptr;
- g_spec_pool[i].active = false;
g_active_patterns[i].active = false;
}
- // Initialize sample cache
- {
- for (int i = 0; i < 256; ++i) {
- g_sample_synth_cache[i] = -1;
- }
+ for (int i = 0; i < MAX_SPECTROGRAM_RESOURCES; ++i) {
+ g_sample_synth_cache[i] = -1;
+ }
- // Pre-register all unique samples (assets + generated notes)
- for (uint32_t sid = 0; sid < g_tracker_samples_count; ++sid) {
- AssetId aid = g_tracker_sample_assets[sid];
+ // Pre-register all unique samples (assets + generated notes)
+ for (uint32_t sid = 0; sid < g_tracker_samples_count; ++sid) {
+ AssetId aid = g_tracker_sample_assets[sid];
+ if (resource_mgr) {
+ // Unified path: register metadata, load eagerly, register with synth
+ if (aid != AssetId::ASSET_LAST_ID) {
+ resource_mgr->register_asset(sid, aid);
+ } else {
+ resource_mgr->register_procedural(sid, g_tracker_samples[sid]);
+ }
+ const Spectrogram* spec = resource_mgr->get_or_load(sid);
+ if (spec) {
+ g_sample_synth_cache[sid] = synth_register_spectrogram(spec);
+ }
+ } else {
+ // Legacy path (no resource manager — standalone tests)
if (aid != AssetId::ASSET_LAST_ID) {
- // ASSET sample: Load once and cache
size_t size;
const uint8_t* data = GetAsset(aid, &size);
#if !defined(STRIP_ALL)
if (data && size > 0 && GetAssetType(aid) == AssetType::MP3) {
- int num_frames = 0;
- float* spec_data =
- convert_mp3_to_spectrogram(data, size, &num_frames);
- if (spec_data && num_frames > 0) {
- const int slot = get_free_pool_slot();
- g_spec_pool[slot].data = spec_data;
- g_spec_pool[slot].active = true;
- Spectrogram spec;
- spec.spectral_data_a = spec_data;
- spec.spectral_data_b = spec_data;
- spec.num_frames = num_frames;
- spec.version = SPEC_VERSION_V2_OLA;
- g_sample_synth_cache[sid] = synth_register_spectrogram(&spec);
- g_spec_pool[slot].synth_id = g_sample_synth_cache[sid];
- }
- } else
+ continue; // MP3 requires resource manager
+ }
#else
if (data != nullptr && GetAssetType(aid) == AssetType::MP3) {
- continue; // MP3 decoding not available in STRIP_ALL builds
+ continue;
}
#endif
- if (data && size >= sizeof(SpecHeader)) {
+ if (data && size >= sizeof(SpecHeader)) {
const SpecHeader* header = (const SpecHeader*)data;
- const int note_frames = header->num_frames;
- const float* spectral_data =
- (const float*)(data + sizeof(SpecHeader));
-
Spectrogram spec;
- spec.spectral_data_a = spectral_data;
- spec.spectral_data_b = spectral_data;
- spec.num_frames = note_frames;
+ spec.spectral_data_a = (const float*)(data + sizeof(SpecHeader));
+ spec.spectral_data_b = spec.spectral_data_a;
+ spec.num_frames = header->num_frames;
spec.version = header->version;
-
- g_sample_synth_cache[sid] = synth_register_spectrogram(&spec);
-
-#if defined(DEBUG_LOG_TRACKER)
- if (g_sample_synth_cache[sid] == -1) {
- DEBUG_TRACKER(
- "[TRACKER INIT] Failed to cache asset sample_id=%d (aid=%d)\n",
- sid, (int)aid);
- }
-#endif /* defined(DEBUG_LOG_TRACKER) */
- }
- } else {
- // GENERATED note: Generate once and cache
- const NoteParams& params = g_tracker_samples[sid];
- int note_frames = 0;
- std::vector<float> note_data =
- generate_note_spectrogram(params, &note_frames);
-
- if (note_frames > 0) {
- // Allocate persistent storage for this note
- const int slot = get_free_pool_slot();
- g_spec_pool[slot].data = new float[note_data.size()];
- memcpy(g_spec_pool[slot].data, note_data.data(),
- note_data.size() * sizeof(float));
-
- Spectrogram spec;
- spec.spectral_data_a = g_spec_pool[slot].data;
- spec.spectral_data_b = g_spec_pool[slot].data;
- spec.num_frames = note_frames;
- spec.version = SPEC_VERSION_V1;
-
g_sample_synth_cache[sid] = synth_register_spectrogram(&spec);
- g_spec_pool[slot].synth_id = g_sample_synth_cache[sid];
- g_spec_pool[slot].active = true; // Mark as permanently allocated
-
-#if defined(DEBUG_LOG_TRACKER)
- if (g_sample_synth_cache[sid] == -1) {
- DEBUG_TRACKER(
- "[TRACKER INIT] Failed to cache generated sample_id=%d "
- "(freq=%.2f)\n",
- sid, params.base_freq);
- }
-#endif /* defined(DEBUG_LOG_TRACKER) */
}
}
+ // Note: procedural notes without resource_mgr are not supported
+ // (requires std::vector allocation — use resource_mgr path)
}
-
- g_cache_initialized = true;
+ }
#if defined(DEBUG_LOG_TRACKER)
- DEBUG_TRACKER("[TRACKER INIT] Cached %d unique samples\n",
- g_tracker_samples_count);
-#endif /* defined(DEBUG_LOG_TRACKER) */
- }
+ DEBUG_TRACKER("[TRACKER INIT] Cached %d unique samples\n",
+ g_tracker_samples_count);
+#endif
// Validate that all pattern events are sorted by unit_time
// (required for early-exit optimization in tracker_update)
@@ -247,36 +116,6 @@ void tracker_reset() {
}
}
-static int get_free_pool_slot() {
- // Try to find an inactive slot first (unused slots)
- for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
- if (!g_spec_pool[i].active)
- return i;
- }
-
- // If all slots are active, reuse the oldest one (round-robin)
- // Clean up the old slot before reuse
- const int slot = g_next_pool_slot;
- g_next_pool_slot = (g_next_pool_slot + 1) % MAX_SPECTROGRAMS;
-
- if (g_spec_pool[slot].synth_id >= 0) {
- synth_unregister_spectrogram(g_spec_pool[slot].synth_id);
- }
- delete[] g_spec_pool[slot].data;
- g_spec_pool[slot].data = nullptr;
- g_spec_pool[slot].synth_id = -1;
- g_spec_pool[slot].active = false;
- return slot;
-}
-
-static int get_free_pattern_slot() {
- for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
- if (!g_active_patterns[i].active)
- return i;
- }
- return -1; // No free slots
-}
-
// Helper to trigger a single note event (OPTIMIZED with caching)
// start_offset_samples: How many samples into the future to trigger (for
// sample-accurate timing)
diff --git a/src/audio/tracker.h b/src/audio/tracker.h
index f471bd8..a96a801 100644
--- a/src/audio/tracker.h
+++ b/src/audio/tracker.h
@@ -7,6 +7,8 @@
#include "generated/assets.h"
#include <cstdint>
+class SpectrogramResourceManager;
+
struct TrackerEvent {
float unit_time; // Unit-less time within pattern (0.0 to pattern.unit_length)
uint16_t sample_id;
@@ -44,6 +46,8 @@ extern const TrackerPattern g_tracker_patterns[];
extern const uint32_t g_tracker_patterns_count;
extern const TrackerScore g_tracker_score;
-void tracker_init();
+// Initialize tracker. If resource_mgr is provided, spectrograms are loaded
+// through it (unified ownership). If nullptr, tracker manages its own storage.
+void tracker_init(SpectrogramResourceManager* resource_mgr = nullptr);
void tracker_update(double music_time_sec, double dt_music_sec);
void tracker_reset(); // Reset tracker state (for tests/seeking)
diff --git a/src/effects/peak_meter_effect.cc b/src/effects/peak_meter_effect.cc
index c2ef42e..8956e32 100644
--- a/src/effects/peak_meter_effect.cc
+++ b/src/effects/peak_meter_effect.cc
@@ -9,8 +9,7 @@ PeakMeter::PeakMeter(const GpuContext& ctx,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs, float start_time,
float end_time)
- : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr),
- bind_group_(nullptr) {
+ : Effect(ctx, inputs, outputs, start_time, end_time) {
HEADLESS_RETURN_IF_NULL(ctx_.device);
const char* shader_main = R"(
@@ -58,15 +57,8 @@ PeakMeter::PeakMeter(const GpuContext& ctx,
std::string shader_code =
ShaderComposer::Get().Compose({"common_uniforms"}, shader_main);
- pipeline_ = create_post_process_pipeline(
- ctx_.device, WGPUTextureFormat_RGBA8Unorm, shader_code.c_str());
-}
-
-PeakMeter::~PeakMeter() {
- if (bind_group_)
- wgpuBindGroupRelease(bind_group_);
- if (pipeline_)
- wgpuRenderPipelineRelease(pipeline_);
+ pipeline_.set(create_post_process_pipeline(
+ ctx_.device, WGPUTextureFormat_RGBA8Unorm, shader_code.c_str()));
}
void PeakMeter::render(WGPUCommandEncoder encoder,
@@ -75,9 +67,10 @@ void PeakMeter::render(WGPUCommandEncoder encoder,
WGPUTextureView input_view = nodes.get_view(input_nodes_[0]);
WGPUTextureView output_view = nodes.get_view(output_nodes_[0]);
- pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view,
+ pp_update_bind_group(ctx_.device, pipeline_.get(),
+ bind_group_.get_address(), input_view,
uniforms_buffer_.get(), {nullptr, 0});
- run_fullscreen_pass(encoder, pipeline_, bind_group_, output_view,
+ run_fullscreen_pass(encoder, pipeline_.get(), bind_group_.get(), output_view,
WGPULoadOp_Load);
}
diff --git a/src/effects/peak_meter_effect.h b/src/effects/peak_meter_effect.h
index 1f19ed6..b822efd 100644
--- a/src/effects/peak_meter_effect.h
+++ b/src/effects/peak_meter_effect.h
@@ -4,18 +4,18 @@
#pragma once
#include "gpu/effect.h"
#include "gpu/uniform_helper.h"
+#include "gpu/wgpu_resource.h"
class PeakMeter : public Effect {
public:
PeakMeter(const GpuContext& ctx, const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs, float start_time,
float end_time);
- ~PeakMeter() override;
void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params,
NodeRegistry& nodes) override;
private:
- WGPURenderPipeline pipeline_;
- WGPUBindGroup bind_group_;
+ RenderPipeline pipeline_;
+ BindGroup bind_group_;
};
diff --git a/src/effects/rotating_cube_effect.cc b/src/effects/rotating_cube_effect.cc
index 82f90c5..000d177 100644
--- a/src/effects/rotating_cube_effect.cc
+++ b/src/effects/rotating_cube_effect.cc
@@ -84,7 +84,7 @@ RotatingCube::RotatingCube(const GpuContext& ctx,
pipeline_desc.multisample.mask = 0xFFFFFFFF;
pipeline_desc.fragment = &fragment;
- pipeline_ = wgpuDeviceCreateRenderPipeline(ctx_.device, &pipeline_desc);
+ pipeline_.set(wgpuDeviceCreateRenderPipeline(ctx_.device, &pipeline_desc));
wgpuShaderModuleRelease(shader_module);
wgpuPipelineLayoutRelease(pipeline_layout);
@@ -103,17 +103,10 @@ RotatingCube::RotatingCube(const GpuContext& ctx,
.entryCount = 2,
.entries = entries,
};
- bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc);
+ bind_group_.set(wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc));
wgpuBindGroupLayoutRelease(bgl);
}
-RotatingCube::~RotatingCube() {
- if (bind_group_)
- wgpuBindGroupRelease(bind_group_);
- if (pipeline_)
- wgpuRenderPipelineRelease(pipeline_);
-}
-
void RotatingCube::declare_nodes(NodeRegistry& registry) {
// Declare depth buffer node
registry.declare_node(depth_node_, NodeType::DEPTH24, -1, -1);
@@ -201,8 +194,8 @@ void RotatingCube::render(WGPUCommandEncoder encoder,
WGPURenderPassEncoder pass =
wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc);
- wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
- wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr);
+ wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get());
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr);
wgpuRenderPassEncoderDraw(pass, 36, 1, 0, 0); // 36 vertices for cube
wgpuRenderPassEncoderEnd(pass);
wgpuRenderPassEncoderRelease(pass);
diff --git a/src/effects/rotating_cube_effect.h b/src/effects/rotating_cube_effect.h
index fb321fa..0aa1469 100644
--- a/src/effects/rotating_cube_effect.h
+++ b/src/effects/rotating_cube_effect.h
@@ -6,6 +6,7 @@
#include "gpu/effect.h"
#include "gpu/gpu.h"
#include "gpu/uniform_helper.h"
+#include "gpu/wgpu_resource.h"
#include "util/mini_math.h"
class RotatingCube : public Effect {
@@ -13,7 +14,6 @@ class RotatingCube : public Effect {
RotatingCube(const GpuContext& ctx, const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs, float start_time,
float end_time);
- ~RotatingCube() override;
void declare_nodes(NodeRegistry& registry) override;
void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params,
@@ -44,8 +44,8 @@ class RotatingCube : public Effect {
};
static_assert(sizeof(ObjectData) == 160, "ObjectData size mismatch");
- WGPURenderPipeline pipeline_ = nullptr;
- WGPUBindGroup bind_group_ = nullptr;
+ RenderPipeline pipeline_;
+ BindGroup bind_group_;
GpuBuffer uniform_buffer_;
GpuBuffer object_buffer_;
float rotation_ = 0.0f;
diff --git a/src/tests/audio/test_audio_engine.cc b/src/tests/audio/test_audio_engine.cc
index 767ee15..98c6aa7 100644
--- a/src/tests/audio/test_audio_engine.cc
+++ b/src/tests/audio/test_audio_engine.cc
@@ -34,13 +34,12 @@ void test_audio_engine_music_loading() {
fixture.load_music(&g_tracker_score, g_tracker_samples,
g_tracker_sample_assets, g_tracker_samples_count);
- // Verify resource manager was initialized (samples registered but not loaded
- // yet)
+ // Verify resource manager was initialized
SpectrogramResourceManager* res_mgr = fixture.engine().get_resource_manager();
assert(res_mgr != nullptr);
- // Initially, no samples should be loaded (lazy loading)
- assert(res_mgr->get_loaded_count() == 0);
+ // After load_music_data, samples are registered (may be eagerly loaded
+ // by tracker_init if already initialized)
printf(" ✓ Music data loaded: %u samples registered\n",
g_tracker_samples_count);
@@ -59,18 +58,13 @@ void test_audio_engine_manual_resource_loading() {
g_tracker_sample_assets, g_tracker_samples_count);
SpectrogramResourceManager* res_mgr = fixture.engine().get_resource_manager();
- const int initial_loaded = res_mgr->get_loaded_count();
- assert(initial_loaded == 0); // No samples loaded yet
-
- // Manually preload first few samples
- res_mgr->preload(0);
- res_mgr->preload(1);
- const int after_preload = res_mgr->get_loaded_count();
- printf(" Samples loaded after manual preload: %d\n", after_preload);
- assert(after_preload == 2); // Should have 2 samples loaded
+ // After init + load_music, tracker_init eagerly loads all samples
+ const int initial_loaded = res_mgr->get_loaded_count();
+ printf(" Samples eagerly loaded by tracker_init: %d\n", initial_loaded);
+ assert(initial_loaded > 0); // Samples loaded eagerly at init
- // Verify samples are accessible
+ // Verify first samples are accessible
const Spectrogram* spec0 = res_mgr->get_spectrogram(0);
const Spectrogram* spec1 = res_mgr->get_spectrogram(1);
@@ -91,12 +85,8 @@ void test_audio_engine_reset() {
SpectrogramResourceManager* res_mgr = fixture.engine().get_resource_manager();
- // Manually load some samples
- res_mgr->preload(0);
- res_mgr->preload(1);
-
const int loaded_before_reset = res_mgr->get_loaded_count();
- assert(loaded_before_reset == 2);
+ assert(loaded_before_reset > 0);
// Reset engine
fixture.engine().reset();
@@ -104,11 +94,12 @@ void test_audio_engine_reset() {
// After reset, state should be cleared
assert(fixture.engine().get_active_voice_count() == 0);
- // Resources should be marked as unloaded (but memory not freed)
+ // After reset, tracker re-initializes and reloads samples eagerly
const int loaded_after_reset = res_mgr->get_loaded_count();
printf(" Loaded count before reset: %d, after reset: %d\n",
loaded_before_reset, loaded_after_reset);
- assert(loaded_after_reset == 0);
+ // Samples are re-loaded by tracker_init after reset
+ assert(loaded_after_reset == loaded_before_reset);
printf(" ✓ AudioEngine reset test passed\n");
}