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:
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<Queue> &deck :: get_queues();
Return the list of queues to the caller.

View File

@ -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 */

View File

@ -5,27 +5,24 @@
#define OCARINA_DECK_H
#include <queue.h>
#include <list>
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<Queue> &get_queues();
Queue *get_queue();
};

View File

@ -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,

View File

@ -3,193 +3,183 @@
*/
#include <callback.h>
#include <deck.h>
#include <error.h>
#include <file.h>
#include <print.h>
#include <library.h>
#include <list>
static std::list<Queue> 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> 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<Queue>::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<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 */
//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<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));
playqueue_deck.erase(it);
write();
queue_deck.erase(it);
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;
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<Queue>::iterator it_old = queue_deck.begin();
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;
std::list<Queue>::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<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.
*/
int main(int argc, char **argv)
{
return 0;
}
/*#include <deck.h>
#include <idle.h>
#include <callback.h>
#include <deck.h>
#include <library.h>
#include <print.h>
#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<Queue>::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<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)
{
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;
}*/
}