#include #include #include #include #include #include #include struct Sample { std::string name; float freq, dur, amp, attack, harmonic_decay; int harmonics; }; struct Event { float beat; std::string sample_name; float volume, pan; }; struct Pattern { std::string name; std::vector events; }; struct Trigger { float time; std::string pattern_name; }; int main(int argc, char** argv) { if (argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); 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 samples; std::map sample_map; std::vector patterns; std::map pattern_map; std::vector score; std::string line; std::string current_section = ""; while (std::getline(in, line)) { if (line.empty() || line[0] == '#') continue; 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); } } } 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"); 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()); 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); } 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"); fclose(out_file); return 0; }