summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/tracker.cc175
-rw-r--r--src/audio/tracker.h3
-rw-r--r--src/generated/music_data.cc299
-rw-r--r--src/tests/test_tracker.cc47
4 files changed, 285 insertions, 239 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)
diff --git a/src/generated/music_data.cc b/src/generated/music_data.cc
index 73ed992..a37d9c0 100644
--- a/src/generated/music_data.cc
+++ b/src/generated/music_data.cc
@@ -7,54 +7,30 @@
const NoteParams g_tracker_samples[] = {
{ 0 }, // ASSET_KICK_1 (ASSET)
{ 0 }, // ASSET_KICK_2 (ASSET)
- { 0 }, // ASSET_KICK_3 (ASSET)
{ 0 }, // ASSET_SNARE_1 (ASSET)
- { 0 }, // ASSET_SNARE_2 (ASSET)
{ 0 }, // ASSET_SNARE_3 (ASSET)
- { 0 }, // ASSET_SNARE_4 (ASSET)
{ 0 }, // ASSET_HIHAT_1 (ASSET)
{ 0 }, // ASSET_HIHAT_2 (ASSET)
- { 0 }, // ASSET_HIHAT_3 (ASSET)
- { 0 }, // ASSET_HIHAT_4 (ASSET)
{ 0 }, // ASSET_CRASH_1 (ASSET)
- { 0 }, // ASSET_RIDE_1 (ASSET)
- { 0 }, // ASSET_SPLASH_1 (ASSET)
{ 0 }, // ASSET_BASS_1 (ASSET)
- { 0 }, // ASSET_SYNTH_BASS_1 (ASSET)
{ 82.4f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // E2
{ 98.0f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // G2
- { 110.0f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // A2
- { 123.5f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // B2
{ 329.6f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // E4
{ 392.0f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // G4
{ 493.9f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // B4
- { 587.3f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // D5
- { 659.3f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // E5
{ 440.0f, 0.50f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 3, 0.6f, 0.0f, 0.0f }, // A4
};
-const uint32_t g_tracker_samples_count = 26;
+const uint32_t g_tracker_samples_count = 14;
const AssetId g_tracker_sample_assets[] = {
AssetId::ASSET_KICK_1,
AssetId::ASSET_KICK_2,
- AssetId::ASSET_KICK_3,
AssetId::ASSET_SNARE_1,
- AssetId::ASSET_SNARE_2,
AssetId::ASSET_SNARE_3,
- AssetId::ASSET_SNARE_4,
AssetId::ASSET_HIHAT_1,
AssetId::ASSET_HIHAT_2,
- AssetId::ASSET_HIHAT_3,
- AssetId::ASSET_HIHAT_4,
AssetId::ASSET_CRASH_1,
- AssetId::ASSET_RIDE_1,
- AssetId::ASSET_SPLASH_1,
AssetId::ASSET_BASS_1,
- AssetId::ASSET_SYNTH_BASS_1,
- AssetId::ASSET_LAST_ID,
- AssetId::ASSET_LAST_ID,
- AssetId::ASSET_LAST_ID,
- AssetId::ASSET_LAST_ID,
AssetId::ASSET_LAST_ID,
AssetId::ASSET_LAST_ID,
AssetId::ASSET_LAST_ID,
@@ -63,177 +39,174 @@ const AssetId g_tracker_sample_assets[] = {
AssetId::ASSET_LAST_ID,
};
-static const TrackerEvent PATTERN_EVENTS_rock_beat[] = {
+static const TrackerEvent PATTERN_EVENTS_kick_basic[] = {
{ 0.0f, 0, 1.0f, 0.0f },
- { 1.0f, 5, 0.9f, 0.1f },
{ 2.0f, 0, 1.0f, 0.0f },
- { 2.5f, 0, 0.7f, -0.1f },
- { 3.0f, 5, 0.9f, 0.1f },
+ { 2.5f, 1, 0.7f, -0.2f },
};
-static const TrackerEvent PATTERN_EVENTS_double_kick[] = {
- { 0.0f, 2, 1.0f, 0.0f },
- { 0.5f, 1, 0.8f, -0.2f },
- { 1.0f, 6, 0.9f, 0.2f },
- { 1.5f, 2, 0.7f, 0.1f },
- { 2.0f, 2, 1.0f, 0.0f },
- { 2.5f, 1, 0.8f, -0.2f },
- { 3.0f, 6, 0.9f, 0.2f },
- { 3.5f, 2, 0.6f, 0.1f },
+static const TrackerEvent PATTERN_EVENTS_snare_basic[] = {
+ { 1.0f, 2, 0.9f, 0.1f },
+ { 3.0f, 2, 0.9f, 0.1f },
};
-static const TrackerEvent PATTERN_EVENTS_hihat_8th[] = {
- { 0.0f, 8, 0.6f, -0.3f },
- { 0.5f, 7, 0.4f, 0.3f },
- { 1.0f, 8, 0.6f, -0.3f },
- { 1.5f, 7, 0.4f, 0.3f },
- { 2.0f, 8, 0.6f, -0.3f },
- { 2.5f, 7, 0.4f, 0.3f },
- { 3.0f, 8, 0.6f, -0.3f },
- { 3.5f, 7, 0.4f, 0.3f },
+static const TrackerEvent PATTERN_EVENTS_hihat_stressed[] = {
+ { 0.0f, 5, 0.8f, -0.3f },
+ { 0.5f, 4, 0.4f, 0.3f },
+ { 1.0f, 5, 0.8f, -0.3f },
+ { 1.5f, 4, 0.4f, 0.3f },
+ { 2.0f, 5, 0.8f, -0.3f },
+ { 2.5f, 4, 0.4f, 0.3f },
+ { 3.0f, 5, 0.8f, -0.3f },
+ { 3.5f, 4, 0.4f, 0.3f },
};
-static const TrackerEvent PATTERN_EVENTS_hihat_16th[] = {
- { 0.0f, 9, 0.7f, -0.4f },
- { 0.2f, 7, 0.3f, 0.4f },
- { 0.5f, 9, 0.6f, -0.2f },
- { 0.8f, 7, 0.3f, 0.2f },
- { 1.0f, 9, 0.7f, -0.4f },
- { 1.2f, 7, 0.3f, 0.4f },
- { 1.5f, 9, 0.6f, -0.2f },
- { 1.8f, 7, 0.3f, 0.2f },
- { 2.0f, 9, 0.7f, -0.4f },
- { 2.2f, 7, 0.3f, 0.4f },
- { 2.5f, 9, 0.6f, -0.2f },
- { 2.8f, 7, 0.3f, 0.2f },
- { 3.0f, 9, 0.7f, -0.4f },
- { 3.2f, 7, 0.3f, 0.4f },
- { 3.5f, 9, 0.6f, -0.2f },
- { 3.8f, 7, 0.3f, 0.2f },
-};
-static const TrackerEvent PATTERN_EVENTS_ride_pattern[] = {
- { 0.0f, 12, 0.6f, 0.5f },
- { 0.5f, 12, 0.5f, -0.3f },
- { 1.0f, 12, 0.6f, 0.5f },
- { 1.5f, 12, 0.5f, -0.3f },
- { 2.0f, 12, 0.6f, 0.5f },
- { 2.5f, 12, 0.5f, -0.3f },
- { 3.0f, 12, 0.7f, 0.5f },
- { 3.5f, 12, 0.5f, -0.3f },
-};
-static const TrackerEvent PATTERN_EVENTS_crash_accent[] = {
- { 0.0f, 11, 0.9f, 0.0f },
+static const TrackerEvent PATTERN_EVENTS_kick_dense[] = {
+ { 0.0f, 0, 1.0f, 0.0f },
+ { 0.5f, 1, 0.6f, -0.2f },
+ { 1.0f, 0, 1.0f, 0.0f },
+ { 1.5f, 1, 0.6f, 0.2f },
+ { 2.0f, 0, 1.0f, 0.0f },
+ { 2.5f, 1, 0.6f, -0.2f },
+ { 3.0f, 0, 1.0f, 0.0f },
+ { 3.5f, 1, 0.6f, 0.2f },
};
-static const TrackerEvent PATTERN_EVENTS_splash_accent[] = {
- { 0.0f, 13, 0.8f, 0.3f },
+static const TrackerEvent PATTERN_EVENTS_snare_dense[] = {
+ { 0.5f, 3, 0.7f, 0.0f },
+ { 1.0f, 2, 0.9f, 0.1f },
+ { 1.5f, 3, 0.7f, 0.0f },
+ { 2.5f, 3, 0.7f, 0.0f },
+ { 3.0f, 2, 0.9f, 0.1f },
+ { 3.5f, 3, 0.7f, 0.0f },
};
-static const TrackerEvent PATTERN_EVENTS_snare_fill[] = {
- { 0.0f, 4, 0.6f, -0.4f },
- { 0.2f, 4, 0.6f, -0.2f },
- { 0.5f, 4, 0.7f, 0.0f },
- { 0.8f, 5, 0.8f, 0.2f },
- { 1.0f, 5, 0.8f, 0.4f },
- { 1.2f, 6, 0.9f, 0.2f },
- { 1.5f, 6, 0.9f, 0.0f },
- { 1.8f, 6, 0.9f, -0.2f },
+static const TrackerEvent PATTERN_EVENTS_crash[] = {
+ { 0.0f, 6, 0.9f, 0.0f },
};
-static const TrackerEvent PATTERN_EVENTS_power_riff[] = {
- { 0.0f, 16, 0.8f, -0.5f },
- { 0.5f, 16, 0.6f, -0.5f },
- { 1.0f, 17, 0.8f, -0.3f },
- { 1.5f, 17, 0.6f, -0.3f },
- { 2.0f, 18, 0.8f, 0.3f },
- { 2.5f, 18, 0.6f, 0.3f },
- { 3.0f, 19, 0.8f, 0.5f },
- { 3.5f, 19, 0.6f, 0.5f },
+static const TrackerEvent PATTERN_EVENTS_bass_e[] = {
+ { 0.0f, 8, 0.9f, 0.0f },
+ { 1.0f, 8, 0.7f, 0.0f },
+ { 2.0f, 8, 0.9f, 0.0f },
+ { 2.5f, 8, 0.6f, 0.0f },
+ { 3.0f, 8, 0.7f, 0.0f },
};
-static const TrackerEvent PATTERN_EVENTS_lead_melody[] = {
- { 0.0f, 20, 0.7f, 0.0f },
- { 0.5f, 21, 0.6f, 0.1f },
- { 1.0f, 22, 0.7f, -0.1f },
- { 1.5f, 23, 0.6f, 0.2f },
- { 2.0f, 24, 0.8f, -0.2f },
- { 2.5f, 23, 0.6f, 0.1f },
- { 3.0f, 22, 0.7f, 0.0f },
- { 3.5f, 25, 0.6f, -0.1f },
+static const TrackerEvent PATTERN_EVENTS_bass_eg[] = {
+ { 0.0f, 8, 0.9f, 0.0f },
+ { 1.0f, 8, 0.7f, 0.0f },
+ { 2.0f, 9, 0.9f, 0.0f },
+ { 3.0f, 9, 0.7f, 0.0f },
};
-static const TrackerEvent PATTERN_EVENTS_bass_line[] = {
- { 0.0f, 16, 0.9f, 0.0f },
- { 1.0f, 16, 0.8f, 0.0f },
- { 2.0f, 16, 0.9f, 0.0f },
- { 2.5f, 16, 0.7f, 0.0f },
- { 3.0f, 17, 0.9f, 0.0f },
+static const TrackerEvent PATTERN_EVENTS_melody_em[] = {
+ { 0.0f, 10, 0.7f, 0.0f },
+ { 0.5f, 11, 0.6f, 0.1f },
+ { 1.0f, 12, 0.7f, -0.1f },
+ { 2.0f, 13, 0.6f, 0.0f },
+ { 2.5f, 11, 0.6f, 0.1f },
+ { 3.0f, 10, 0.7f, 0.0f },
};
const TrackerPattern g_tracker_patterns[] = {
- { PATTERN_EVENTS_rock_beat, 5, 4.0f }, // rock_beat
- { PATTERN_EVENTS_double_kick, 8, 4.0f }, // double_kick
- { PATTERN_EVENTS_hihat_8th, 8, 4.0f }, // hihat_8th
- { PATTERN_EVENTS_hihat_16th, 16, 4.0f }, // hihat_16th
- { PATTERN_EVENTS_ride_pattern, 8, 4.0f }, // ride_pattern
- { PATTERN_EVENTS_crash_accent, 1, 4.0f }, // crash_accent
- { PATTERN_EVENTS_splash_accent, 1, 4.0f }, // splash_accent
- { PATTERN_EVENTS_snare_fill, 8, 4.0f }, // snare_fill
- { PATTERN_EVENTS_power_riff, 8, 4.0f }, // power_riff
- { PATTERN_EVENTS_lead_melody, 8, 4.0f }, // lead_melody
- { PATTERN_EVENTS_bass_line, 5, 4.0f }, // bass_line
+ { PATTERN_EVENTS_kick_basic, 3, 4.0f }, // kick_basic
+ { PATTERN_EVENTS_snare_basic, 2, 4.0f }, // snare_basic
+ { PATTERN_EVENTS_hihat_stressed, 8, 4.0f }, // hihat_stressed
+ { PATTERN_EVENTS_kick_dense, 8, 4.0f }, // kick_dense
+ { PATTERN_EVENTS_snare_dense, 6, 4.0f }, // snare_dense
+ { PATTERN_EVENTS_crash, 1, 4.0f }, // crash
+ { PATTERN_EVENTS_bass_e, 5, 4.0f }, // bass_e
+ { PATTERN_EVENTS_bass_eg, 4, 4.0f }, // bass_eg
+ { PATTERN_EVENTS_melody_em, 6, 4.0f }, // melody_em
};
-const uint32_t g_tracker_patterns_count = 11;
+const uint32_t g_tracker_patterns_count = 9;
static const TrackerPatternTrigger SCORE_TRIGGERS[] = {
{ 0.0f, 5 },
- { 0.0f, 2 },
{ 0.0f, 0 },
- { 2.0f, 2 },
+ { 0.0f, 1 },
+ { 0.0f, 2 },
{ 2.0f, 0 },
+ { 2.0f, 1 },
+ { 2.0f, 2 },
{ 4.0f, 5 },
- { 4.0f, 2 },
{ 4.0f, 0 },
- { 4.0f, 10 },
- { 6.0f, 2 },
+ { 4.0f, 1 },
+ { 4.0f, 2 },
{ 6.0f, 0 },
- { 6.0f, 10 },
- { 8.0f, 6 },
- { 8.0f, 3 },
+ { 6.0f, 1 },
+ { 6.0f, 2 },
+ { 8.0f, 5 },
+ { 8.0f, 0 },
{ 8.0f, 1 },
- { 8.0f, 10 },
- { 8.0f, 9 },
- { 10.0f, 3 },
+ { 8.0f, 2 },
+ { 10.0f, 5 },
+ { 10.0f, 0 },
{ 10.0f, 1 },
- { 10.0f, 10 },
- { 10.0f, 9 },
- { 12.0f, 5 },
- { 12.0f, 4 },
+ { 10.0f, 2 },
+ { 10.0f, 6 },
+ { 12.0f, 0 },
{ 12.0f, 1 },
- { 12.0f, 8 },
- { 14.0f, 4 },
+ { 12.0f, 2 },
+ { 12.0f, 6 },
+ { 14.0f, 0 },
{ 14.0f, 1 },
- { 14.0f, 8 },
- { 16.0f, 7 },
+ { 14.0f, 2 },
+ { 14.0f, 6 },
+ { 16.0f, 5 },
{ 16.0f, 3 },
- { 17.0f, 6 },
- { 17.0f, 2 },
- { 17.0f, 0 },
- { 17.0f, 10 },
- { 18.0f, 5 },
+ { 16.0f, 4 },
+ { 16.0f, 2 },
+ { 16.0f, 6 },
{ 18.0f, 3 },
- { 18.0f, 1 },
- { 18.0f, 10 },
- { 18.0f, 9 },
+ { 18.0f, 4 },
+ { 18.0f, 2 },
+ { 18.0f, 7 },
+ { 20.0f, 5 },
{ 20.0f, 3 },
- { 20.0f, 1 },
- { 20.0f, 10 },
- { 20.0f, 8 },
- { 22.0f, 7 },
+ { 20.0f, 4 },
+ { 20.0f, 2 },
+ { 20.0f, 6 },
{ 22.0f, 3 },
- { 23.0f, 5 },
- { 23.0f, 0 },
- { 23.0f, 10 },
- { 25.0f, 5 },
+ { 22.0f, 4 },
+ { 22.0f, 2 },
+ { 22.0f, 6 },
+ { 24.0f, 3 },
+ { 24.0f, 4 },
+ { 24.0f, 2 },
+ { 24.0f, 7 },
+ { 26.0f, 5 },
+ { 26.0f, 0 },
+ { 26.0f, 1 },
+ { 26.0f, 2 },
+ { 26.0f, 6 },
+ { 28.0f, 0 },
+ { 28.0f, 1 },
+ { 28.0f, 2 },
+ { 28.0f, 7 },
+ { 30.0f, 5 },
+ { 30.0f, 0 },
+ { 30.0f, 1 },
+ { 30.0f, 2 },
+ { 30.0f, 6 },
+ { 30.0f, 8 },
+ { 32.0f, 0 },
+ { 32.0f, 1 },
+ { 32.0f, 2 },
+ { 32.0f, 7 },
+ { 32.0f, 8 },
+ { 34.0f, 0 },
+ { 34.0f, 1 },
+ { 34.0f, 2 },
+ { 34.0f, 6 },
+ { 34.0f, 8 },
+ { 36.0f, 5 },
+ { 36.0f, 0 },
+ { 36.0f, 1 },
+ { 36.0f, 2 },
+ { 36.0f, 7 },
+ { 36.0f, 8 },
+ { 38.0f, 5 },
};
const TrackerScore g_tracker_score = {
- SCORE_TRIGGERS, 49, 120.0f
+ SCORE_TRIGGERS, 85, 120.0f
};
// Resource usage analysis:
-// Maximum simultaneous pattern triggers: 5
-// Recommended MAX_VOICES: 10 (current: see synth.h)
-// Recommended MAX_SPECTROGRAMS: 10 (current: see synth.h)
+// Maximum simultaneous pattern triggers: 6
+// Recommended MAX_VOICES: 12 (current: see synth.h)
+// Recommended MAX_SPECTROGRAMS: 12 (current: see synth.h)
diff --git a/src/tests/test_tracker.cc b/src/tests/test_tracker.cc
index ea1debd..7ef7172 100644
--- a/src/tests/test_tracker.cc
+++ b/src/tests/test_tracker.cc
@@ -24,25 +24,42 @@ void test_tracker_pattern_triggering() {
synth_init();
tracker_init();
- // Test 1: Trigger patterns at 0.0f
+ // At time 0.0f, 4 patterns are triggered:
+ // - crash (1 event at beat 0.0)
+ // - kick_basic (events at beat 0.0, 2.0, 2.5)
+ // - snare_basic (events at beat 1.0, 3.0)
+ // - hihat_stressed (events at beat 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5)
+ // With the new event-based triggering, only events at beat 0.0 trigger immediately.
+
+ // Test 1: At music_time = 0.0f, events at beat 0.0 trigger
tracker_update(0.0f);
- printf("Actual active voice count: %d\n", synth_get_active_voice_count());
- // Expect 4 voices (one for each pattern triggered at 0.0f:
- // crash, kick_basic, snare_basic, hihat_stressed)
- assert(synth_get_active_voice_count() == 4);
+ printf("Actual active voice count at 0.0f: %d\n",
+ synth_get_active_voice_count());
+ // Expect 3 voices: crash (beat 0.0), kick_basic (beat 0.0), hihat_stressed
+ // (beat 0.0)
+ assert(synth_get_active_voice_count() == 3);
- // Test 2: Advance time slightly
- tracker_update(0.1f);
+ // Test 2: At music_time = 0.25f (beat 0.5 @ 120 BPM), hihat event triggers
+ // beat_duration = 60.0f / 120.0f = 0.5s per beat
+ // beat 0.5 = 0.25s
+ tracker_update(0.25f);
+ printf("Actual active voice count at 0.25f: %d\n",
+ synth_get_active_voice_count());
+ // Expect 4 voices (3 previous + 1 new hihat)
assert(synth_get_active_voice_count() == 4);
- // Test 3: Advance further, no new triggers until 4.0f
- tracker_update(3.0f);
- // Voices from 0.0f triggers might have ended, but new ones haven't started.
- // synth_get_active_voice_count might drop if previous voices ended.
- // For this test, we assume voices triggered at 0.0f are still active for a
- // short duration. A more robust test would check for specific spectrograms or
- // mock synth. For now, we expect voices to still be somewhat active or new
- // ones to be triggered if there's overlap
+ // Test 3: At music_time = 0.5f (beat 1.0), snare event triggers
+ tracker_update(0.5f);
+ printf("Actual active voice count at 0.5f: %d\n",
+ synth_get_active_voice_count());
+ // Expect 6 voices (4 previous + snare + hihat at beat 1.0)
+ assert(synth_get_active_voice_count() == 6);
+
+ // Test 4: Advance further to 2.0f (beat 4.0), new pattern triggers at 2.0f
+ tracker_update(2.0f);
+ printf("Actual active voice count at 2.0f: %d\n",
+ synth_get_active_voice_count());
+ // Multiple events have triggered by now
assert(synth_get_active_voice_count() > 0);
printf("Tracker pattern triggering test PASSED\n");