From 0bd2d4eae458d9017de4e2c4e04c1c1cc5315520 Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 3 Feb 2026 12:47:27 +0100 Subject: feat(audio): Fix tracker bugs and implement rock demo track Critical Bug Fixes: - Fixed pool exhaustion: Tracker slots never freed after use, music stopped after 8 patterns. Implemented round-robin allocation with cleanup. - Fixed note name parsing: Added automatic note-to-frequency conversion in tracker_compiler. Bass and melody now play correctly. - Fixed timing mismatch: Patterns are 2 seconds but triggered every 4 seconds, causing silence gaps. Updated SCORE to trigger every 2 seconds. Improvements: - Implemented dynamic resource sizing in tracker_compiler: Analyzes score to determine optimal MAX_VOICES/MAX_SPECTROGRAMS values. - Created comprehensive rock track: 11 patterns with drums, bass, power chords, and lead melody over 25 seconds. - Added 213 lines of asset system documentation with 8 prioritized tasks. Known Issues for next session: - Audio quality could be improved (some artifacts remain) - Note synthesis uses default parameters, needs tuning - Pattern overlaps might cause voice exhaustion under heavy load Files Changed: - src/audio/tracker.cc: Round-robin pool allocation, cleanup logic - tools/tracker_compiler.cc: Note name parser, resource usage analysis - src/audio/synth.h: Increased limits to 16 based on analysis - assets/music.track: 230-line rock arrangement - doc/ASSET_SYSTEM.md: Comprehensive documentation + 8 tasks - TODO.md: Updated with recent completions and known issues handoff(Gemini): Music system now functional but needs quality improvements. Audio artifacts and synthesis tuning remain. See TODO.md for details. Co-Authored-By: Claude Sonnet 4.5 --- src/audio/tracker.cc | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) (limited to 'src/audio/tracker.cc') diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc index 5d99a45..470123a 100644 --- a/src/audio/tracker.cc +++ b/src/audio/tracker.cc @@ -15,9 +15,11 @@ struct ManagedSpectrogram { // Simple pool for dynamic spectrograms static ManagedSpectrogram g_spec_pool[MAX_SPECTROGRAMS]; +static int g_next_pool_slot = 0; // Round-robin allocation void tracker_init() { g_last_trigger_idx = 0; + g_next_pool_slot = 0; for (int i = 0; i < MAX_SPECTROGRAMS; ++i) { g_spec_pool[i].synth_id = -1; g_spec_pool[i].data = nullptr; @@ -26,11 +28,17 @@ void tracker_init() { } 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; } - return -1; + + // If all slots are active, reuse the oldest one (round-robin) + // This automatically handles cleanup of old patterns + const int slot = g_next_pool_slot; + g_next_pool_slot = (g_next_pool_slot + 1) % MAX_SPECTROGRAMS; + return slot; } void tracker_update(float time_sec) { @@ -50,7 +58,7 @@ void tracker_update(float time_sec) { for (uint32_t i = 0; i < pattern.num_events; ++i) { const TrackerEvent& event = pattern.events[i]; - + std::vector note_data; int note_frames = 0; @@ -77,27 +85,32 @@ void tracker_update(float time_sec) { } if (dest_num_frames > 0) { - int slot = get_free_pool_slot(); - if (slot != -1) { - if (g_spec_pool[slot].synth_id != -1) { - synth_unregister_spectrogram(g_spec_pool[slot].synth_id); - delete[] g_spec_pool[slot].data; - } + const int slot = get_free_pool_slot(); - g_spec_pool[slot].data = new float[pattern_data.size()]; - memcpy(g_spec_pool[slot].data, pattern_data.data(), - pattern_data.size() * sizeof(float)); + // Clean up old data in this slot if reusing + if (g_spec_pool[slot].synth_id != -1) { + synth_unregister_spectrogram(g_spec_pool[slot].synth_id); + g_spec_pool[slot].synth_id = -1; + } + if (g_spec_pool[slot].data != nullptr) { + delete[] g_spec_pool[slot].data; + g_spec_pool[slot].data = nullptr; + } - Spectrogram spec; - spec.spectral_data_a = g_spec_pool[slot].data; - spec.spectral_data_b = g_spec_pool[slot].data; - spec.num_frames = dest_num_frames; + // Allocate and register new pattern + g_spec_pool[slot].data = new float[pattern_data.size()]; + memcpy(g_spec_pool[slot].data, pattern_data.data(), + pattern_data.size() * sizeof(float)); - g_spec_pool[slot].synth_id = synth_register_spectrogram(&spec); - g_spec_pool[slot].active = true; + Spectrogram spec; + spec.spectral_data_a = g_spec_pool[slot].data; + spec.spectral_data_b = g_spec_pool[slot].data; + spec.num_frames = dest_num_frames; - synth_trigger_voice(g_spec_pool[slot].synth_id, 1.0f, 0.0f); - } + g_spec_pool[slot].synth_id = synth_register_spectrogram(&spec); + g_spec_pool[slot].active = true; + + synth_trigger_voice(g_spec_pool[slot].synth_id, 1.0f, 0.0f); } g_last_trigger_idx++; -- cgit v1.2.3