From 211d240484ebfdc755c30f937c127def42947e46 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Mon, 26 May 2014 22:20:07 -0400 Subject: [PATCH] deck: Rework the deck code I update the unit test and redo much of the code to be cleaner and make more sense. One big improvement is that the recently played queue will now be managed by the deck. Signed-off-by: Anna Schumaker --- DESIGN | 22 ++-- include/callback.h | 1 - include/deck.h | 19 ++-- lib/callback.cpp | 1 - lib/deck.cpp | 260 ++++++++++++++++++++++----------------------- tests/deck.cpp | 255 ++++++++++++++++++++++---------------------- 6 files changed, 274 insertions(+), 284 deletions(-) diff --git a/DESIGN b/DESIGN index 4e2c67e1..4ae018fe 100644 --- a/DESIGN +++ b/DESIGN @@ -1176,7 +1176,8 @@ Playlist: Deck: The deck is used to hold temporary queues created by the user. This layer is also in charge of maintaining a "recently played" queue of - tracks. + tracks. The function deck :: write() must be called whenever a queue + is modified to save the changes to disk. The deck will be saved to the file "deck". When upgrading from file version V0 to V1, use the saved random flag and sort order to set up @@ -1233,18 +1234,14 @@ Deck: Remove the requested queue from the deck and trigger the on_pq_removed() callback. Save the deck to disk. - void deck :: add(Track *track, Queue *queue); - Add the requested track to the requested queue. Save the deck - to disk. - - void deck :: remove(Track *track, Queue *queue); - Remove the requested track from the requested queue. Save the - deck to disk. - void deck :: move(Queue *queue, unsigned int pos); Move the queue to the new location in the deck. Save the deck to disk. + unsigned int deck :: index(Queue *queue); + Return the index of the queue in the deck or deck.size() if + the queue is not currently in the deck. + Track *deck :: next(); Find the first enabled queue on the deck and return the track given by queue->next(). @@ -1255,8 +1252,15 @@ Deck: If there are no enabled queues, return a track from the library queue. If the library queue is empty, return NULL. + If the result is non-NULL, add the found track to the recent + queue. + Save the deck before returning. + Track *deck :: prev(); + Return the track given by recent_queue->next(). If the recent + queue is empty, return NULL. + list &deck :: get_queues(); Return the list of queues to the caller. diff --git a/include/callback.h b/include/callback.h index 722f1516..af4b9427 100644 --- a/include/callback.h +++ b/include/callback.h @@ -16,7 +16,6 @@ struct Callbacks { void (*on_pause_count_changed)(bool, unsigned int); /* Deck callbacks */ - void (*on_pq_created)(Queue *, unsigned int); void (*on_pq_removed)(Queue *); /* Queue callbacks */ diff --git a/include/deck.h b/include/deck.h index 5c776b83..421e434a 100644 --- a/include/deck.h +++ b/include/deck.h @@ -5,27 +5,24 @@ #define OCARINA_DECK_H #include +#include namespace deck { void init(); - void read(); void write(); Queue *create(bool); - void remove(unsigned int); - Queue *get(unsigned int); - unsigned int size(); - void move(unsigned int, unsigned int); + void destroy(Queue *); void move(Queue *, unsigned int); - Track *next(); - Queue *get_library_pq(); + unsigned int index(Queue *); -#ifdef CONFIG_TEST - void reset(); - void print_info(); -#endif /* CONFIG_TEST */ + Track *next(); + Track *prev(); + + std::list &get_queues(); + Queue *get_queue(); }; diff --git a/lib/callback.cpp b/lib/callback.cpp index 3f71a2c9..573853a3 100644 --- a/lib/callback.cpp +++ b/lib/callback.cpp @@ -17,7 +17,6 @@ static struct Callbacks callbacks = { .on_track_loaded = no_op, .on_pause_count_changed = no_op, - .on_pq_created = no_op, .on_pq_removed = no_op, .on_queue_track_add = no_op, diff --git a/lib/deck.cpp b/lib/deck.cpp index daa92a5e..b95fecb6 100644 --- a/lib/deck.cpp +++ b/lib/deck.cpp @@ -3,193 +3,183 @@ */ #include #include -#include #include -#include +#include -#include -static std::list playqueue_deck; -static File deck_file("deck", 0); +class RecentQueue : public Queue +{ +public: + RecentQueue() : Queue(Q_ENABLED | Q_REPEAT | Q_NO_SORT) {} + + unsigned int add(Track *track) + { + del(track); + _cur = 0; + return _add_at(track, 0); + } +}; + + +static std::list queue_deck; +static RecentQueue recent_queue; +static File deck_file("deck", 1); + + +static void upgrade_v0() +{ + int random, ascending; + unsigned int num, field; + Queue *library = library :: get_queue(); + + deck_file >> random >> num; + if (random) + library->set_flag(Q_RANDOM); + + for (unsigned int i = 0; i < num; i++) { + deck_file >> field >> ascending; + library->sort((sort_t)field, (i == 0) ? true : false); + if (!ascending) + library->sort((sort_t)field, false); + } +} void deck :: init() -{ - read(); - get_callbacks()->on_queue_changed = write; -} - -void deck :: read() { unsigned int num; - int random; - unsigned int field; - bool ascending; + bool upgraded = false; std::list::iterator it; - if (!deck_file.exists()) + if (!deck_file.open(OPEN_READ)) return; - deck_file.open(OPEN_READ); - deck_file >> random >> num; - for (unsigned int i = 0; i < num; i++) { - deck_file >> field >> ascending; - //library_playqueue.sort((sort_t)field, i == 0); - //if (!ascending) - // library_playqueue.sort((sort_t)field, false); + if (deck_file.get_version() == 0) { + upgrade_v0(); + upgraded = true; } deck_file >> num; - playqueue_deck.resize(num); + queue_deck.resize(num); - num = 0; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { + for (it = queue_deck.begin(); it != queue_deck.end(); it++) it->read(deck_file); - get_callbacks()->on_pq_created(&(*it), num); - num++; - } deck_file.close(); + + if (upgraded) + deck :: write(); } void deck :: write() { std::list::iterator it; - //std::list::iterator st; - //std::list sort_order; - deck_file.open(OPEN_WRITE); + if (!deck_file.open(OPEN_WRITE)) + return; - /* Save library playqueue */ - //sort_order = library_playqueue.get_sort_order(); - deck_file << false << " "; //library_playqueue.has_flag(Q_RANDOM) << " "; - deck_file << 0 /* sort_order.size() */ << " "; - //for (st = sort_order.begin(); st != sort_order.end(); st++) - // deck_file << st->field << " " << st->ascending << " "; - - deck_file << playqueue_deck.size() << std :: endl; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { + deck_file << queue_deck.size() << std :: endl; + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { it->write(deck_file); deck_file << std::endl; } + deck_file.close(); } Queue *deck :: create(bool random) { - Queue *pq; - playqueue_deck.push_back(Queue(Q_ENABLED)); - pq = &playqueue_deck.back(); - if (random == true) - pq->set_flag(Q_RANDOM); - get_callbacks()->on_pq_created(pq, playqueue_deck.size() - 1); - return pq; + unsigned int flag = (random ? Q_RANDOM : 0); + queue_deck.push_back(Queue(Q_ENABLED | flag)); + return &queue_deck.back(); } -void deck :: remove(unsigned int id) +static void _destroy(std::list::iterator &it) { - std::list::iterator it = playqueue_deck.begin(); - for (unsigned int i = 0; i < id; i++) - it++; get_callbacks()->on_pq_removed(&(*it)); - playqueue_deck.erase(it); - write(); + queue_deck.erase(it); + deck :: write(); } -Queue *deck :: get(unsigned int id) +void deck :: destroy(Queue *queue) { - std::list::iterator it = playqueue_deck.begin(); - for (unsigned int i = 0; i < id; i++) - it++; - return &(*it); -} - -unsigned int deck :: size() -{ - return playqueue_deck.size(); -} - -void deck :: move(unsigned int old_pos, unsigned int new_pos) -{ - std::list::iterator it_old = playqueue_deck.begin(); - std::list::iterator it_new = playqueue_deck.begin(); - - for (unsigned int i = 0; i < playqueue_deck.size(); i++) { - if (i < old_pos) - it_old++; - if (i < new_pos) - it_new++; - } - - if (new_pos > old_pos) - it_new++; - - playqueue_deck.splice(it_new, playqueue_deck, it_old); -} - -void deck :: move(Queue *pq, unsigned int new_pos) -{ - unsigned int old_pos = 0; - std::list::iterator it_old = playqueue_deck.begin(); - std::list::iterator it_new = playqueue_deck.begin(); - - for (unsigned int i = 0; i < playqueue_deck.size(); i++) { - if (&(*it_old) != pq) { - it_old++; - old_pos++; - } - if (i < new_pos) - it_new++; - } - - if (new_pos > old_pos) - it_new++; - - playqueue_deck.splice(it_new, playqueue_deck, it_old); -} - -Track *deck :: next() -{ - Track *track; std::list::iterator it; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - if (it->has_flag(Q_ENABLED)) { - if (it->size() == 0) { - playqueue_deck.erase(it); - get_callbacks()->on_pq_removed(&(*it)); - } else { - track = it->next(); - if (it->size() == 0) { - playqueue_deck.erase(it); - get_callbacks()->on_pq_removed(&(*it)); - } - } - write(); - return track; + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (&(*it) == queue) { + _destroy(it); + return; } } - - return library :: get_queue()->next(); } -#ifdef CONFIG_TEST -static void no_op() {} - -void deck :: reset() +void deck :: move(Queue *queue, unsigned int new_pos) { - get_callbacks()->on_queue_changed = no_op; - playqueue_deck.clear(); - //library_playqueue.reset(); + unsigned int old_pos = deck :: index(queue); + std::list::iterator it_old = queue_deck.begin(); + std::list::iterator it_new = queue_deck.begin(); + + for (unsigned int i = 0; i < queue_deck.size(); i++) { + if (i < old_pos) + it_old++; + + if (i < new_pos) + it_new++; + } + + if (new_pos > old_pos) + it_new++; + + queue_deck.splice(it_new, queue_deck, it_old); } -void deck :: print_info() +unsigned int deck :: index(Queue *queue) { unsigned int i = 0; std::list::iterator it; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - print("deck[%u] = Queue { size = %u, flags = %u }\n", - i, it->size()); + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (&(*it) == queue) + return i; i++; } + + return queue_deck.size(); +} + +Track *deck :: next() +{ + Track *track = NULL; + std::list::iterator it; + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (it->has_flag(Q_ENABLED) == false) + continue; + + track = it->next(); + if (it->size() == 0) + _destroy(it); + else + write(); + break; + } + + if (!track) + track = library :: get_queue()->next(); + + recent_queue.add(track); + return track; +} + +Track *deck :: prev() +{ + return recent_queue.next(); +} + +std::list &deck :: get_queues() +{ + return queue_deck; +} + +Queue *deck :: get_queue() +{ + return &recent_queue; } -#endif /* CONFIG_TEST */ diff --git a/tests/deck.cpp b/tests/deck.cpp index 0fb83d4a..6944bc79 100644 --- a/tests/deck.cpp +++ b/tests/deck.cpp @@ -1,153 +1,154 @@ /* * Copyright 2013 (c) Anna Schumaker. */ - - - -int main(int argc, char **argv) -{ - return 0; -} - - -/*#include -#include +#include +#include #include -#include +#include "test.h" -void test_add_playlist(unsigned int size) -{ - Playqueue *pqueue = deck :: create(false); - for (unsigned i = 0; i < size; i++) - pqueue->add(i); -} -*/ -/* Test creating a deck of playlists */ -/*void test_0() -{ - for (unsigned int i = 0; i < 10; i++) - test_add_playlist(10 + i); - print("Test 0:\n"); - deck :: print_info(); - print("\n"); -} -*/ -/* Test removing playlists from the deck */ -/*void test_1() -{ - print("Test 1:\n"); - deck :: remove(3); - deck :: remove(7); - deck :: remove(1); - deck :: remove(5); - deck :: print_info(); - print("\n"); -} -*/ -/* Get a specific playlist from the deck */ -/*void test_2() -{ - Playqueue *pqueue; - print("Test 2: "); +static Queue *Q_NULL = NULL; +static Track *TRACK_NULL = NULL; - pqueue = deck :: get(3); - print("Playqueue { size = %u, flags = %u }", pqueue->size(), pqueue->get_flags()); - print("\n\n"); -}*/ - -/* Move a playlist to a new position in the deck */ -/*void test_3() +static void test_init() { - print("Test 3:\n"); - deck :: move(4, 0); - deck :: print_info(); - print("\n"); - deck :: move(5, 1); - deck :: print_info(); - print("\n"); - deck :: move(2, 5); - deck :: print_info(); - print("\n"); - deck :: move(3, 4); - deck :: print_info(); - print("\n"); - deck :: move(4, 3); - deck :: print_info(); - print("\n"); - deck :: move(4, 4); - deck :: print_info(); - print("\n"); -}*/ + unsigned int val; + File f("deck", 0); + std::list::iterator it; -/* Test the next() function for playlists */ -/*void test_4() -{ - print("Test 4:\n"); + deck :: init(); + test_equal(library :: get_queue()->has_flag(Q_RANDOM), true); + test_equal(deck :: get_queues().size(), (size_t)2); - deck :: get(0)->unset_flag(PQ_ENABLED); - deck :: get(1)->unset_flag(PQ_ENABLED); - deck :: get(4)->unset_flag(PQ_ENABLED); + it = deck :: get_queues().begin(); + test_equal(it->size(), (unsigned)4); + for (unsigned int i = 0; i < 4; i++) + test_equal((*it)[i]->id, i); - for (unsigned int i = 0; i < 40; i++) { - print("Playing id: %u\n", deck :: next()); - if (i == 11 || i == 25) - deck :: print_info(); + it++; + test_equal(it->size(), (unsigned)5); + for (unsigned int i = 0; i < 5; i++) + test_equal((*it)[i]->id, i + 4); + + /* + * Test that we saved the deck in the new format + */ + f.open(OPEN_READ); + test_equal(f.get_version(), (unsigned)1); + f >> val; /* number of queues */ + test_equal(val, (unsigned)2); + + for (unsigned int i = 0; i < 2; i++) { + f >> val; /* queues[i].flags */ + test_equal(val, (unsigned)1); + + f >> val; /* queues[i].size */ + test_equal(val, 4 + i); + + for (unsigned int j = 0; j < 4 + i; j++) { + f >> val; + test_equal(val, (4 * i) + j); + } } - deck :: print_info(); - print("\n"); -}*/ + f.close(); +} -/* Test load / save functions */ -/*void test_5() +static unsigned int n = 0; +static void on_queue_removed(Queue *queue) { - print("Test 5:\n"); + n++; +} - deck :: get(1)->set_flag(PQ_ENABLED); - deck :: get(2)->set_flag(PQ_ENABLED); - deck :: get(2)->set_flag(PQ_RANDOM); +static void test_create_mv_destroy() +{ + Queue *q1, *q2; - print("Saving playqueue deck\n"); - deck :: write(); + q1 = deck :: create(true); + test_not_equal(q1, Q_NULL); + test_equal(q1->has_flag(Q_ENABLED), true); + test_equal(q1->has_flag(Q_RANDOM), true); + test_equal(deck :: index(q1), (unsigned)2); - print("Clearing deck\n"); - deck :: reset(); - deck :: print_info(); + q2 = deck :: create(false); + test_not_equal(q2, Q_NULL); + test_equal(q2->has_flag(Q_ENABLED), true); + test_equal(q2->has_flag(Q_RANDOM), false); + test_equal(deck :: index(q2), (unsigned)3); - print("Reading back playqueue deck\n"); - deck :: init(); + deck :: move(q1, 3); + test_equal(deck :: index(q1), (unsigned)3); + deck :: move(q1, 3); + test_equal(deck :: index(q1), (unsigned)3); + deck :: move(q1, 2); + test_equal(deck :: index(q1), (unsigned)2); - deck :: print_info(); + get_callbacks()->on_pq_removed = on_queue_removed; + + deck :: destroy(q1); + test_equal(n, (unsigned)1); + test_equal(deck :: index(q1), (unsigned)3); + test_equal(deck :: index(q2), (unsigned)2); + + deck :: destroy(q2); + test_equal(n, (unsigned)2); + test_equal(deck :: index(q2), (unsigned)2); +} + +static void test_next_prev() +{ + std::list::iterator it = deck :: get_queues().begin(); + Queue *q = deck :: get_queue(); + Queue *q0 = &(*it++); + Queue *q1 = &(*it++); + + q0->unset_flag(Q_RANDOM); + for (unsigned int i = 0; i < 4; i++) + q0->add(tagdb :: lookup(i)); + + test_not_equal(q, Q_NULL); + test_equal(q->size(), (unsigned)0); + test_equal(deck :: prev(), TRACK_NULL); + + for (unsigned int i = 0; i < 2; i++) { + test_equal(deck :: next()->id, (unsigned)0); + test_equal(deck :: next()->id, (unsigned)1); + test_equal(deck :: next()->id, (unsigned)2); + test_equal(deck :: next()->id, (unsigned)3); + test_equal(q->size(), (unsigned)4); + } + + for (unsigned int i = 0; i < 2; i++) { + if (i == 1) + test_equal(deck :: prev()->id, (unsigned)3); + test_equal(deck :: prev()->id, (unsigned)2); + test_equal(deck :: prev()->id, (unsigned)1); + test_equal(deck :: prev()->id, (unsigned)0); + } + + test_equal(deck :: get_queues().size(), (size_t)1); + test_equal(deck :: index(q1), (unsigned)0); + + q1->unset_flag(Q_ENABLED); + library :: get_queue()->unset_flag(Q_RANDOM); + + test_equal(q1->size(), (unsigned)5); + deck :: next(); + test_equal(q1->size(), (unsigned)5); + + q1->set_flag(Q_ENABLED); + for (unsigned int i = 0; i < 5; i++) + deck :: next(); + test_equal(deck :: get_queues().size(), (size_t)0); } int main(int argc, char **argv) { + test :: cp_data_dir(); + tagdb :: init(); library :: init(); - deck :: init(); - library :: reset(); - library :: add_path("/tmp/library/0"); - while (idle :: run_task()); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - test_0(); - test_1(); - test_2(); - test_3(); - test_4(); - test_5(); - - print("Disabling library path\n"); - library :: set_enabled(0, false); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - print("Enabling library path\n"); - library :: set_enabled(0, true); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - print("Deleting library path\n"); - library :: del_path(0); - while (idle :: run_task()); - print("Library size: %u\n", deck :: get_library_pq()->size()); + run_test("Deck Init Test", test_init); + run_test("Deck Create, Move and Destroy Test", test_create_mv_destroy); + run_test("Deck Next and Prev Test", test_next_prev); return 0; -}*/ +}