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 <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2014-05-26 22:20:07 -04:00
parent b6156bab11
commit 211d240484
6 changed files with 274 additions and 284 deletions

22
DESIGN
View File

@ -1176,7 +1176,8 @@ Playlist:
Deck: Deck:
The deck is used to hold temporary queues created by the user. This 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 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 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 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 Remove the requested queue from the deck and trigger the
on_pq_removed() callback. Save the deck to disk. 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); void deck :: move(Queue *queue, unsigned int pos);
Move the queue to the new location in the deck. Save the deck Move the queue to the new location in the deck. Save the deck
to disk. 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(); Track *deck :: next();
Find the first enabled queue on the deck and return the track Find the first enabled queue on the deck and return the track
given by queue->next(). given by queue->next().
@ -1255,8 +1252,15 @@ Deck:
If there are no enabled queues, return a track from the library If there are no enabled queues, return a track from the library
queue. If the library queue is empty, return NULL. 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. 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<Queue> &deck :: get_queues(); list<Queue> &deck :: get_queues();
Return the list of queues to the caller. Return the list of queues to the caller.

View File

@ -16,7 +16,6 @@ struct Callbacks {
void (*on_pause_count_changed)(bool, unsigned int); void (*on_pause_count_changed)(bool, unsigned int);
/* Deck callbacks */ /* Deck callbacks */
void (*on_pq_created)(Queue *, unsigned int);
void (*on_pq_removed)(Queue *); void (*on_pq_removed)(Queue *);
/* Queue callbacks */ /* Queue callbacks */

View File

@ -5,27 +5,24 @@
#define OCARINA_DECK_H #define OCARINA_DECK_H
#include <queue.h> #include <queue.h>
#include <list>
namespace deck namespace deck
{ {
void init(); void init();
void read();
void write(); void write();
Queue *create(bool); Queue *create(bool);
void remove(unsigned int); void destroy(Queue *);
Queue *get(unsigned int);
unsigned int size();
void move(unsigned int, unsigned int);
void move(Queue *, unsigned int); void move(Queue *, unsigned int);
Track *next(); unsigned int index(Queue *);
Queue *get_library_pq();
#ifdef CONFIG_TEST Track *next();
void reset(); Track *prev();
void print_info();
#endif /* CONFIG_TEST */ std::list<Queue> &get_queues();
Queue *get_queue();
}; };

View File

@ -17,7 +17,6 @@ static struct Callbacks callbacks = {
.on_track_loaded = no_op, .on_track_loaded = no_op,
.on_pause_count_changed = no_op, .on_pause_count_changed = no_op,
.on_pq_created = no_op,
.on_pq_removed = no_op, .on_pq_removed = no_op,
.on_queue_track_add = no_op, .on_queue_track_add = no_op,

View File

@ -3,193 +3,183 @@
*/ */
#include <callback.h> #include <callback.h>
#include <deck.h> #include <deck.h>
#include <error.h>
#include <file.h> #include <file.h>
#include <print.h> #include <library.h>
#include <list>
static std::list<Queue> playqueue_deck; class RecentQueue : public Queue
static File deck_file("deck", 0); {
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> 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() void deck :: init()
{
read();
get_callbacks()->on_queue_changed = write;
}
void deck :: read()
{ {
unsigned int num; unsigned int num;
int random; bool upgraded = false;
unsigned int field;
bool ascending;
std::list<Queue>::iterator it; std::list<Queue>::iterator it;
if (!deck_file.exists()) if (!deck_file.open(OPEN_READ))
return; return;
deck_file.open(OPEN_READ); if (deck_file.get_version() == 0) {
deck_file >> random >> num; upgrade_v0();
for (unsigned int i = 0; i < num; i++) { upgraded = true;
deck_file >> field >> ascending;
//library_playqueue.sort((sort_t)field, i == 0);
//if (!ascending)
// library_playqueue.sort((sort_t)field, false);
} }
deck_file >> num; deck_file >> num;
playqueue_deck.resize(num); queue_deck.resize(num);
num = 0; for (it = queue_deck.begin(); it != queue_deck.end(); it++)
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) {
it->read(deck_file); it->read(deck_file);
get_callbacks()->on_pq_created(&(*it), num);
num++;
}
deck_file.close(); deck_file.close();
if (upgraded)
deck :: write();
} }
void deck :: write() void deck :: write()
{ {
std::list<Queue>::iterator it; std::list<Queue>::iterator it;
//std::list<sort_info>::iterator st;
//std::list<sort_info> sort_order;
deck_file.open(OPEN_WRITE); if (!deck_file.open(OPEN_WRITE))
return;
/* Save library playqueue */ deck_file << queue_deck.size() << std :: endl;
//sort_order = library_playqueue.get_sort_order(); for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
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++) {
it->write(deck_file); it->write(deck_file);
deck_file << std::endl; deck_file << std::endl;
} }
deck_file.close(); deck_file.close();
} }
Queue *deck :: create(bool random) Queue *deck :: create(bool random)
{ {
Queue *pq; unsigned int flag = (random ? Q_RANDOM : 0);
playqueue_deck.push_back(Queue(Q_ENABLED)); queue_deck.push_back(Queue(Q_ENABLED | flag));
pq = &playqueue_deck.back(); return &queue_deck.back();
if (random == true)
pq->set_flag(Q_RANDOM);
get_callbacks()->on_pq_created(pq, playqueue_deck.size() - 1);
return pq;
} }
void deck :: remove(unsigned int id) static void _destroy(std::list<Queue>::iterator &it)
{ {
std::list<Queue>::iterator it = playqueue_deck.begin();
for (unsigned int i = 0; i < id; i++)
it++;
get_callbacks()->on_pq_removed(&(*it)); get_callbacks()->on_pq_removed(&(*it));
playqueue_deck.erase(it); queue_deck.erase(it);
write(); deck :: write();
} }
Queue *deck :: get(unsigned int id) void deck :: destroy(Queue *queue)
{ {
std::list<Queue>::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<Queue>::iterator it_old = playqueue_deck.begin();
std::list<Queue>::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<Queue>::iterator it_old = playqueue_deck.begin();
std::list<Queue>::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<Queue>::iterator it; std::list<Queue>::iterator it;
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
if (it->has_flag(Q_ENABLED)) { if (&(*it) == queue) {
if (it->size() == 0) { _destroy(it);
playqueue_deck.erase(it); return;
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;
} }
} }
return library :: get_queue()->next();
} }
#ifdef CONFIG_TEST void deck :: move(Queue *queue, unsigned int new_pos)
static void no_op() {}
void deck :: reset()
{ {
get_callbacks()->on_queue_changed = no_op; unsigned int old_pos = deck :: index(queue);
playqueue_deck.clear(); std::list<Queue>::iterator it_old = queue_deck.begin();
//library_playqueue.reset(); std::list<Queue>::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; unsigned int i = 0;
std::list<Queue>::iterator it; std::list<Queue>::iterator it;
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
print("deck[%u] = Queue { size = %u, flags = %u }\n", if (&(*it) == queue)
i, it->size()); return i;
i++; i++;
} }
return queue_deck.size();
}
Track *deck :: next()
{
Track *track = NULL;
std::list<Queue>::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<Queue> &deck :: get_queues()
{
return queue_deck;
}
Queue *deck :: get_queue()
{
return &recent_queue;
} }
#endif /* CONFIG_TEST */

View File

@ -1,153 +1,154 @@
/* /*
* Copyright 2013 (c) Anna Schumaker. * Copyright 2013 (c) Anna Schumaker.
*/ */
#include <callback.h>
#include <deck.h>
int main(int argc, char **argv)
{
return 0;
}
/*#include <deck.h>
#include <idle.h>
#include <library.h> #include <library.h>
#include <print.h> #include "test.h"
void test_add_playlist(unsigned int size) static Queue *Q_NULL = NULL;
{ static Track *TRACK_NULL = NULL;
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: ");
pqueue = deck :: get(3); static void test_init()
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()
{ {
print("Test 3:\n"); unsigned int val;
deck :: move(4, 0); File f("deck", 0);
deck :: print_info(); std::list<Queue>::iterator it;
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");
}*/
/* Test the next() function for playlists */ deck :: init();
/*void test_4() test_equal(library :: get_queue()->has_flag(Q_RANDOM), true);
{ test_equal(deck :: get_queues().size(), (size_t)2);
print("Test 4:\n");
deck :: get(0)->unset_flag(PQ_ENABLED); it = deck :: get_queues().begin();
deck :: get(1)->unset_flag(PQ_ENABLED); test_equal(it->size(), (unsigned)4);
deck :: get(4)->unset_flag(PQ_ENABLED); for (unsigned int i = 0; i < 4; i++)
test_equal((*it)[i]->id, i);
for (unsigned int i = 0; i < 40; i++) { it++;
print("Playing id: %u\n", deck :: next()); test_equal(it->size(), (unsigned)5);
if (i == 11 || i == 25) for (unsigned int i = 0; i < 5; i++)
deck :: print_info(); 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(); f.close();
print("\n"); }
}*/
/* Test load / save functions */ static unsigned int n = 0;
/*void test_5() static void on_queue_removed(Queue *queue)
{ {
print("Test 5:\n"); n++;
}
deck :: get(1)->set_flag(PQ_ENABLED); static void test_create_mv_destroy()
deck :: get(2)->set_flag(PQ_ENABLED); {
deck :: get(2)->set_flag(PQ_RANDOM); Queue *q1, *q2;
print("Saving playqueue deck\n"); q1 = deck :: create(true);
deck :: write(); 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"); q2 = deck :: create(false);
deck :: reset(); test_not_equal(q2, Q_NULL);
deck :: print_info(); 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 :: move(q1, 3);
deck :: init(); 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<Queue>::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) int main(int argc, char **argv)
{ {
test :: cp_data_dir();
tagdb :: init();
library :: 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; return 0;
}*/ }