2013-12-31 15:44:44 -05:00
|
|
|
/*
|
|
|
|
* Copyright 2013 (c) Anna Schumaker.
|
|
|
|
*/
|
2014-01-19 11:03:14 -05:00
|
|
|
#include <callback.h>
|
2014-01-02 21:58:18 -05:00
|
|
|
#include <library.h>
|
2014-01-02 21:12:46 -05:00
|
|
|
#include <playqueue.h>
|
2014-01-22 21:17:51 -05:00
|
|
|
|
2013-12-31 15:44:44 -05:00
|
|
|
#include <stdlib.h>
|
2014-01-26 13:44:25 -05:00
|
|
|
#include <algorithm>
|
2014-01-22 21:17:51 -05:00
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#define O_MINUTES (60)
|
|
|
|
#define O_HOURS (60 * O_MINUTES)
|
|
|
|
#define O_DAYS (24 * O_HOURS)
|
2013-12-31 15:44:44 -05:00
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
Playqueue :: Playqueue()
|
2014-01-02 21:58:18 -05:00
|
|
|
: flags(0), cur(-1), length(0)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
Playqueue :: Playqueue(playqueue_flags f)
|
2014-01-02 21:58:18 -05:00
|
|
|
: flags(f), cur(-1), length(0)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
Playqueue :: ~Playqueue()
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
void Playqueue :: write(File &f)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
f << flags << " " << tracks.size();
|
|
|
|
for (unsigned int i = 0; i < tracks.size(); i++)
|
|
|
|
f << " " << tracks[i];
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
void Playqueue :: read(File &f)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
f >> flags >> n;
|
|
|
|
tracks.resize(n);
|
|
|
|
for (unsigned int i = 0; i < n; i++)
|
|
|
|
f >> tracks[i];
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
void Playqueue :: set_flag(playqueue_flags f)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
flags |= f;
|
2014-01-24 21:43:18 -05:00
|
|
|
get_callbacks()->on_queue_changed();
|
2013-12-31 15:44:44 -05:00
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
void Playqueue :: unset_flag(playqueue_flags f)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
flags &= ~f;
|
2014-01-24 21:43:18 -05:00
|
|
|
get_callbacks()->on_queue_changed();
|
2013-12-31 15:44:44 -05:00
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
const unsigned int Playqueue :: get_flags()
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:58:18 -05:00
|
|
|
unsigned int Playqueue :: get_length()
|
|
|
|
{
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2014-01-22 21:17:51 -05:00
|
|
|
static inline void add_duration(std::stringstream &ss, unsigned int dur,
|
|
|
|
unsigned int remaining, const std::string &field)
|
|
|
|
{
|
|
|
|
if (dur > 0) {
|
|
|
|
ss << dur << " " << field;
|
2014-01-24 16:19:33 -05:00
|
|
|
if (dur > 1)
|
|
|
|
ss << "s";
|
2014-01-22 21:17:51 -05:00
|
|
|
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;
|
2014-01-24 16:19:33 -05:00
|
|
|
add_duration(ss, days, len, "day");
|
2014-01-22 21:17:51 -05:00
|
|
|
|
|
|
|
unsigned int hours = len / O_HOURS;
|
|
|
|
len -= hours *O_HOURS;
|
2014-01-24 16:19:33 -05:00
|
|
|
add_duration(ss, hours, len, "hour");
|
2014-01-22 21:17:51 -05:00
|
|
|
unsigned int mins = len / O_MINUTES;
|
2014-01-24 16:19:33 -05:00
|
|
|
add_duration(ss, mins, len, "minute");
|
2014-01-22 21:17:51 -05:00
|
|
|
unsigned int secs = len - (mins * O_MINUTES);
|
2014-01-24 16:19:33 -05:00
|
|
|
add_duration(ss, secs, 0, "second");
|
2014-01-22 21:17:51 -05:00
|
|
|
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
2014-01-25 14:43:51 -05:00
|
|
|
/*
|
|
|
|
* std::string.compare() returns
|
|
|
|
* 0: Strings are equal
|
|
|
|
* < 0: a < b
|
|
|
|
* > 0: a > b
|
|
|
|
*/
|
2014-03-29 11:44:39 -04:00
|
|
|
static inline int track_compare(Track *lhs, Track *rhs, sort_t field)
|
2014-01-25 14:43:51 -05:00
|
|
|
{
|
2014-03-29 11:44:39 -04:00
|
|
|
int ret = lhs->less_than(rhs, field);
|
|
|
|
if (field == SORT_YEAR && ret == 0)
|
|
|
|
ret = lhs->less_than(rhs, SORT_ALBUM);
|
|
|
|
return ret;
|
2014-01-26 12:12:01 -05:00
|
|
|
}
|
2014-01-25 14:43:51 -05:00
|
|
|
|
2014-03-29 11:44:39 -04:00
|
|
|
static bool track_less_than(Track *lhs, Track *rhs, std::list<sort_info> &order)
|
2014-01-26 12:12:01 -05:00
|
|
|
{
|
2014-01-26 14:44:56 -05:00
|
|
|
std::list<sort_info>::iterator it;
|
2014-01-26 12:12:01 -05:00
|
|
|
int res;
|
2014-01-25 14:43:51 -05:00
|
|
|
|
2014-01-26 12:12:01 -05:00
|
|
|
for (it = order.begin(); it != order.end(); it++) {
|
2014-01-26 14:44:56 -05:00
|
|
|
if (it->ascending == true)
|
|
|
|
res = track_compare(lhs, rhs, it->field);
|
|
|
|
else
|
|
|
|
res = track_compare(rhs, lhs, it->field);
|
2014-01-26 12:12:01 -05:00
|
|
|
if (res != 0)
|
|
|
|
return res < 0;
|
|
|
|
}
|
|
|
|
return res;
|
2014-01-25 14:43:51 -05:00
|
|
|
}
|
|
|
|
|
2014-03-29 11:44:39 -04:00
|
|
|
unsigned int Playqueue :: find_sorted_id(Track *rhs)
|
2014-01-25 14:43:51 -05:00
|
|
|
{
|
2014-03-29 11:44:39 -04:00
|
|
|
Track *lhs;
|
2014-01-28 09:42:38 -05:00
|
|
|
unsigned int begin = 0, end = (tracks.size() - 1), mid;
|
2014-01-25 14:43:51 -05:00
|
|
|
|
2014-01-28 09:42:38 -05:00
|
|
|
if (tracks.size() == 0)
|
2014-01-27 11:22:35 -05:00
|
|
|
return 0;
|
2014-01-26 10:41:13 -05:00
|
|
|
|
2014-01-28 09:42:38 -05:00
|
|
|
while (end > begin) {
|
|
|
|
mid = begin + ((end - begin) / 2);
|
2014-03-29 11:44:39 -04:00
|
|
|
lhs = tagdb :: lookup(tracks[mid]);
|
2014-01-27 11:22:35 -05:00
|
|
|
if (track_less_than(lhs, rhs, sort_order))
|
2014-01-28 09:42:38 -05:00
|
|
|
begin = mid + 1;
|
|
|
|
else {
|
|
|
|
if (mid == begin)
|
|
|
|
return begin;
|
|
|
|
else
|
|
|
|
end = mid - 1;
|
2014-01-27 11:22:35 -05:00
|
|
|
}
|
2014-01-27 10:23:22 -05:00
|
|
|
}
|
|
|
|
|
2014-03-29 11:44:39 -04:00
|
|
|
lhs = tagdb :: lookup(tracks[begin]);
|
2014-01-28 09:42:38 -05:00
|
|
|
if (track_less_than(lhs, rhs, sort_order))
|
|
|
|
return begin + 1;
|
|
|
|
return begin;
|
2014-01-25 14:43:51 -05:00
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
unsigned int Playqueue :: add(unsigned int track_id)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
2014-01-20 19:06:52 -05:00
|
|
|
unsigned int id = tracks.size();
|
2014-03-29 11:44:39 -04:00
|
|
|
Track *track = tagdb :: lookup(track_id);
|
2014-01-25 14:43:51 -05:00
|
|
|
|
2014-01-26 12:12:01 -05:00
|
|
|
if (sort_order.size() > 0)
|
2014-03-29 11:44:39 -04:00
|
|
|
id = find_sorted_id(track);
|
2014-01-25 14:43:51 -05:00
|
|
|
|
2014-01-28 09:42:38 -05:00
|
|
|
tracks.insert(tracks.begin() + id, track_id);
|
2014-03-29 11:44:39 -04:00
|
|
|
length += track->length;
|
2014-01-20 19:06:52 -05:00
|
|
|
get_callbacks()->on_queue_track_add(this, id);
|
2014-01-25 13:21:57 -05:00
|
|
|
if (!(flags & PQ_DISABLE_CHANGED_SIZE))
|
|
|
|
get_callbacks()->on_queue_changed();
|
2014-01-20 19:06:52 -05:00
|
|
|
return id;
|
2013-12-31 15:44:44 -05:00
|
|
|
}
|
|
|
|
|
2014-01-02 21:58:18 -05:00
|
|
|
unsigned int Playqueue :: add_front(unsigned int track_id)
|
|
|
|
{
|
2014-03-29 11:44:39 -04:00
|
|
|
Track *track;
|
2014-01-02 21:58:18 -05:00
|
|
|
tracks.insert(tracks.begin(), track_id);
|
|
|
|
|
2014-03-29 11:44:39 -04:00
|
|
|
track = tagdb :: lookup(track_id);
|
|
|
|
length += track->length;
|
2014-01-20 19:06:52 -05:00
|
|
|
get_callbacks()->on_queue_track_add(this, 0);
|
2014-01-25 13:21:57 -05:00
|
|
|
if (!(flags & PQ_DISABLE_CHANGED_SIZE))
|
|
|
|
get_callbacks()->on_queue_changed();
|
2014-01-02 21:58:18 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
void Playqueue :: del(unsigned int plist_id)
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
2014-03-29 11:44:39 -04:00
|
|
|
Track *track;
|
2014-01-02 21:58:18 -05:00
|
|
|
unsigned int track_id = tracks[plist_id];
|
|
|
|
|
2013-12-31 15:44:44 -05:00
|
|
|
tracks.erase(tracks.begin() + plist_id);
|
2014-03-29 11:44:39 -04:00
|
|
|
track = tagdb :: lookup(track_id);
|
|
|
|
length -= track->length;
|
2014-01-20 19:06:52 -05:00
|
|
|
get_callbacks()->on_queue_track_del(this, plist_id);
|
2014-01-25 13:21:57 -05:00
|
|
|
if (!(flags & PQ_DISABLE_CHANGED_SIZE))
|
|
|
|
get_callbacks()->on_queue_changed();
|
2013-12-31 15:44:44 -05:00
|
|
|
}
|
|
|
|
|
2014-01-16 22:05:36 -05:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-29 22:45:01 -05:00
|
|
|
void Playqueue :: track_updated(unsigned int track_id)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < tracks.size(); i++) {
|
|
|
|
if (tracks[i] == track_id)
|
|
|
|
get_callbacks()->on_queue_track_changed(this, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
unsigned int Playqueue :: size()
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
return tracks.size();
|
|
|
|
}
|
|
|
|
|
2014-01-26 13:44:25 -05:00
|
|
|
|
|
|
|
/* Sorting function */
|
|
|
|
class SortTracks {
|
|
|
|
private:
|
2014-01-26 14:44:56 -05:00
|
|
|
std::list<sort_info> fields;
|
2014-01-26 13:44:25 -05:00
|
|
|
public:
|
2014-01-26 14:44:56 -05:00
|
|
|
SortTracks(std::list<sort_info> f) : fields(f) {}
|
2014-01-26 13:44:25 -05:00
|
|
|
bool operator()(unsigned int a, unsigned int b)
|
|
|
|
{
|
2014-03-29 11:44:39 -04:00
|
|
|
Track *lhs = tagdb :: lookup(a);
|
|
|
|
Track *rhs = tagdb :: lookup(b);
|
2014-01-26 13:44:25 -05:00
|
|
|
return track_less_than(lhs, rhs, fields);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-01-29 21:37:06 -05:00
|
|
|
void Playqueue :: _add_sort(sort_t field, bool ascending)
|
2014-01-26 12:12:01 -05:00
|
|
|
{
|
2014-01-26 14:44:56 -05:00
|
|
|
struct sort_info info;
|
|
|
|
std::list<sort_info>::iterator it;
|
|
|
|
|
|
|
|
/* Is field already in the sort_order? */
|
|
|
|
for (it = sort_order.begin(); it != sort_order.end(); it++) {
|
|
|
|
if (it->field == field) {
|
|
|
|
it->ascending = !it->ascending;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info.field = field;
|
2014-01-29 21:37:06 -05:00
|
|
|
info.ascending = ascending;
|
2014-01-26 14:44:56 -05:00
|
|
|
sort_order.push_back(info);
|
2014-01-26 12:12:01 -05:00
|
|
|
if (sort_order.size() >= 4)
|
|
|
|
sort_order.erase(sort_order.begin());
|
2014-01-26 14:44:56 -05:00
|
|
|
}
|
|
|
|
|
2014-01-29 21:37:06 -05:00
|
|
|
void Playqueue :: add_sort(sort_t field, bool ascending)
|
2014-01-26 14:44:56 -05:00
|
|
|
{
|
2014-01-26 14:49:07 -05:00
|
|
|
if (flags & PQ_NEVER_SORT)
|
|
|
|
return;
|
|
|
|
|
2014-01-29 21:37:06 -05:00
|
|
|
_add_sort(field, ascending);
|
2014-01-26 13:44:25 -05:00
|
|
|
std::stable_sort(tracks.begin(), tracks.end(), SortTracks(sort_order));
|
2014-01-26 14:44:56 -05:00
|
|
|
|
2014-01-26 13:44:25 -05:00
|
|
|
for (unsigned int i = 0; i < tracks.size(); i++)
|
|
|
|
get_callbacks()->on_queue_track_changed(this, i);
|
2014-01-26 14:44:56 -05:00
|
|
|
get_callbacks()->on_queue_changed();
|
2014-01-26 12:12:01 -05:00
|
|
|
}
|
|
|
|
|
2014-01-29 21:37:06 -05:00
|
|
|
void Playqueue :: reset_sort(sort_t field, bool ascending)
|
2014-01-26 12:12:01 -05:00
|
|
|
{
|
2014-01-26 14:49:07 -05:00
|
|
|
if (flags & PQ_NEVER_SORT)
|
|
|
|
return;
|
2014-01-28 09:42:38 -05:00
|
|
|
if (sort_order.front().field != field)
|
|
|
|
sort_order.clear();
|
2014-01-29 21:37:06 -05:00
|
|
|
add_sort(field, ascending);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playqueue :: force_clear_sort()
|
|
|
|
{
|
|
|
|
sort_order.clear();
|
2014-01-26 12:12:01 -05:00
|
|
|
}
|
|
|
|
|
2014-01-26 14:44:56 -05:00
|
|
|
std::list<sort_info> &Playqueue :: get_sort_order()
|
|
|
|
{
|
|
|
|
return sort_order;
|
|
|
|
}
|
|
|
|
|
2014-01-20 19:06:52 -05:00
|
|
|
unsigned int Playqueue :: operator[](unsigned int i)
|
|
|
|
{
|
|
|
|
return tracks[i];
|
|
|
|
}
|
|
|
|
|
2014-01-02 21:12:46 -05:00
|
|
|
unsigned int Playqueue :: next()
|
2013-12-31 15:44:44 -05:00
|
|
|
{
|
|
|
|
unsigned int res;
|
|
|
|
|
2014-01-04 13:09:44 -05:00
|
|
|
if (tracks.size() == 0)
|
|
|
|
throw -E_EXIST;
|
|
|
|
else if (tracks.size() == 1)
|
2013-12-31 15:44:44 -05:00
|
|
|
cur = 0;
|
2014-01-02 21:12:46 -05:00
|
|
|
else if (flags & PQ_RANDOM)
|
2013-12-31 15:44:44 -05:00
|
|
|
cur += rand() % (tracks.size() / 2) + 1;
|
|
|
|
else
|
|
|
|
cur++;
|
|
|
|
|
|
|
|
if (cur >= tracks.size())
|
|
|
|
cur -= tracks.size();
|
|
|
|
|
|
|
|
res = tracks[cur];
|
2014-01-02 21:12:46 -05:00
|
|
|
if (!(flags & PQ_REPEAT)) {
|
2014-01-02 21:58:18 -05:00
|
|
|
del(cur);
|
2013-12-31 15:44:44 -05:00
|
|
|
cur--;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2014-01-04 10:54:02 -05:00
|
|
|
|
2014-01-29 22:11:03 -05:00
|
|
|
void Playqueue :: set_cur(unsigned int c)
|
2014-01-04 10:54:02 -05:00
|
|
|
{
|
2014-01-29 22:11:03 -05:00
|
|
|
cur = c;
|
2014-01-04 10:54:02 -05:00
|
|
|
}
|
2014-01-22 19:34:01 -05:00
|
|
|
|
2014-02-06 20:06:58 -05:00
|
|
|
void Playqueue :: path_selected(unsigned int id)
|
|
|
|
{
|
|
|
|
cur = id;
|
|
|
|
if (!(flags &PQ_REPEAT)) {
|
|
|
|
del(cur);
|
|
|
|
cur--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-22 19:34:01 -05:00
|
|
|
#ifdef CONFIG_TEST
|
|
|
|
void Playqueue :: reset()
|
|
|
|
{
|
|
|
|
tracks.clear();
|
2014-01-30 22:52:44 -05:00
|
|
|
set_cur(0);
|
2014-01-22 19:34:01 -05:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_TEST */
|