/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #define O_MINUTES (60) #define O_HOURS (60 * O_MINUTES) #define O_DAYS (24 * O_HOURS) Playqueue :: Playqueue() : flags(0), cur(-1), length(0) { } Playqueue :: Playqueue(playqueue_flags f) : flags(f), cur(-1), length(0) { } Playqueue :: ~Playqueue() { } void Playqueue :: write(File &f) { f << flags << " " << tracks.size(); for (unsigned int i = 0; i < tracks.size(); i++) f << " " << tracks[i]; } void Playqueue :: read(File &f) { unsigned int n; f >> flags >> n; tracks.resize(n); for (unsigned int i = 0; i < n; i++) f >> tracks[i]; } void Playqueue :: set_flag(playqueue_flags f) { flags |= f; get_callbacks()->on_queue_changed(); } void Playqueue :: unset_flag(playqueue_flags f) { flags &= ~f; get_callbacks()->on_queue_changed(); } const unsigned int Playqueue :: get_flags() { return flags; } unsigned int Playqueue :: get_length() { return length; } static inline void add_duration(std::stringstream &ss, unsigned int dur, unsigned int remaining, const std::string &field) { if (dur > 0) { ss << dur << " " << field; if (dur > 1) ss << "s"; if (remaining > 0) ss << ", "; } } std::string Playqueue :: get_length_str() { std::stringstream ss; unsigned int len = length; unsigned int days = len / O_DAYS; len -= days * O_DAYS; add_duration(ss, days, len, "day"); unsigned int hours = len / O_HOURS; len -= hours *O_HOURS; add_duration(ss, hours, len, "hour"); unsigned int mins = len / O_MINUTES; add_duration(ss, mins, len, "minute"); unsigned int secs = len - (mins * O_MINUTES); add_duration(ss, secs, 0, "second"); return ss.str(); } /* * Returns: * 0: lhs == rhs * < 0: lhs < rhs * > 0: lhs > rhs */ static inline int compare_uint(unsigned int a, unsigned int b) { if (a == b) return 0; if (a < b) return -1; return 1; } /* * std::string.compare() returns * 0: Strings are equal * < 0: a < b * > 0: a > b */ static inline int track_compare(library :: Song &lhs, library :: Song &rhs, sort_t field) { switch (field) { case SORT_ARTIST: return lhs.artist->key_lower.compare(rhs.artist->key_lower); case SORT_ALBUM: return lhs.album->name_lower.compare(rhs.album->name_lower); case SORT_COUNT: return compare_uint(lhs.track->track, rhs.track->track); case SORT_GENRE: return lhs.genre->key_lower.compare(rhs.genre->key_lower); case SORT_LENGTH: return compare_uint(lhs.track->length, rhs.track->length); case SORT_PLAYED: return compare_uint(lhs.track->play_count, rhs.track->play_count); case SORT_TITLE: return lhs.track->title_lower.compare(rhs.track->title_lower); case SORT_TRACK: return compare_uint(lhs.track->track, rhs.track->track); default: //case SORT_YEAR return compare_uint(lhs.album->year, rhs.album->year); } } static bool track_less_than(library :: Song &lhs, library :: Song &rhs, std::list &order) { std::list::iterator it; int res; for (it = order.begin(); it != order.end(); it++) { res = track_compare(lhs, rhs, *it); if (res != 0) return res < 0; } return res; } unsigned int Playqueue :: add_sorted(unsigned int track_id, library :: Song &rhs) { library :: Song lhs; unsigned int cur, start = 0, end = (tracks.size() - 1); if (tracks.size() == 0) { tracks.push_back(track_id); return 0; } while (start != end) { cur = start + ((end - start) / 2); library :: lookup(tracks[cur], &lhs); if (track_less_than(lhs, rhs, sort_order)) { if (end - start == 1) cur = end; start = cur; } else end = cur; } tracks.insert(tracks.begin() + cur, track_id); return cur; } unsigned int Playqueue :: add(unsigned int track_id) { unsigned int id = tracks.size(); library :: Song song; library :: lookup(track_id, &song); if (sort_order.size() > 0) add_sorted(track_id, song); else tracks.push_back(track_id); length += song.track->length; get_callbacks()->on_queue_track_add(this, id); if (!(flags & PQ_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); return id; } unsigned int Playqueue :: add_front(unsigned int track_id) { library :: Song song; tracks.insert(tracks.begin(), track_id); library :: lookup(track_id, &song); length += song.track->length; get_callbacks()->on_queue_track_add(this, 0); if (!(flags & PQ_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); return 0; } void Playqueue :: del(unsigned int plist_id) { library :: Song song; unsigned int track_id = tracks[plist_id]; tracks.erase(tracks.begin() + plist_id); library :: lookup(track_id, &song); length -= song.track->length; get_callbacks()->on_queue_track_del(this, plist_id); if (!(flags & PQ_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); } void Playqueue :: del_track(unsigned int track_id) { unsigned int i = 0; while (i < tracks.size()) { if (tracks[i] == track_id) del(i); else i++; } } unsigned int Playqueue :: size() { return tracks.size(); } void Playqueue :: add_sort(sort_t field) { sort_order.remove(field); sort_order.push_back(field); if (sort_order.size() >= 4) sort_order.erase(sort_order.begin()); } void Playqueue :: reset_sort(sort_t field) { sort_order.clear(); add_sort(field); } unsigned int Playqueue :: operator[](unsigned int i) { return tracks[i]; } unsigned int Playqueue :: next() { unsigned int res; if (tracks.size() == 0) throw -E_EXIST; else if (tracks.size() == 1) cur = 0; else if (flags & PQ_RANDOM) cur += rand() % (tracks.size() / 2) + 1; else cur++; if (cur >= tracks.size()) cur -= tracks.size(); res = tracks[cur]; if (!(flags & PQ_REPEAT)) { del(cur); cur--; } return res; } void Playqueue :: reset_cur() { cur = 0; } #ifdef CONFIG_TEST void Playqueue :: reset() { tracks.clear(); reset_cur(); } #endif /* CONFIG_TEST */