summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-02 22:05:50 +0100
committerskal <pascal.massimino@gmail.com>2026-02-02 22:05:50 +0100
commit5d2ec00c0179d1ee1b07883511d34dac272c680f (patch)
treebdb50394baa8c5df1ac64e39a9560b0d5d8e0fcc /src/audio
parent4fc02a8d2acf1eafce36c1348261890d54b8b5b5 (diff)
feat: Integrate tracker system and update project context documentation
- Implemented the basic tracker system with runtime support (tracker.h, tracker.cc). - Added a sample music track file (assets/music.track). - Created a tracker compiler tool (tools/tracker_compiler.cc) to generate music data. - Updated CMakeLists.txt to build the tracker compiler and integrate generated data. - Updated GEMINI.md to reflect new file locations and project context.
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/tracker.cc95
-rw-r--r--src/audio/tracker.h42
2 files changed, 137 insertions, 0 deletions
diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc
new file mode 100644
index 0000000..0b57ce3
--- /dev/null
+++ b/src/audio/tracker.cc
@@ -0,0 +1,95 @@
+// This file is part of the 64k demo project.
+// It implements the tracker runtime logic.
+
+#include "tracker.h"
+#include "audio/synth.h"
+#include <vector>
+
+static uint32_t g_last_trigger_idx = 0;
+
+struct ManagedSpectrogram {
+ int synth_id;
+ float* data;
+ bool active;
+};
+
+// Simple pool for dynamic spectrograms
+static ManagedSpectrogram g_spec_pool[MAX_SPECTROGRAMS];
+
+void tracker_init() {
+ g_last_trigger_idx = 0;
+ 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;
+ }
+}
+
+static int get_free_pool_slot() {
+ for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
+ if (!g_spec_pool[i].active)
+ return i;
+ }
+ // If no free slot, find one where the synth voice is inactive
+ // (In a real implementation, we'd check if any voice is still using this)
+ // For now, just wrap around or return -1
+ return -1;
+}
+
+void tracker_update(float time_sec) {
+ while (g_last_trigger_idx < g_tracker_score.num_triggers) {
+ const TrackerPatternTrigger& trigger =
+ g_tracker_score.triggers[g_last_trigger_idx];
+
+ if (trigger.time_sec > time_sec)
+ break;
+
+ // Trigger pattern!
+ const TrackerPattern& pattern = g_tracker_patterns[trigger.pattern_id];
+
+ // Generate spectrogram for the pattern
+ int dest_num_frames = 0;
+ std::vector<float> pattern_data;
+
+ float beat_to_sec = 60.0f / g_tracker_score.bpm;
+
+ for (uint32_t i = 0; i < pattern.num_events; ++i) {
+ const TrackerEvent& event = pattern.events[i];
+ const NoteParams& params = g_tracker_samples[event.sample_id];
+
+ int note_frames = 0;
+ std::vector<float> note_data =
+ generate_note_spectrogram(params, &note_frames);
+
+ int frame_offset = (int)(event.beat * beat_to_sec * 32000.0f / DCT_SIZE);
+ paste_spectrogram(pattern_data, &dest_num_frames, note_data, note_frames,
+ frame_offset);
+ }
+
+ // Register with synth
+ int slot = get_free_pool_slot();
+ if (slot != -1) {
+ // Clean up old if needed
+ if (g_spec_pool[slot].synth_id != -1) {
+ synth_unregister_spectrogram(g_spec_pool[slot].synth_id);
+ delete[] g_spec_pool[slot].data;
+ }
+
+ g_spec_pool[slot].data = new float[pattern_data.size()];
+ memcpy(g_spec_pool[slot].data, pattern_data.data(),
+ pattern_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 = dest_num_frames;
+
+ 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++;
+ }
+}
diff --git a/src/audio/tracker.h b/src/audio/tracker.h
new file mode 100644
index 0000000..d97b483
--- /dev/null
+++ b/src/audio/tracker.h
@@ -0,0 +1,42 @@
+// This file is part of the 64k demo project.
+// It defines the tracker system for music composition.
+
+#pragma once
+
+#include "audio/gen.h"
+#include <cstdint>
+
+struct TrackerEvent {
+ float beat;
+ uint16_t sample_id;
+ float volume;
+ float pan;
+};
+
+struct TrackerPattern {
+ const TrackerEvent* events;
+ uint32_t num_events;
+ float num_beats;
+};
+
+struct TrackerPatternTrigger {
+ float time_sec;
+ uint16_t pattern_id;
+ // Modifiers could be added here
+};
+
+struct TrackerScore {
+ const TrackerPatternTrigger* triggers;
+ uint32_t num_triggers;
+ float bpm;
+};
+
+// Global music data generated by tracker_compiler
+extern const NoteParams g_tracker_samples[];
+extern const uint32_t g_tracker_samples_count;
+extern const TrackerPattern g_tracker_patterns[];
+extern const uint32_t g_tracker_patterns_count;
+extern const TrackerScore g_tracker_score;
+
+void tracker_init();
+void tracker_update(float time_sec);