summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt47
-rw-r--r--src/audio/tracker.cc1
-rw-r--r--src/generated/music_data.cc47
-rw-r--r--src/gpu/effects/shader_composer.cc42
-rw-r--r--src/gpu/effects/shader_composer.h21
-rw-r--r--src/gpu/effects/shaders.cc10
-rw-r--r--src/gpu/gpu.h4
-rw-r--r--src/main.cc148
-rw-r--r--src/tests/test_maths.cc3
-rw-r--r--src/tests/test_shader_composer.cc50
-rw-r--r--src/tests/test_tracker.cc69
-rw-r--r--tools/tracker_compiler.cc263
12 files changed, 410 insertions, 295 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7825e6a..706f1c0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -121,6 +121,20 @@ else()
set(TRACKER_COMPILER_DEPENDS tracker_compiler)
endif()
+# Always build a host-native tracker_compiler for code generation purposes
+add_executable(tracker_compiler_host tools/tracker_compiler.cc)
+set_target_properties(tracker_compiler_host PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools_host")
+
+# Determine which tracker compiler to use for code generation
+if (DEMO_CROSS_COMPILE_WIN32)
+ set(TRACKER_COMPILER_FINAL_CMD "${CMAKE_SOURCE_DIR}/build_native/tools_host/tracker_compiler_host")
+ set(TRACKER_COMPILER_FINAL_DEPENDS tracker_compiler_host)
+else()
+ set(TRACKER_COMPILER_FINAL_CMD ${TRACKER_COMPILER_CMD})
+ set(TRACKER_COMPILER_FINAL_DEPENDS ${TRACKER_COMPILER_DEPENDS})
+endif()
+
#-- - Code Generation Helpers -- -
function(pack_assets NAME INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME)
set(OUT_H ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/${NAME}.h)
@@ -166,14 +180,25 @@ add_custom_command(
)
add_custom_target(generate_timeline ALL DEPENDS ${GENERATED_TIMELINE_CC})
+set(TRACKER_MUSIC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/assets/music.track)
+set(GENERATED_MUSIC_DATA_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/music_data.cc)
+add_custom_command(
+ OUTPUT ${GENERATED_MUSIC_DATA_CC}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated
+ COMMAND ${TRACKER_COMPILER_FINAL_CMD} ${TRACKER_MUSIC_PATH} ${GENERATED_MUSIC_DATA_CC}
+ DEPENDS ${TRACKER_COMPILER_FINAL_DEPENDS} ${TRACKER_MUSIC_PATH} tracker_compiler_host
+ COMMENT "Compiling tracker music..."
+)
+add_custom_target(generate_tracker_music ALL DEPENDS ${GENERATED_MUSIC_DATA_CC})
+
pack_assets(assets ${CMAKE_CURRENT_SOURCE_DIR}/assets/final/demo_assets.txt GEN_DEMO_H GEN_DEMO_CC generate_demo_assets)
pack_test_assets(test_assets ${CMAKE_CURRENT_SOURCE_DIR}/assets/final/test_assets_list.txt GEN_TEST_H GEN_TEST_CC generate_test_assets)
set(UTIL_SOURCES src/util/asset_manager.cc)
#-- - Main Demo -- -
-add_executable(demo64k src/main.cc ${PLATFORM_SOURCES} ${GPU_SOURCES} ${AUDIO_SOURCES} ${PROCEDURAL_SOURCES} ${UTIL_SOURCES} ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC})
-add_dependencies(demo64k generate_demo_assets generate_timeline)
+add_executable(demo64k src/main.cc ${PLATFORM_SOURCES} ${GPU_SOURCES} ${AUDIO_SOURCES} ${PROCEDURAL_SOURCES} ${UTIL_SOURCES} ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC})
+add_dependencies(demo64k generate_demo_assets generate_timeline generate_tracker_music)
target_link_libraries(demo64k PRIVATE ${DEMO_LIBS})
#Size optimizations
@@ -202,9 +227,15 @@ if(DEMO_BUILD_TESTS)
add_executable(test_synth src/tests/test_synth.cc src/audio/synth.cc src/audio/idct.cc src/audio/window.cc)
add_test(NAME SynthEngineTest COMMAND test_synth)
- add_executable(test_spectool src/tests/test_spectool.cc ${AUDIO_SOURCES} ${PLATFORM_SOURCES})
+ add_executable(test_tracker src/tests/test_tracker.cc src/audio/synth.cc src/audio/gen.cc src/audio/tracker.cc src/audio/window.cc src/audio/fdct.cc src/audio/idct.cc ${GENERATED_MUSIC_DATA_CC})
+ target_link_libraries(test_tracker PRIVATE ${DEMO_LIBS})
+ add_dependencies(test_tracker generate_tracker_music)
+ add_test(NAME TrackerSystemTest COMMAND test_tracker)
+
+ add_executable(test_spectool src/tests/test_spectool.cc ${AUDIO_SOURCES} ${PLATFORM_SOURCES} ${GENERATED_MUSIC_DATA_CC})
target_compile_definitions(test_spectool PRIVATE DEMO_BUILD_TOOLS)
target_link_libraries(test_spectool PRIVATE ${DEMO_LIBS})
+ add_dependencies(test_spectool generate_tracker_music)
add_test(NAME SpectoolEndToEndTest COMMAND test_spectool)
add_executable(test_assets src/tests/test_assets.cc ${UTIL_SOURCES} ${PROCEDURAL_SOURCES} ${GEN_TEST_CC})
@@ -228,19 +259,21 @@ if(DEMO_BUILD_TESTS)
add_executable(test_shader_composer src/tests/test_shader_composer.cc src/gpu/effects/shader_composer.cc)
add_test(NAME ShaderComposerTest COMMAND test_shader_composer)
- add_executable(test_texture_manager src/tests/test_texture_manager.cc ${GPU_SOURCES} ${PLATFORM_SOURCES} ${PROCEDURAL_SOURCES} ${AUDIO_SOURCES} ${GENERATED_TIMELINE_CC} ${UTIL_SOURCES} ${GEN_DEMO_CC})
+ add_executable(test_texture_manager src/tests/test_texture_manager.cc ${GPU_SOURCES} ${PLATFORM_SOURCES} ${PROCEDURAL_SOURCES} ${AUDIO_SOURCES} ${GENERATED_TIMELINE_CC} ${UTIL_SOURCES} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC})
target_link_libraries(test_texture_manager PRIVATE ${DEMO_LIBS})
- add_test(NAME TextureManagerTest COMMAND test_texture_manager)
+ add_dependencies(test_texture_manager generate_timeline generate_demo_assets generate_tracker_music)
- add_executable(test_3d_render src/tests/test_3d_render.cc src/3d/renderer.cc ${GPU_SOURCES} ${PLATFORM_SOURCES} ${PROCEDURAL_SOURCES} ${AUDIO_SOURCES} ${GENERATED_TIMELINE_CC} ${UTIL_SOURCES} ${GEN_DEMO_CC})
+ add_executable(test_3d_render src/tests/test_3d_render.cc src/3d/renderer.cc ${GPU_SOURCES} ${PLATFORM_SOURCES} ${PROCEDURAL_SOURCES} ${AUDIO_SOURCES} ${GENERATED_TIMELINE_CC} ${UTIL_SOURCES} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC})
target_link_libraries(test_3d_render PRIVATE ${DEMO_LIBS})
+ add_dependencies(test_3d_render generate_timeline generate_demo_assets generate_tracker_music)
endif()
#-- - Extra Tools -- -
if(DEMO_BUILD_TOOLS OR DEMO_BUILD_TESTS)
- add_executable(spectool tools/spectool.cc ${PLATFORM_SOURCES} ${AUDIO_SOURCES})
+ add_executable(spectool tools/spectool.cc ${PLATFORM_SOURCES} ${AUDIO_SOURCES} ${GENERATED_MUSIC_DATA_CC})
target_compile_definitions(spectool PRIVATE DEMO_BUILD_TOOLS)
target_link_libraries(spectool PRIVATE ${DEMO_LIBS})
+ add_dependencies(spectool generate_tracker_music)
add_executable(specview tools/specview.cc)
endif()
diff --git a/src/audio/tracker.cc b/src/audio/tracker.cc
index 0b57ce3..9f9263d 100644
--- a/src/audio/tracker.cc
+++ b/src/audio/tracker.cc
@@ -3,6 +3,7 @@
#include "tracker.h"
#include "audio/synth.h"
+#include <cstring>
#include <vector>
static uint32_t g_last_trigger_idx = 0;
diff --git a/src/generated/music_data.cc b/src/generated/music_data.cc
new file mode 100644
index 0000000..87c1f65
--- /dev/null
+++ b/src/generated/music_data.cc
@@ -0,0 +1,47 @@
+// Generated by tracker_compiler. Do not edit.
+
+#include "audio/tracker.h"
+
+const NoteParams g_tracker_samples[] = {
+ { 50.0f, 0.20f, 1.0f, 0.01f, 0.0f, 0.0f, 0.0f, 1, 0.5f, 0.0f, 0.0f }, // kick
+ { 200.0f, 0.20f, 0.8f, 0.01f, 0.0f, 0.0f, 0.0f, 5, 0.7f, 0.0f, 0.0f }, // snare
+ { 1000.0f, 0.05f, 0.3f, 0.00f, 0.0f, 0.0f, 0.0f, 10, 0.4f, 0.0f, 0.0f }, // hihat
+};
+const uint32_t g_tracker_samples_count = 3;
+
+static const TrackerEvent PATTERN_EVENTS_drum_loop[] = {
+ { 0.0f, 0, 1.0f, 0.0f },
+ { 1.0f, 1, 0.7f, 0.0f },
+ { 2.0f, 0, 1.0f, 0.0f },
+ { 2.5f, 0, 0.6f, 0.2f },
+ { 3.0f, 1, 0.7f, 0.0f },
+};
+static const TrackerEvent PATTERN_EVENTS_hihat_roll[] = {
+ { 0.0f, 2, 0.5f, -0.5f },
+ { 0.5f, 2, 0.4f, 0.5f },
+ { 1.0f, 2, 0.5f, -0.5f },
+ { 1.5f, 2, 0.4f, 0.5f },
+ { 2.0f, 2, 0.5f, -0.5f },
+ { 2.5f, 2, 0.4f, 0.5f },
+ { 3.0f, 2, 0.5f, -0.5f },
+ { 3.5f, 2, 0.4f, 0.5f },
+};
+
+const TrackerPattern g_tracker_patterns[] = {
+ { PATTERN_EVENTS_drum_loop, 5, 4.0f }, // drum_loop
+ { PATTERN_EVENTS_hihat_roll, 8, 4.0f }, // hihat_roll
+};
+const uint32_t g_tracker_patterns_count = 2;
+
+static const TrackerPatternTrigger SCORE_TRIGGERS[] = {
+ { 0.0f, 0 },
+ { 0.0f, 1 },
+ { 4.0f, 0 },
+ { 4.0f, 1 },
+ { 8.0f, 0 },
+ { 12.0f, 0 },
+};
+
+const TrackerScore g_tracker_score = {
+ SCORE_TRIGGERS, 6, 120.0f
+};
diff --git a/src/gpu/effects/shader_composer.cc b/src/gpu/effects/shader_composer.cc
index 61da6e6..3e08df9 100644
--- a/src/gpu/effects/shader_composer.cc
+++ b/src/gpu/effects/shader_composer.cc
@@ -5,29 +5,31 @@
#include <sstream>
ShaderComposer& ShaderComposer::Get() {
- static ShaderComposer instance;
- return instance;
+ static ShaderComposer instance;
+ return instance;
}
-void ShaderComposer::RegisterSnippet(const std::string& name, const std::string& code) {
- snippets_[name] = code;
+void ShaderComposer::RegisterSnippet(const std::string& name,
+ const std::string& code) {
+ snippets_[name] = code;
}
-std::string ShaderComposer::Compose(const std::vector<std::string>& dependencies, const std::string& main_code) {
- std::stringstream ss;
- ss << "// Generated by ShaderComposer\n\n";
-
- for (const auto& dep : dependencies) {
- auto it = snippets_.find(dep);
- if (it != snippets_.end()) {
- ss << "// --- Snippet: " << dep << " ---\n";
- ss << it->second << "\n";
- }
+std::string
+ShaderComposer::Compose(const std::vector<std::string>& dependencies,
+ const std::string& main_code) {
+ std::stringstream ss;
+ ss << "// Generated by ShaderComposer\n\n";
+
+ for (const auto& dep : dependencies) {
+ auto it = snippets_.find(dep);
+ if (it != snippets_.end()) {
+ ss << "// --- Snippet: " << dep << " ---\n";
+ ss << it->second << "\n";
}
-
- ss << "// --- Main Code ---\n";
- ss << main_code;
-
- return ss.str();
-}
+ }
+ ss << "// --- Main Code ---\n";
+ ss << main_code;
+
+ return ss.str();
+}
diff --git a/src/gpu/effects/shader_composer.h b/src/gpu/effects/shader_composer.h
index 7d918a9..49bf00c 100644
--- a/src/gpu/effects/shader_composer.h
+++ b/src/gpu/effects/shader_composer.h
@@ -3,21 +3,22 @@
#pragma once
-#include <string>
#include <map>
+#include <string>
#include <vector>
class ShaderComposer {
-public:
- static ShaderComposer& Get();
+ public:
+ static ShaderComposer& Get();
- // Register a snippet (e.g. "common_math", "sdf_primitives")
- void RegisterSnippet(const std::string& name, const std::string& code);
+ // Register a snippet (e.g. "common_math", "sdf_primitives")
+ void RegisterSnippet(const std::string& name, const std::string& code);
- // Assemble a final shader string by prepending required snippets
- std::string Compose(const std::vector<std::string>& dependencies, const std::string& main_code);
+ // Assemble a final shader string by prepending required snippets
+ std::string Compose(const std::vector<std::string>& dependencies,
+ const std::string& main_code);
-private:
- ShaderComposer() = default;
- std::map<std::string, std::string> snippets_;
+ private:
+ ShaderComposer() = default;
+ std::map<std::string, std::string> snippets_;
};
diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc
index 746392a..579160c 100644
--- a/src/gpu/effects/shaders.cc
+++ b/src/gpu/effects/shaders.cc
@@ -6,9 +6,9 @@
#include "gpu/effects/shader_composer.h"
void InitShaderComposer() {
- auto& sc = ShaderComposer::Get();
+ auto& sc = ShaderComposer::Get();
- sc.RegisterSnippet("common_uniforms", R"(
+ sc.RegisterSnippet("common_uniforms", R"(
struct GlobalUniforms {
view_proj: mat4x4<f32>,
camera_pos_time: vec4<f32>,
@@ -25,7 +25,7 @@ struct ObjectsBuffer {
};
)");
- sc.RegisterSnippet("sdf_primitives", R"(
+ sc.RegisterSnippet("sdf_primitives", R"(
fn sdSphere(p: vec3<f32>, r: f32) -> f32 {
return length(p) - r;
}
@@ -42,7 +42,7 @@ fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32 {
}
)");
- sc.RegisterSnippet("lighting", R"(
+ sc.RegisterSnippet("lighting", R"(
fn get_normal_basic(p: vec3<f32>, obj_type: f32) -> vec3<f32> {
if (obj_type == 1.0) { return normalize(p); }
let e = vec2<f32>(0.001, 0.0);
@@ -68,7 +68,7 @@ fn calc_shadow(ro: vec3<f32>, rd: vec3<f32>, tmin: f32, tmax: f32, skip_idx: u32
}
)");
- sc.RegisterSnippet("ray_box", R"(
+ sc.RegisterSnippet("ray_box", R"(
struct RayBounds {
t_entry: f32,
t_exit: f32,
diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h
index f25d242..45c6413 100644
--- a/src/gpu/gpu.h
+++ b/src/gpu/gpu.h
@@ -107,8 +107,8 @@ void gpu_simulate_until(float time);
void gpu_shutdown();
// Placeholder for GPU performance capture.
-// This define can be controlled via CMake to conditionally enable profiling code.
-// #define ENABLE_GPU_PERF_CAPTURE
+// This define can be controlled via CMake to conditionally enable profiling
+// code. #define ENABLE_GPU_PERF_CAPTURE
// Helper functions (exposed for internal/future use)
struct ResourceBinding {
diff --git a/src/main.cc b/src/main.cc
index 842c174..55bb4a0 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -6,6 +6,7 @@
#include "audio/audio.h"
#include "audio/gen.h"
#include "audio/synth.h"
+#include "audio/tracker.h"
#include "generated/assets.h" // Include generated asset header
#include "gpu/gpu.h"
#include "platform.h"
@@ -16,8 +17,6 @@
#include <string.h>
#include <vector>
-#define DEMO_BPM 128.0f
-#define SECONDS_PER_BEAT (60.0f / DEMO_BPM)
#define SPEC_FRAMES 16
struct SpecHeader {
@@ -48,68 +47,10 @@ static float* g_spec_buffer_a[SPEC_FRAMES * DCT_SIZE] = {0};
static float* g_spec_buffer_b[SPEC_FRAMES * DCT_SIZE] = {0};
// Global storage for the melody to ensure it persists
-std::vector<float> g_melody_data;
-
-int generate_melody() {
- g_melody_data.clear();
- int melody_frames = 0;
-
- // Simple C Minor pentatonic-ish sequence
- float notes[] = {261.63f, 311.13f, 349.23f, 392.00f, 466.16f, 523.25f};
- int num_notes = 6;
-
- // 128 beats at 128 BPM = 60 seconds
- // Generate a random sequence
- srand(12345); // Fixed seed for reproducibility
-
- for (int i = 0; i < 128; ++i) {
- if (i % 4 == 0)
- continue; // Rest on beat 1 of every bar
-
- NoteParams params = {};
- params.base_freq = notes[rand() % num_notes];
- if (rand() % 4 == 0)
- params.base_freq *= 2.0f; // Occasional octave up
-
- params.duration_sec = (rand() % 2 == 0) ? 0.2f : 0.4f;
- params.amplitude = 0.4f;
- params.attack_sec = 0.05f;
- params.decay_sec = 0.1f;
- params.vibrato_rate = 6.0f;
- params.vibrato_depth = 1.5f;
- params.num_harmonics = 4;
- params.harmonic_decay = 0.6f;
- params.pitch_randomness = 0.5f;
- params.amp_randomness = 0.05f;
-
- int note_frames = 0;
- std::vector<float> note_data =
- generate_note_spectrogram(params, &note_frames);
-
- // Apply some post-processing for texture
- apply_spectral_noise(note_data, note_frames, 0.2f); // Add grit
- if (i % 2 == 0) {
- apply_spectral_comb(note_data, note_frames, 10.0f,
- 0.8f); // Phaser-like effect
- }
-
- // Calculate offset in frames
- // i is the beat index (quarter notes)
- // 1 beat = 60 / 128 seconds = 0.46875 sec
- float beat_time = i * SECONDS_PER_BEAT;
- int frame_offset = (int)(beat_time * 32000.0f / DCT_SIZE);
-
- paste_spectrogram(g_melody_data, &melody_frames, note_data, note_frames,
- frame_offset);
- }
-
- Spectrogram spec;
- spec.spectral_data_a = g_melody_data.data();
- spec.spectral_data_b = g_melody_data.data();
- spec.num_frames = melody_frames;
+// Global storage for the melody to ensure it persists
+// std::vector<float> g_melody_data; // Tracker now handles melody generation
- return synth_register_spectrogram(&spec);
-}
+// int generate_melody() { ... } // Replaced by tracker
float* generate_tone(float* buffer, float freq) {
if (buffer == nullptr) {
@@ -163,59 +104,62 @@ int main(int argc, char** argv) {
platform_init(&platform_state, fullscreen_enabled, width_ptr, height_ptr);
gpu_init(&platform_state);
audio_init();
+ synth_init();
+ tracker_init();
- // Register drum assets
- int kick_id = register_spec_asset(AssetId::ASSET_KICK_1);
- int snare_id = register_spec_asset(AssetId::ASSET_SNARE_1);
- int hihat_id = register_spec_asset(AssetId::ASSET_HIHAT_1);
+ // Register drum assets (if still needed, can be moved to tracker samples)
+ // int kick_id = register_spec_asset(AssetId::ASSET_KICK_1);
+ // int snare_id = register_spec_asset(AssetId::ASSET_SNARE_1);
+ // int hihat_id = register_spec_asset(AssetId::ASSET_HIHAT_1);
- // Still keep the dynamic tone for bass
+ // Still keep the dynamic tone for bass (can be integrated into tracker too)
const float* g_spec_buffer_a = generate_tone(nullptr, 110.0f); // A2
const float* g_spec_buffer_b = generate_tone(nullptr, 110.0f);
const Spectrogram bass_spec = {g_spec_buffer_a, g_spec_buffer_b, SPEC_FRAMES};
int bass_id = synth_register_spectrogram(&bass_spec);
- // Generate and play melody
- int melody_id = generate_melody();
- synth_trigger_voice(melody_id, 0.6f, 0.0f);
+ // Generate and play melody (replaced by tracker)
+ // int melody_id = generate_melody();
+ // synth_trigger_voice(melody_id, 0.6f, 0.0f);
- double last_beat_time = 0.0;
- int beat_count = 0;
+ // double last_beat_time = 0.0;
+ // int beat_count = 0;
auto update_game_logic = [&](double t) {
- if (t - last_beat_time > SECONDS_PER_BEAT / 2.0) { // 8th notes
- last_beat_time = t; // Sync to t
+ // if (t - last_beat_time > SECONDS_PER_BEAT / 2.0) { // 8th notes
+ // last_beat_time = t; // Sync to t
- const int step = beat_count % 16;
+ // const int step = beat_count % 16;
- // Kick on 1, 9, 11, 14...
- if (step == 0 || step == 8 || step == 10 || step == 13) {
- synth_trigger_voice(kick_id, 1.0f, 0.0f);
- }
+ // // Kick on 1, 9, 11, 14...
+ // if (step == 0 || step == 8 || step == 10 || step == 13) {
+ // synth_trigger_voice(kick_id, 1.0f, 0.0f);
+ // }
- // Snare on 4, 12
- if (step == 4 || step == 12) {
- synth_trigger_voice(snare_id, 0.8f, step & 8 ? -1.0f : 1.0f);
- }
+ // // Snare on 4, 12
+ // if (step == 4 || step == 12) {
+ // synth_trigger_voice(snare_id, 0.8f, step & 8 ? -1.0f : 1.0f);
+ // }
- // Hihat on every offbeat
- if (step % 2 == 1) {
- synth_trigger_voice(hihat_id, 0.5f, 0.3f);
- }
+ // // Hihat on every offbeat
+ // if (step % 2 == 1) {
+ // synth_trigger_voice(hihat_id, 0.5f, 0.3f);
+ // }
- // Bass pattern
- if (step % 4 == 0) {
- float* back_buffer = synth_begin_update(bass_id);
- if (back_buffer) {
- float bass_freq = (step < 8) ? 110.0f : 164.82f; // A3 then E3
- generate_tone(back_buffer, bass_freq);
- synth_commit_update(bass_id);
- }
- synth_trigger_voice(bass_id, 0.9f, 1.2f);
- }
+ // // Bass pattern
+ // if (step % 4 == 0) {
+ // float* back_buffer = synth_begin_update(bass_id);
+ // if (back_buffer) {
+ // float bass_freq = (step < 8) ? 110.0f : 164.82f; // A3 then E3
+ // generate_tone(back_buffer, bass_freq);
+ // synth_commit_update(bass_id);
+ // }
+ // synth_trigger_voice(bass_id, 0.9f, 1.2f);
+ // }
- ++beat_count;
- }
+ // ++beat_count;
+ // }
+ tracker_update((float)t);
};
#if !defined(STRIP_ALL)
@@ -261,7 +205,9 @@ int main(int argc, char** argv) {
float raw_peak = synth_get_output_peak();
float visual_peak = fminf(raw_peak * 8.0f, 1.0f);
- float beat = fmodf((float)current_time * DEMO_BPM / 60.0f, 1.0f);
+ // float beat = fmodf((float)current_time * DEMO_BPM / 60.0f, 1.0f); // Use
+ // tracker BPM
+ float beat = fmodf((float)current_time * g_tracker_score.bpm / 60.0f, 1.0f);
gpu_draw(visual_peak, aspect_ratio, (float)current_time, beat);
audio_update();
}
diff --git a/src/tests/test_maths.cc b/src/tests/test_maths.cc
index 8eb7ec6..0a3b9e6 100644
--- a/src/tests/test_maths.cc
+++ b/src/tests/test_maths.cc
@@ -194,7 +194,8 @@ void test_matrix_inversion() {
check_identity(s * s_inv);
// 4. Rotation
- mat4 r = mat4::rotate({1.0f, 2.0f, 3.0f}, 0.785f); // 45 deg around complex axis
+ mat4 r =
+ mat4::rotate({1.0f, 2.0f, 3.0f}, 0.785f); // 45 deg around complex axis
mat4 r_inv = r.inverse();
check_identity(r * r_inv);
diff --git a/src/tests/test_shader_composer.cc b/src/tests/test_shader_composer.cc
index 4a5cb8b..cdb5c88 100644
--- a/src/tests/test_shader_composer.cc
+++ b/src/tests/test_shader_composer.cc
@@ -7,32 +7,32 @@
#include <string>
void test_composition() {
- std::cout << "Testing Shader Composition..." << std::endl;
- auto& sc = ShaderComposer::Get();
-
- sc.RegisterSnippet("math", "fn add(a: f32, b: f32) -> f32 { return a + b; }");
- sc.RegisterSnippet("util", "fn square(a: f32) -> f32 { return a * a; }");
-
- std::string main_code = "fn main() { let x = add(1.0, square(2.0)); }";
- std::string result = sc.Compose({"math", "util"}, main_code);
-
- // Verify order and presence
- assert(result.find("Snippet: math") != std::string::npos);
- assert(result.find("Snippet: util") != std::string::npos);
- assert(result.find("Main Code") != std::string::npos);
-
- size_t pos_math = result.find("Snippet: math");
- size_t pos_util = result.find("Snippet: util");
- size_t pos_main = result.find("Main Code");
-
- assert(pos_math < pos_util);
- assert(pos_util < pos_main);
-
- std::cout << "Composition logic verified." << std::endl;
+ std::cout << "Testing Shader Composition..." << std::endl;
+ auto& sc = ShaderComposer::Get();
+
+ sc.RegisterSnippet("math", "fn add(a: f32, b: f32) -> f32 { return a + b; }");
+ sc.RegisterSnippet("util", "fn square(a: f32) -> f32 { return a * a; }");
+
+ std::string main_code = "fn main() { let x = add(1.0, square(2.0)); }";
+ std::string result = sc.Compose({"math", "util"}, main_code);
+
+ // Verify order and presence
+ assert(result.find("Snippet: math") != std::string::npos);
+ assert(result.find("Snippet: util") != std::string::npos);
+ assert(result.find("Main Code") != std::string::npos);
+
+ size_t pos_math = result.find("Snippet: math");
+ size_t pos_util = result.find("Snippet: util");
+ size_t pos_main = result.find("Main Code");
+
+ assert(pos_math < pos_util);
+ assert(pos_util < pos_main);
+
+ std::cout << "Composition logic verified." << std::endl;
}
int main() {
- test_composition();
- std::cout << "--- ALL SHADER COMPOSER TESTS PASSED ---" << std::endl;
- return 0;
+ test_composition();
+ std::cout << "--- ALL SHADER COMPOSER TESTS PASSED ---" << std::endl;
+ return 0;
}
diff --git a/src/tests/test_tracker.cc b/src/tests/test_tracker.cc
new file mode 100644
index 0000000..95e746b
--- /dev/null
+++ b/src/tests/test_tracker.cc
@@ -0,0 +1,69 @@
+// This file is part of the 64k demo project.
+// It tests the core functionality of the audio tracker engine.
+
+#include "audio/tracker.h"
+#include "audio/synth.h"
+#include "audio/gen.h"
+// #include "generated/music_data.h" // Will be generated by tracker_compiler
+#include <assert.h>
+#include <stdio.h>
+
+// Forward declaration for generated data to avoid compilation issues before generation
+// 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 test_tracker_init() {
+ synth_init();
+ tracker_init();
+ printf("Tracker init test PASSED\n");
+}
+
+void test_tracker_pattern_triggering() {
+ synth_init();
+ tracker_init();
+
+ // Need a minimal set of samples for generation
+ // These values should match what's expected by the music.track file
+ // For testing purposes, we define dummy data here. In a real scenario,
+ // we'd rely on the generated g_tracker_samples, g_tracker_patterns, etc.
+ // This test focuses on the logic of tracker_update, not the full audio generation pipeline.
+
+ // Assuming g_tracker_score, g_tracker_patterns, and g_tracker_samples are available globally
+ // after tracker_compiler has run.
+
+ // Test 1: No triggers initially, active voices should be 0
+ tracker_update(0.0f);
+ assert(synth_get_active_voice_count() == 2); // Expect 2 voices (one for each pattern triggered at 0.0f)
+
+ // Test 2: Advance time to first trigger (0.0f in music.track for drum_loop and hihat_roll)
+ // In our dummy music.track, there are two patterns triggered at 0.0f
+ // Each pattern has multiple events which trigger voices.
+ tracker_update(0.1f); // Advance just past the 0.0f trigger point
+
+ // The exact number of voices depends on the music.track content.
+ // For the given music.track, two patterns are triggered at 0.0f.
+ // Each pattern registers one spectrogram and triggers one voice.
+ assert(synth_get_active_voice_count() == 2);
+
+ // 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
+ assert(synth_get_active_voice_count() > 0);
+
+ printf("Tracker pattern triggering test PASSED\n");
+}
+
+int main() {
+ printf("Running Tracker tests...\n");
+ test_tracker_init();
+ test_tracker_pattern_triggering();
+ printf("Tracker tests PASSED\n");
+ return 0;
+}
diff --git a/tools/tracker_compiler.cc b/tools/tracker_compiler.cc
index 491aebb..9f0d6b4 100644
--- a/tools/tracker_compiler.cc
+++ b/tools/tracker_compiler.cc
@@ -1,159 +1,174 @@
#include <cstdio>
#include <fstream>
#include <iostream>
+#include <map>
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
-#include <map>
struct Sample {
- std::string name;
- float freq, dur, amp, attack, harmonic_decay;
- int harmonics;
+ std::string name;
+ float freq, dur, amp, attack, harmonic_decay;
+ int harmonics;
};
struct Event {
- float beat;
- std::string sample_name;
- float volume, pan;
+ float beat;
+ std::string sample_name;
+ float volume, pan;
};
struct Pattern {
- std::string name;
- std::vector<Event> events;
+ std::string name;
+ std::vector<Event> events;
};
struct Trigger {
- float time;
- std::string pattern_name;
+ float time;
+ std::string pattern_name;
};
int main(int argc, char** argv) {
- if (argc < 3) {
- std::cerr << "Usage: " << argv[0] << " <input.track> <output.cc>" << std::endl;
- return 1;
- }
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s <input.track> <output.cc>\n", argv[0]);
+ return 1;
+ }
- std::ifstream in(argv[1]);
- if (!in.is_open()) {
- std::cerr << "Could not open input file: " << argv[1] << std::endl;
- return 1;
- }
+ std::ifstream in(argv[1]);
+ if (!in.is_open()) {
+ fprintf(stderr, "Could not open input file: %s\n", argv[1]);
+ return 1;
+ }
- float bpm = 120.0f;
- std::vector<Sample> samples;
- std::map<std::string, int> sample_map;
- std::vector<Pattern> patterns;
- std::map<std::string, int> pattern_map;
- std::vector<Trigger> score;
+ float bpm = 120.0f;
+ std::vector<Sample> samples;
+ std::map<std::string, int> sample_map;
+ std::vector<Pattern> patterns;
+ std::map<std::string, int> pattern_map;
+ std::vector<Trigger> score;
- std::string line;
- std::string current_section = "";
+ std::string line;
+ std::string current_section = "";
- while (std::getline(in, line)) {
- if (line.empty() || line[0] == '#') continue;
+ while (std::getline(in, line)) {
+ if (line.empty() || line[0] == '#')
+ continue;
- std::stringstream ss(line);
- std::string cmd;
- ss >> cmd;
+ std::stringstream ss(line);
+ std::string cmd;
+ ss >> cmd;
- if (cmd == "BPM") {
- ss >> bpm;
- } else if (cmd == "SAMPLE") {
- Sample s;
- std::string name;
- ss >> name;
- if (name.back() == ',') name.pop_back();
- s.name = name;
-
- // Very simple parsing: freq, dur, amp, attack, harmonics, harmonic_decay
- char comma;
- ss >> s.freq >> comma >> s.dur >> comma >> s.amp >> comma >> s.attack >> comma >> s.harmonics >> comma >> s.harmonic_decay;
-
- sample_map[s.name] = samples.size();
- samples.push_back(s);
- } else if (cmd == "PATTERN") {
- Pattern p;
- ss >> p.name;
- current_section = "PATTERN:" + p.name;
- patterns.push_back(p);
- pattern_map[p.name] = patterns.size() - 1;
- } else if (cmd == "SCORE") {
- current_section = "SCORE";
- } else {
- if (current_section.rfind("PATTERN:", 0) == 0) {
- // Parse event: beat, sample, vol, pan
- Event e;
- float beat;
- std::stringstream ss2(line);
- ss2 >> beat;
- char comma;
- ss2 >> comma;
- std::string sname;
- ss2 >> sname;
- if (sname.back() == ',') sname.pop_back();
- e.beat = beat;
- e.sample_name = sname;
- ss2 >> e.volume >> comma >> e.pan;
- patterns.back().events.push_back(e);
- } else if (current_section == "SCORE") {
- Trigger t;
- float time;
- std::stringstream ss2(line);
- ss2 >> time;
- char comma;
- ss2 >> comma;
- std::string pname;
- ss2 >> pname;
- t.time = time;
- t.pattern_name = pname;
- score.push_back(t);
- }
- }
- }
+ if (cmd == "BPM") {
+ ss >> bpm;
+ } else if (cmd == "SAMPLE") {
+ Sample s;
+ std::string name;
+ ss >> name;
+ if (name.back() == ',')
+ name.pop_back();
+ s.name = name;
- std::ofstream out(argv[2]);
- out << "// Generated by tracker_compiler. Do not edit.\n\n";
- out << "#include \"audio/tracker.h\"\n\n";
+ // Very simple parsing: freq, dur, amp, attack, harmonics, harmonic_decay
+ char comma;
+ ss >> s.freq >> comma >> s.dur >> comma >> s.amp >> comma >> s.attack >>
+ comma >> s.harmonics >> comma >> s.harmonic_decay;
- out << "const NoteParams g_tracker_samples[] = {\n";
- for (const auto& s : samples) {
- out << " { " << s.freq << "f, " << s.dur << "f, " << s.amp << "f, " << s.attack << "f, 0.0f, 0.0f, 0.0f, "
- << s.harmonics << ", " << s.harmonic_decay << "f, 0.0f, 0.0f }, // " << s.name << "\n";
+ sample_map[s.name] = samples.size();
+ samples.push_back(s);
+ } else if (cmd == "PATTERN") {
+ Pattern p;
+ ss >> p.name;
+ current_section = "PATTERN:" + p.name;
+ patterns.push_back(p);
+ pattern_map[p.name] = patterns.size() - 1;
+ } else if (cmd == "SCORE") {
+ current_section = "SCORE";
+ } else {
+ if (current_section.rfind("PATTERN:", 0) == 0) {
+ // Parse event: beat, sample, vol, pan
+ Event e;
+ float beat;
+ std::stringstream ss2(line);
+ ss2 >> beat;
+ char comma;
+ ss2 >> comma;
+ std::string sname;
+ ss2 >> sname;
+ if (sname.back() == ',')
+ sname.pop_back();
+ e.beat = beat;
+ e.sample_name = sname;
+ ss2 >> e.volume >> comma >> e.pan;
+ patterns.back().events.push_back(e);
+ } else if (current_section == "SCORE") {
+ Trigger t;
+ float time;
+ std::stringstream ss2(line);
+ ss2 >> time;
+ char comma;
+ ss2 >> comma;
+ std::string pname;
+ ss2 >> pname;
+ t.time = time;
+ t.pattern_name = pname;
+ score.push_back(t);
+ }
}
- out << "};
-";
- out << "const uint32_t g_tracker_samples_count = " << samples.size() << ";\n\n";
+ }
- for (const auto& p : patterns) {
- out << "static const TrackerEvent PATTERN_EVENTS_" << p.name << "[] = {\n";
- for (const auto& e : p.events) {
- out << " { " << e.beat << "f, " << sample_map[e.sample_name] << ", " << e.volume << "f, " << e.pan << "f },\n";
- }
- out << "};
-";
- }
- out << "\n";
+ FILE* out_file = fopen(argv[2], "w");
+ if (!out_file) {
+ fprintf(stderr, "Could not open output file: %s\n", argv[2]);
+ return 1;
+ }
+ fprintf(out_file, "// Generated by tracker_compiler. Do not edit.\n\n");
+ fprintf(out_file, "#include \"audio/tracker.h\"\n\n");
- out << "const TrackerPattern g_tracker_patterns[] = {\n";
- for (const auto& p : patterns) {
- out << " { PATTERN_EVENTS_" << p.name << ", " << p.events.size() << ", 4.0f }, // " << p.name << "\n";
- }
- out << "};
-";
- out << "const uint32_t g_tracker_patterns_count = " << patterns.size() << ";\n\n";
+ fprintf(out_file, "const NoteParams g_tracker_samples[] = {\n");
+ for (const auto& s : samples) {
+ fprintf(out_file,
+ " { %.1ff, %.2ff, %.1ff, %.2ff, 0.0f, 0.0f, 0.0f, %d, %.1ff, "
+ "0.0f, 0.0f }, // %s\n",
+ s.freq, s.dur, s.amp, s.attack, s.harmonics, s.harmonic_decay,
+ s.name.c_str());
+ }
+ fprintf(out_file, "};\n");
+ fprintf(out_file, "const uint32_t g_tracker_samples_count = %zu;\n\n",
+ samples.size());
- out << "static const TrackerPatternTrigger SCORE_TRIGGERS[] = {\n";
- for (const auto& t : score) {
- out << " { " << t.time << "f, " << pattern_map[t.pattern_name] << " },\n";
+ for (const auto& p : patterns) {
+ fprintf(out_file, "static const TrackerEvent PATTERN_EVENTS_%s[] = {\n",
+ p.name.c_str());
+ for (const auto& e : p.events) {
+ fprintf(out_file, " { %.1ff, %d, %.1ff, %.1ff },\n", e.beat,
+ sample_map[e.sample_name], e.volume, e.pan);
}
- out << "};
-\n";
+ fprintf(out_file, "};\n");
+ }
+ fprintf(out_file, "\n");
+
+ fprintf(out_file, "const TrackerPattern g_tracker_patterns[] = {\n");
+ for (const auto& p : patterns) {
+ fprintf(out_file, " { PATTERN_EVENTS_%s, %zu, 4.0f }, // %s\n",
+ p.name.c_str(), p.events.size(), p.name.c_str());
+ }
+ fprintf(out_file, "};\n");
+ fprintf(out_file, "const uint32_t g_tracker_patterns_count = %zu;\n\n",
+ patterns.size());
+
+ fprintf(out_file,
+ "static const TrackerPatternTrigger SCORE_TRIGGERS[] = {\n");
+ for (const auto& t : score) {
+ fprintf(out_file, " { %.1ff, %d },\n", t.time,
+ pattern_map[t.pattern_name]);
+ }
+ fprintf(out_file, "};\n\n");
+
+ fprintf(out_file, "const TrackerScore g_tracker_score = {\n");
+ fprintf(out_file, " SCORE_TRIGGERS, %zu, %.1ff\n", score.size(), bpm);
+ fprintf(out_file, "};\n");
- out << "const TrackerScore g_tracker_score = {\n";
- out << " SCORE_TRIGGERS, " << score.size() << ", " << bpm << "f\n";
- out << "};
-";
+ fclose(out_file);
- return 0;
+ return 0;
}