summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-03 12:47:27 +0100
committerskal <pascal.massimino@gmail.com>2026-02-03 12:47:27 +0100
commit0bd2d4eae458d9017de4e2c4e04c1c1cc5315520 (patch)
tree57d8a2659757dcde284ed0a4338718b4d87924ef /src/audio
parent69a12784eb11784a187d18a6a88b23a1026d123c (diff)
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 <noreply@anthropic.com>
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/synth.h5
-rw-r--r--src/audio/tracker.cc51
2 files changed, 36 insertions, 20 deletions
diff --git a/src/audio/synth.h b/src/audio/synth.h
index a0720f2..a8f15a9 100644
--- a/src/audio/synth.h
+++ b/src/audio/synth.h
@@ -7,8 +7,11 @@
#include "dct.h"
#include <cstdint>
+// Based on tracker score analysis (see generated/music_data.cc)
+// Max simultaneous patterns: 5, recommended: 10 each
+// Using 16 for comfortable headroom
#define MAX_VOICES 16
-#define MAX_SPECTROGRAMS 8
+#define MAX_SPECTROGRAMS 16
struct Spectrogram {
const float* spectral_data_a; // Front buffer
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<float> 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++;