summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/tracker.cc175
-rw-r--r--src/audio/tracker.h3
2 files changed, 117 insertions, 61 deletions
diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc
index 8f3da38..cb97f23 100644
--- a/src/audio/tracker.cc
+++ b/src/audio/tracker.cc
@@ -7,13 +7,23 @@
static uint32_t g_last_trigger_idx = 0;
+// Active pattern instance tracking
+struct ActivePattern {
+ uint16_t pattern_id;
+ float start_music_time; // When this pattern was triggered (music time)
+ uint32_t next_event_idx; // Next event to trigger within this pattern
+ bool active;
+};
+
+static ActivePattern g_active_patterns[MAX_SPECTROGRAMS];
+
struct ManagedSpectrogram {
int synth_id;
float* data;
bool active;
};
-// Simple pool for dynamic spectrograms
+// 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
@@ -24,6 +34,14 @@ void tracker_init() {
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;
+ }
+}
+
+void tracker_reset() {
+ g_last_trigger_idx = 0;
+ for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
+ g_active_patterns[i].active = false;
}
}
@@ -41,81 +59,118 @@ static int get_free_pool_slot() {
return slot;
}
-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];
+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
+}
- if (trigger.time_sec > time_sec)
- break;
+// Helper to trigger a single note event
+static void trigger_note_event(const TrackerEvent& event) {
+ std::vector<float> note_data;
+ int note_frames = 0;
+
+ // Load or generate the note spectrogram
+ AssetId aid = g_tracker_sample_assets[event.sample_id];
+ if (aid != AssetId::ASSET_LAST_ID) {
+ size_t size;
+ const uint8_t* data = GetAsset(aid, &size);
+ if (data && size >= sizeof(SpecHeader)) {
+ const SpecHeader* header = (const SpecHeader*)data;
+ note_frames = header->num_frames;
+ const float* src_spectral_data =
+ (const float*)(data + sizeof(SpecHeader));
+ note_data.assign(src_spectral_data,
+ src_spectral_data + (size_t)note_frames * DCT_SIZE);
+ }
+ } else {
+ const NoteParams& params = g_tracker_samples[event.sample_id];
+ note_data = generate_note_spectrogram(params, &note_frames);
+ }
- const TrackerPattern& pattern = g_tracker_patterns[trigger.pattern_id];
+ if (note_frames > 0) {
+ const int slot = get_free_pool_slot();
- int dest_num_frames = 0;
- std::vector<float> pattern_data;
+ // 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;
+ }
- float beat_to_sec = 60.0f / g_tracker_score.bpm;
+ // Allocate and register new note
+ g_spec_pool[slot].data = new float[note_data.size()];
+ memcpy(g_spec_pool[slot].data, note_data.data(),
+ note_data.size() * sizeof(float));
- for (uint32_t i = 0; i < pattern.num_events; ++i) {
- const TrackerEvent& event = pattern.events[i];
+ 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;
- std::vector<float> note_data;
- int note_frames = 0;
+ 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, event.volume, event.pan);
+ }
+}
- AssetId aid = g_tracker_sample_assets[event.sample_id];
- if (aid != AssetId::ASSET_LAST_ID) {
- size_t size;
- const uint8_t* data = GetAsset(aid, &size);
- if (data && size >= sizeof(SpecHeader)) {
- const SpecHeader* header = (const SpecHeader*)data;
- note_frames = header->num_frames;
- const float* src_spectral_data =
- (const float*)(data + sizeof(SpecHeader));
- note_data.assign(src_spectral_data,
- src_spectral_data + (size_t)note_frames * DCT_SIZE);
- }
- } else {
- const NoteParams& params = g_tracker_samples[event.sample_id];
- note_data = generate_note_spectrogram(params, &note_frames);
- }
+void tracker_update(float music_time_sec) {
+ // Step 1: Process new pattern triggers
+ while (g_last_trigger_idx < g_tracker_score.num_triggers) {
+ const TrackerPatternTrigger& trigger =
+ g_tracker_score.triggers[g_last_trigger_idx];
- if (note_frames > 0) {
- 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);
- }
+ if (trigger.time_sec > music_time_sec)
+ break;
+
+ // Add this pattern to active patterns list
+ const int slot = get_free_pattern_slot();
+ if (slot != -1) {
+ g_active_patterns[slot].pattern_id = trigger.pattern_id;
+ g_active_patterns[slot].start_music_time = trigger.time_sec;
+ g_active_patterns[slot].next_event_idx = 0;
+ g_active_patterns[slot].active = true;
}
- if (dest_num_frames > 0) {
- const int slot = get_free_pool_slot();
+ g_last_trigger_idx++;
+ }
- // 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;
- }
+ // Step 2: Update all active patterns and trigger individual events
+ const float beat_duration = 60.0f / g_tracker_score.bpm;
+
+ for (int i = 0; i < MAX_SPECTROGRAMS; ++i) {
+ if (!g_active_patterns[i].active)
+ continue;
- // 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));
+ ActivePattern& active = g_active_patterns[i];
+ const TrackerPattern& pattern = g_tracker_patterns[active.pattern_id];
- 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;
+ // Calculate elapsed beats since pattern started
+ const float elapsed_music_time = music_time_sec - active.start_music_time;
+ const float elapsed_beats = elapsed_music_time / beat_duration;
- g_spec_pool[slot].synth_id = synth_register_spectrogram(&spec);
- g_spec_pool[slot].active = true;
+ // Trigger all events that have passed their beat time
+ while (active.next_event_idx < pattern.num_events) {
+ const TrackerEvent& event = pattern.events[active.next_event_idx];
- synth_trigger_voice(g_spec_pool[slot].synth_id, 1.0f, 0.0f);
+ if (event.beat > elapsed_beats)
+ break; // This event hasn't reached its time yet
+
+ // Trigger this event as an individual voice
+ trigger_note_event(event);
+
+ active.next_event_idx++;
}
- g_last_trigger_idx++;
+ // If all events have been triggered, mark pattern as complete
+ if (active.next_event_idx >= pattern.num_events) {
+ active.active = false;
+ }
}
}
diff --git a/src/audio/tracker.h b/src/audio/tracker.h
index 49fcd3c..e6d479a 100644
--- a/src/audio/tracker.h
+++ b/src/audio/tracker.h
@@ -41,4 +41,5 @@ extern const uint32_t g_tracker_patterns_count;
extern const TrackerScore g_tracker_score;
void tracker_init();
-void tracker_update(float time_sec);
+void tracker_update(float music_time_sec);
+void tracker_reset(); // Reset tracker state (for tests/seeking)