int main(int argc, char* argv[]) { RunTests(); // Our tracks Tracks tracks; vector<string> names; //GetTracks("tracks.txt", tracks); GetTracksTabSeparated("tracks_tsv.txt", tracks, names); //// Write separate values out //ofstream names("names.txt"); //ofstream bpms("bpms.txt"); //ofstream keys("keys.txt"); //for (auto t : tracks) { // names << t.name << endl; // bpms << t.bpm << endl; // keys << t.key.short_name << endl; //} //names.close(); //bpms.close(); //keys.close(); // Collect stats on how many tracks are in which keys KeyCount key_counts; for (auto t : tracks) { //key_counts[t.key].push_back(t); key_counts[t.key]++; } // Write the key counts for (auto k : key_counts) { cout << Key::GetShortName(k.first.num, k.first.type) << ": " << k.second << endl; } // Exhaustive solution // Start at each track and try to get as many tracks into a mix as possible // We will exhaustively try to join into each possible next track that is compatible Tracks available = tracks; Tracks best; double best_cost = DBL_MAX; for (size_t i = 0; i < available.size(); ++i) { Tracks chosen; Tracks this_best; double cost = 0; double this_best_cost = DBL_MAX; int its = 0; Tracks now_available = available; Track t = now_available[i]; now_available.erase(now_available.begin() + i); Tracks now_chosen = chosen; now_chosen.push_back(t); cout << "Starting with " << names[t.idx] << endl; ChooseTrack(names, now_available, now_chosen, t.idx, t.key, t.bpm, cost, this_best_cost, kMixSongLen, this_best, its); if (this_best.size() > best.size()) { best = this_best; cout << "New OVERALL best of " << best.size() << " found!" << endl; } else if (this_best.size() == best.size() && this_best_cost < best_cost) { best_cost = this_best_cost; best = this_best; cout << "New OVERALL best cost of " << best_cost << " found!" << endl; } } // Show our results for (auto t : best) { cout << setw(3) << Key::GetShortName(t.key.num, t.key.type) << " @ " << setw(3) << static_cast<int>(t.bpm) << "bpm" << ", " << names[t.idx] << endl; } cout << "Cost is " << best_cost << endl; return EXIT_SUCCESS; //// Sort by BPM //sort(tracks.begin(), tracks.end(), [](Track const& a, Track const& b) //{ // return a.bpm < b.bpm; //}); //// Interactive selection //vector<Track> order; //int idx = 0; //for (;;) { // // Mark our work // Track selected = tracks[idx]; // tracks.erase(tracks.begin() + idx); // order.push_back(selected); // cout << "Your current track: " << selected.name << endl; // // Get options... // vector<TrackOption> options; // for (size_t i = 0; i < tracks.size(); ++i) { // // Must be compatible // if (!AreCompatibleKeys(selected.key, tracks[i].key)) { // continue; // } // // Get the BPM ratio // double bpm_ratio = max(selected.bpm, tracks[i].bpm) / min(selected.bpm, tracks[i].bpm); // //double bpm_ratio_st = log(bpm_ratio) / log(Utils::GetSemitoneRatio()); // // We have an option // TrackOption option(tracks[i], bpm_ratio, i); // options.push_back(option); // } // // Sort by BPM ratio // sort(options.begin(), options.end(), [](TrackOption const& a, TrackOption const& b) // { // return a.bpm_ratio < b.bpm_ratio; // } // ); // // Print options // cout << "Options: " << endl; // for (size_t i = 0; i < options.size(); ++i) { // cout << i << "\t" << fixed << setw(4) << options[i].bpm_ratio << "\t" << options[i].track.name << " (" << options[i].track.key.short_name << ")" << endl; // } // cout << "You have selected " << order.size() << " tracks" << endl; // string choice; // getline(cin, choice); // if (!choice.compare("q")) { // break; // } // stringstream ss(choice); // ss >> idx; // idx = options[idx].idx; //} //// Write our order //ofstream order_f("order.txt"); //for (auto t : order) { // order_f << t.bpm << "\t" << t.name << " (" << t.key.short_name << ")" << endl; //} //order_f.close(); //// Start by just choosing a base track (try each one) //vector<Tracks> options; //// Our starting track //for (auto t : tracks) { // cout << "Starting with " << t.name << endl; // Tracks order; // if (ChooseTrack(t, tracks, order)) { // options.push_back(order); // cout << "Found " << options.size() << " solutions!" << endl; // } //} //// Select a key ordering //vector<Keys> options; //int max_depth = 0; //for (auto k : key_counts) { // cout << "Starting with " << k.first.short_name << endl; // Keys order; // if (ChooseKey(KeyFromString("Dbm") /*k.first*/, key_counts, order, 1, max_depth)) { // options.push_back(order); // cout << "Found " << options.size() << " solutions!" << endl; // } //} // Find a mix that works! MixAnt ma; Mix m = ma.FindMix(tracks); // Print the mix //cout << m << endl; cout << "Mix uses " << m.steps.size() << " of " << tracks.size() << " input tracks" << endl << endl; // Save the mix to a file ofstream ofs("mix.txt"); ofs << m; ofs.close(); // write our unused tracks Tracks unused(tracks); for (auto s : m.steps) { for (auto it = unused.begin(); it != unused.end(); ++it) { if (*it == s.track) { unused.erase(it); break; } } } ofstream unused_f("unused.txt"); for (auto u : unused) { //unused_f << u.name << endl; //unused_f << u.key.short_name << endl; unused_f << u.bpm << endl; } unused_f.close(); //cin.get(); }
/** Returns the number of tracks. */ size_t getNumberOfTracks() const { return m_tracks.size(); }
void ChooseTrack( vector<string> names, Tracks& available, Tracks& chosen, int prev_idx, Key prev_key, double prev_bpm, double cost, double& best_cost, int max_len, Tracks& best, int& its ) { // We're too long! if (chosen.size() >= static_cast<size_t>(max_len)) { return; } // We have a new best length (more important than cost) if (chosen.size() > best.size()) { best_cost = cost; best = chosen; cout << "New best of length " << best.size() << " found." << endl; its = 0; } // Mix length is the same, but less cost else if (chosen.size() == best.size() && cost < best_cost && !chosen.empty()) { best_cost = cost; cout << "New best cost of " << best_cost << " found." << endl; best = chosen; its = 0; } // No improvement! else { ++its; } // We've spent too long, so bail! if (its >= 1000000) { cout << "Too many iterations without improvement!" << endl; return; } // Nothing to look at if (available.empty()) { cout << "No more tracks to choose." << endl; return; } Track prev(prev_idx, prev_bpm, prev_key); // Go through all available and find those compatible Tracks candidates; Keys candidates_adj; vector<double> candidate_costs; for (auto t : available) { Key candidate_adj; double this_cost; if (AreCompatibleTracks( prev, t, kBPMThresh, kKeyShiftThresh, candidate_adj, this_cost )) { candidates.push_back(t); candidates_adj.push_back(candidate_adj); candidate_costs.push_back(this_cost); } } for (size_t i = 0; i < candidates.size(); ++i) { Tracks now_available = available; Track t = candidates[i]; // Erase the candidate from the now available list for (size_t j = 0; j < now_available.size(); ++j) { if (now_available[j] == t) { now_available.erase(now_available.begin() + j); break; } } // Set to our new key t.key = candidates_adj[i]; Tracks now_chosen = chosen; now_chosen.push_back(t); // NOTE: We send in an ADJUSTED key for this track! ChooseTrack(names, now_available, now_chosen, t.idx, t.key, t.bpm, cost + candidate_costs[i], best_cost, max_len, best, its); } }