diff options
Diffstat (limited to 'src/audio')
| -rw-r--r-- | src/audio/tracker.cc | 175 | ||||
| -rw-r--r-- | src/audio/tracker.h | 3 |
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, ¬e_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, ¬e_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) |
