Merge branch 'Ocarina-next'
Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net> Conflicts: .gitignore Sconstruct gui/main.cpp include/deck.h include/playqueue.h lib/deck.cpp lib/playqueue.cpp
This commit is contained in:
commit
40a99f7eda
|
@ -1,10 +1,14 @@
|
|||
*.o
|
||||
*.sw*
|
||||
*.run
|
||||
*.test
|
||||
*.out
|
||||
*.glade~
|
||||
share/ocarina/#*
|
||||
bin/
|
||||
.sconsign.dblite
|
||||
*.patch
|
||||
*.tar.gz
|
||||
tests/*-core
|
||||
tests/*-lib
|
||||
*.gcov
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
|
50
Sconstruct
50
Sconstruct
|
@ -2,8 +2,11 @@
|
|||
import os
|
||||
|
||||
# Configuration variables
|
||||
CONFIG_VERSION = 6.0
|
||||
CONFIG_DEBUG = False
|
||||
CONFIG_VERSION = 6.1
|
||||
CONFIG_DEBUG = True
|
||||
CONFIG_TEST_VALGRIND = False
|
||||
CONFIG_TEST_COVERAGE = False
|
||||
CONFIG_TEST_CPPCHECK = False
|
||||
|
||||
|
||||
# Set up default environment
|
||||
|
@ -11,28 +14,43 @@ CONFIG_CCFLAGS = [ "-O2" ]
|
|||
if CONFIG_DEBUG == True:
|
||||
CONFIG_CCFLAGS = [ "-Wall", "-Werror", "-g", "-DCONFIG_DEBUG" ]
|
||||
|
||||
env = Environment( CCFLAGS = CONFIG_CCFLAGS )
|
||||
env.Append(CPPPATH = os.path.abspath("include"))
|
||||
env.Append(CXXCOMSTR = "C++ $TARGET")
|
||||
env.Append(LINKCOMSTR = "Linking $TARGET")
|
||||
class OEnvironment(Environment):
|
||||
Debug = False
|
||||
Version = 0
|
||||
Valgrind = False
|
||||
Coverage = False
|
||||
CppCheck = False
|
||||
|
||||
def __init__(self, CCFLAGS = CONFIG_CCFLAGS):
|
||||
Environment.__init__(self, CCFLAGS = CCFLAGS)
|
||||
self.Append(CPPPATH = os.path.abspath("include"))
|
||||
self.Append(CXXCOMSTR = "C++ $TARGET")
|
||||
self.Append(LINKCOMSTR = "Linking $TARGET")
|
||||
self.Debug = CONFIG_DEBUG
|
||||
self.Version = CONFIG_VERSION
|
||||
self.Valgrind = CONFIG_TEST_VALGRIND
|
||||
self.Coverage = CONFIG_TEST_COVERAGE
|
||||
self.CppCheck = CONFIG_TEST_CPPCHECK
|
||||
|
||||
def use_package(name):
|
||||
env.ParseConfig("pkg-config --cflags --libs %s" % name)
|
||||
def UsePackage(self, name):
|
||||
self.ParseConfig("pkg-config --cflags --libs %s" % name)
|
||||
|
||||
Export("env", "use_package", "CONFIG_DEBUG", "CONFIG_VERSION")
|
||||
env = OEnvironment()
|
||||
test_env = OEnvironment( CONFIG_CCFLAGS + [ "-DCONFIG_TEST" ] )
|
||||
Export("env", "test_env")
|
||||
|
||||
|
||||
include = SConscript("include/Sconscript")
|
||||
|
||||
lib = SConscript("lib/Sconscript")
|
||||
Export("lib")
|
||||
|
||||
tests = SConscript("tests/Sconscript")
|
||||
|
||||
core = SConscript("core/Sconscript")
|
||||
gui = SConscript("gui/Sconscript")
|
||||
|
||||
ocarina = env.Program("bin/ocarina", lib + gui)
|
||||
tests = SConscript("tests/Sconscript")
|
||||
Clean(tests, Glob("*.gcov"))
|
||||
Clean(tests, Glob("tests/*.gcda"))
|
||||
Clean(tests, Glob("tests/*.gcno"))
|
||||
|
||||
|
||||
ocarina = env.Program("bin/ocarina", core + gui)
|
||||
Default(ocarina)
|
||||
Clean(ocarina, "bin/")
|
||||
|
||||
|
|
21
TODO
21
TODO
|
@ -61,14 +61,8 @@ Future work:
|
|||
the library? Perhaps create a mirror group?
|
||||
|
||||
- Extra testing ideas:
|
||||
- Run tests through valgrind to help find memory leaks
|
||||
- Some way to enable / disable tests during development
|
||||
- Run tests based on dependencies
|
||||
- Fix tests that will only work on my computer
|
||||
- Double check that all inputs and outputs are tested
|
||||
- Simple testing library?
|
||||
- Stop tests on failure
|
||||
- Test callbacks
|
||||
- Create a default main() that deletes data directory
|
||||
|
||||
- Preferences:
|
||||
- Set default sort
|
||||
|
@ -86,13 +80,12 @@ Future work:
|
|||
Calculate value after first playback?
|
||||
Store in library :: Track structure
|
||||
|
||||
- Autosave databases
|
||||
Artist, album, genere and library path don't change often so
|
||||
saving on every add / remove won't be a huge performance hit
|
||||
and may actually be more efficient in the long run.
|
||||
|
||||
- Playqueue and database inherit from common class
|
||||
- "About" dialog
|
||||
- Investigate "Bulk insert" callbacks for performance
|
||||
- Initialize PQs with multiple flags
|
||||
- Prefill Song structures in each library :: Track
|
||||
- Use "friend" class for valid and id flags of a DatabaseEntry
|
||||
- Doxygen support for documentation?
|
||||
- Jenkins
|
||||
- Run tests
|
||||
- Show gcov graph
|
||||
- Run cppcheck
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/python
|
||||
Import("env")
|
||||
|
||||
env.UsePackage("gstreamer-1.0")
|
||||
env.UsePackage("taglib")
|
||||
|
||||
res = Glob("*.cpp")
|
||||
Return("res")
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/driver.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static bool _pause_enabled = false;
|
||||
static unsigned int _pause_count = 0;
|
||||
|
||||
static Track *cur_track = NULL;
|
||||
static File f_cur_track("cur_track", 0);
|
||||
|
||||
|
||||
static void save_state()
|
||||
{
|
||||
f_cur_track.open(OPEN_WRITE);
|
||||
f_cur_track << cur_track->id << std::endl;
|
||||
f_cur_track.close();
|
||||
}
|
||||
|
||||
static void _load_track(Track *track, bool start_playback)
|
||||
{
|
||||
cur_track = track;
|
||||
if (!track)
|
||||
return;
|
||||
|
||||
driver :: get_driver()->load(track->path());
|
||||
get_callbacks()->on_track_loaded(track);
|
||||
if (start_playback)
|
||||
audio :: play();
|
||||
else
|
||||
audio :: pause();
|
||||
save_state();
|
||||
}
|
||||
|
||||
static inline void _load_track_default(Track *track)
|
||||
{
|
||||
_load_track(track, driver :: get_driver()->is_playing());
|
||||
}
|
||||
|
||||
static bool continue_playback()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (_pause_enabled) {
|
||||
if (_pause_count == 0) {
|
||||
ret = false;
|
||||
_pause_enabled = false;
|
||||
} else
|
||||
_pause_count--;
|
||||
get_callbacks()->on_pause_count_changed(_pause_enabled, _pause_count);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void on_eos()
|
||||
{
|
||||
if (cur_track) {
|
||||
cur_track->played();
|
||||
library :: get_queue()->updated(cur_track);
|
||||
}
|
||||
|
||||
_load_track(deck :: next(), continue_playback());
|
||||
}
|
||||
|
||||
void audio :: init(int *argc, char ***argv)
|
||||
{
|
||||
unsigned int id;
|
||||
|
||||
driver :: get_driver()->init(argc, argv, on_eos, audio :: next);
|
||||
if (f_cur_track.exists()) {
|
||||
f_cur_track.open(OPEN_READ);
|
||||
f_cur_track >> id;
|
||||
f_cur_track.close();
|
||||
audio :: load_track(tagdb :: lookup(id));
|
||||
}
|
||||
}
|
||||
|
||||
void audio :: play()
|
||||
{
|
||||
if (!cur_track)
|
||||
return;
|
||||
if (driver :: get_driver()->play())
|
||||
get_callbacks()->on_play();
|
||||
}
|
||||
|
||||
void audio :: pause()
|
||||
{
|
||||
if (!cur_track)
|
||||
return;
|
||||
if (driver :: get_driver()->pause())
|
||||
get_callbacks()->on_pause();
|
||||
}
|
||||
|
||||
void audio :: seek_to(long pos)
|
||||
{
|
||||
if (!cur_track)
|
||||
return;
|
||||
driver :: get_driver()->seek_to(pos);
|
||||
}
|
||||
|
||||
void audio :: stop()
|
||||
{
|
||||
pause();
|
||||
seek_to(0);
|
||||
}
|
||||
|
||||
long audio :: position()
|
||||
{
|
||||
if (!cur_track)
|
||||
return 0;
|
||||
return driver :: get_driver()->position();
|
||||
}
|
||||
|
||||
long audio :: duration()
|
||||
{
|
||||
if (!cur_track)
|
||||
return 0;
|
||||
return driver :: get_driver()->duration();
|
||||
}
|
||||
|
||||
std::string audio :: position_str()
|
||||
{
|
||||
std::stringstream ss;
|
||||
long cur = position() / O_SECOND;
|
||||
unsigned int minutes = cur / 60;
|
||||
unsigned int seconds = cur % 60;
|
||||
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void audio :: next()
|
||||
{
|
||||
_load_track_default(deck :: next());
|
||||
}
|
||||
|
||||
void audio :: prev()
|
||||
{
|
||||
_load_track_default(deck :: prev());
|
||||
}
|
||||
|
||||
void audio :: load_track(Track *track)
|
||||
{
|
||||
if (!track || track == cur_track)
|
||||
return;
|
||||
|
||||
_load_track(track, driver :: get_driver()->is_playing());
|
||||
deck :: get_queue()->add(cur_track);
|
||||
}
|
||||
|
||||
Track *audio :: current_track()
|
||||
{
|
||||
return cur_track;
|
||||
}
|
||||
|
||||
void audio :: pause_after(bool enabled, unsigned int n)
|
||||
{
|
||||
if (n > _pause_count)
|
||||
enabled = true;
|
||||
|
||||
_pause_enabled = enabled;
|
||||
_pause_count = n;
|
||||
get_callbacks()->on_pause_count_changed(enabled, n);
|
||||
}
|
||||
|
||||
bool audio :: pause_enabled()
|
||||
{
|
||||
return _pause_enabled;
|
||||
}
|
||||
|
||||
unsigned int audio :: pause_count()
|
||||
{
|
||||
return _pause_count;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/callback.h>
|
||||
|
||||
|
||||
static void no_op() {}
|
||||
static void no_op(bool, unsigned int) {}
|
||||
static void no_op(Queue *, unsigned int) {}
|
||||
static void no_op(Queue *) {}
|
||||
static void no_op(Track *) {}
|
||||
|
||||
|
||||
static struct Callbacks callbacks = {
|
||||
.on_play = no_op,
|
||||
.on_pause = no_op,
|
||||
.on_track_loaded = no_op,
|
||||
.on_pause_count_changed = no_op,
|
||||
|
||||
.on_pq_removed = no_op,
|
||||
|
||||
.on_queue_track_add = no_op,
|
||||
.on_queue_track_del = no_op,
|
||||
.on_queue_track_changed = no_op,
|
||||
};
|
||||
|
||||
|
||||
struct Callbacks *get_callbacks()
|
||||
{
|
||||
return &callbacks;
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <database.h>
|
||||
#include <core/database.h>
|
||||
|
||||
|
||||
DatabaseEntry :: DatabaseEntry()
|
||||
: valid(false), id(0)
|
||||
: id(0)
|
||||
{
|
||||
}
|
||||
|
||||
DatabaseEntry :: ~DatabaseEntry() {}
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/file.h>
|
||||
#include <core/library.h>
|
||||
|
||||
|
||||
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<TempQueue> queue_deck;
|
||||
static RecentQueue recent_queue;
|
||||
static File deck_file("deck", 1);
|
||||
|
||||
|
||||
TempQueue :: TempQueue() {}
|
||||
TempQueue :: TempQueue(bool random)
|
||||
: Queue(Q_ENABLED | (random ? Q_RANDOM : 0)) {}
|
||||
|
||||
void TempQueue :: set_flag(queue_flags flag)
|
||||
{
|
||||
Queue :: set_flag(flag);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
void TempQueue :: unset_flag(queue_flags flag)
|
||||
{
|
||||
Queue :: unset_flag(flag);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
unsigned int TempQueue :: add(Track *track)
|
||||
{
|
||||
unsigned int res = Queue :: add(track);
|
||||
deck :: write();
|
||||
return res;
|
||||
}
|
||||
|
||||
void TempQueue :: del(Track *track)
|
||||
{
|
||||
Queue :: del(track);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
void TempQueue :: del(unsigned int id)
|
||||
{
|
||||
Queue :: del(id);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
void TempQueue :: sort(sort_t field, bool ascending)
|
||||
{
|
||||
Queue :: sort(field, ascending);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
unsigned int num;
|
||||
bool upgraded = false;
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
if (!deck_file.open(OPEN_READ))
|
||||
return;
|
||||
|
||||
if (deck_file.get_version() == 0) {
|
||||
upgrade_v0();
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
deck_file >> num;
|
||||
queue_deck.resize(num);
|
||||
|
||||
for (it = queue_deck.begin(); it != queue_deck.end(); it++)
|
||||
it->read(deck_file);
|
||||
deck_file.close();
|
||||
|
||||
if (upgraded)
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
void deck :: write()
|
||||
{
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
if (!deck_file.open(OPEN_WRITE))
|
||||
return;
|
||||
|
||||
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_deck.push_back(TempQueue(random));
|
||||
return &queue_deck.back();
|
||||
}
|
||||
|
||||
static void _destroy(std::list<TempQueue>::iterator &it)
|
||||
{
|
||||
get_callbacks()->on_pq_removed(&(*it));
|
||||
queue_deck.erase(it);
|
||||
deck :: write();
|
||||
}
|
||||
|
||||
void deck :: destroy(Queue *queue)
|
||||
{
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
|
||||
if (&(*it) == queue) {
|
||||
_destroy(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deck :: move(Queue *queue, unsigned int new_pos)
|
||||
{
|
||||
unsigned int old_pos = deck :: index(queue);
|
||||
std::list<TempQueue>::iterator it_old = queue_deck.begin();
|
||||
std::list<TempQueue>::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);
|
||||
write();
|
||||
}
|
||||
|
||||
unsigned int deck :: index(Queue *queue)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
|
||||
if (&(*it) == queue)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return queue_deck.size();
|
||||
}
|
||||
|
||||
Queue *deck :: get(unsigned int index)
|
||||
{
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
for (it = queue_deck.begin(); it != queue_deck.end(); it++) {
|
||||
if (index == 0)
|
||||
return &(*it);
|
||||
index--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Track *deck :: next()
|
||||
{
|
||||
Track *track = NULL;
|
||||
std::list<TempQueue>::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);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!track)
|
||||
track = library :: get_queue()->next();
|
||||
if (track)
|
||||
recent_queue.add(track);
|
||||
return track;
|
||||
}
|
||||
|
||||
Track *deck :: prev()
|
||||
{
|
||||
return recent_queue.next();
|
||||
}
|
||||
|
||||
std::list<TempQueue> &deck :: get_queues()
|
||||
{
|
||||
return queue_deck;
|
||||
}
|
||||
|
||||
Queue *deck :: get_queue()
|
||||
{
|
||||
return &recent_queue;
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/driver.h>
|
||||
|
||||
|
||||
Driver :: Driver() {}
|
||||
Driver :: ~Driver() {}
|
||||
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
|
||||
TestDriver :: TestDriver() : playing(false), cur_pos(0), cur_duration(0) {}
|
||||
TestDriver :: ~TestDriver() {}
|
||||
|
||||
void TestDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)())
|
||||
{ on_eos = eos_cb; on_error = error_cb; }
|
||||
void TestDriver :: load(const std::string &file)
|
||||
{ cur_file = file; playing = false; cur_pos = 0; }
|
||||
bool TestDriver :: play() { playing = true; return true; }
|
||||
bool TestDriver :: pause() { playing = false; return true; }
|
||||
bool TestDriver :: is_playing() { return playing; }
|
||||
|
||||
void TestDriver :: seek_to(long pos) { cur_pos = pos; }
|
||||
long TestDriver :: position() { return cur_pos; }
|
||||
long TestDriver :: duration() { return cur_duration; }
|
||||
|
||||
void TestDriver :: eos() { on_eos(); }
|
||||
void TestDriver :: error() { on_error(); }
|
||||
|
||||
#else /* CONFIG_TEST */
|
||||
|
||||
static gboolean on_gst_message(GstBus *bus, GstMessage *message, gpointer data)
|
||||
{
|
||||
GSTDriver *driver = (GSTDriver *)data;
|
||||
driver->on_message(message);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void parse_gst_error(GstMessage *error, const std::string filepath)
|
||||
{
|
||||
GError *err;
|
||||
gchar *debug;
|
||||
|
||||
gst_message_parse_error(error, &err, &debug);
|
||||
g_print("Error playing file: %s\n", filepath.c_str());
|
||||
g_print("Error: %s\n", err->message);
|
||||
g_error_free(err);
|
||||
g_free(debug);
|
||||
}
|
||||
|
||||
GSTDriver :: GSTDriver() {}
|
||||
|
||||
GSTDriver :: ~GSTDriver()
|
||||
{
|
||||
change_state(GST_STATE_NULL);
|
||||
gst_deinit();
|
||||
}
|
||||
|
||||
bool GSTDriver :: change_state(GstState state)
|
||||
{
|
||||
GstStateChangeReturn ret = gst_element_set_state(player, state);
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_SUCCESS:
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GSTDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)())
|
||||
{
|
||||
GstBus *bus;
|
||||
|
||||
on_eos = eos_cb;
|
||||
on_error = error_cb;
|
||||
|
||||
gst_init(argc, argv);
|
||||
player = gst_element_factory_make("playbin", "ocarina_player");
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(player));
|
||||
gst_bus_add_watch(bus, on_gst_message, this);
|
||||
}
|
||||
|
||||
void GSTDriver :: load(const std::string &filepath)
|
||||
{
|
||||
gchar *uri;
|
||||
|
||||
cur_file = filepath;
|
||||
change_state(GST_STATE_NULL);
|
||||
uri = gst_filename_to_uri(filepath.c_str(), NULL);
|
||||
g_object_set(G_OBJECT(player), "uri", uri, NULL);
|
||||
g_free(uri);
|
||||
}
|
||||
|
||||
bool GSTDriver :: play()
|
||||
{
|
||||
return change_state(GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
bool GSTDriver :: pause()
|
||||
{
|
||||
return change_state(GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
bool GSTDriver :: is_playing()
|
||||
{
|
||||
GstState state;
|
||||
gst_element_get_state(player, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||
return state == GST_STATE_PLAYING;
|
||||
}
|
||||
|
||||
void GSTDriver :: seek_to(long pos)
|
||||
{
|
||||
gst_element_seek_simple(player, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, pos);
|
||||
}
|
||||
|
||||
long GSTDriver :: position()
|
||||
{
|
||||
long position;
|
||||
if (gst_element_query_position(player, GST_FORMAT_TIME, &position))
|
||||
return position;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long GSTDriver :: duration()
|
||||
{
|
||||
long duration;
|
||||
|
||||
if (gst_element_query_duration(player, GST_FORMAT_TIME, &duration))
|
||||
return duration;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GSTDriver :: on_message(GstMessage *message)
|
||||
{
|
||||
switch (GST_MESSAGE_TYPE(message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
parse_gst_error(message, cur_file);
|
||||
on_error();
|
||||
break;
|
||||
case GST_MESSAGE_EOS:
|
||||
on_eos();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
static TestDriver cur_driver;
|
||||
#else /* CONFIG_TEST */
|
||||
static GSTDriver cur_driver;
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
|
||||
Driver *driver :: get_driver()
|
||||
{
|
||||
return &cur_driver;
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/file.h>
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
const std::string OCARINA_DIR = "ocarina-test";
|
||||
#elif CONFIG_DEBUG
|
||||
const std::string OCARINA_DIR = "ocarina-debug";
|
||||
#else
|
||||
const std::string OCARINA_DIR = "ocarina";
|
||||
#endif
|
||||
|
||||
File :: File(const std::string &name, unsigned int vers)
|
||||
: mode(NOT_OPEN), filename(name), version(vers), prev_version(0)
|
||||
{
|
||||
}
|
||||
|
||||
File :: ~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
const std::string File :: find_dir()
|
||||
{
|
||||
std::string res(g_get_user_data_dir());
|
||||
res += "/" + OCARINA_DIR;
|
||||
return res;
|
||||
}
|
||||
|
||||
const std::string File :: get_filepath()
|
||||
{
|
||||
std::string res = "";
|
||||
|
||||
if (filename != "") {
|
||||
res = find_dir();
|
||||
res += "/" + filename;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const unsigned int File :: get_version()
|
||||
{
|
||||
if (mode == OPEN_READ)
|
||||
return prev_version;
|
||||
return version;
|
||||
}
|
||||
|
||||
bool File :: exists()
|
||||
{
|
||||
return g_file_test(get_filepath().c_str(), G_FILE_TEST_EXISTS);
|
||||
}
|
||||
|
||||
bool File :: open_read()
|
||||
{
|
||||
if (!exists())
|
||||
return false;
|
||||
|
||||
std::fstream::open(get_filepath().c_str(), std::fstream::in);
|
||||
if (std::fstream::fail())
|
||||
return false;
|
||||
|
||||
mode = OPEN_READ;
|
||||
std::fstream::operator>>(prev_version);
|
||||
getline();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File :: open_write()
|
||||
{
|
||||
if (g_mkdir_with_parents(find_dir().c_str(), 0755) != 0)
|
||||
return false;
|
||||
|
||||
std::fstream::open(get_filepath().c_str(), std::fstream::out);
|
||||
if (std::fstream::fail())
|
||||
return false;
|
||||
|
||||
mode = OPEN_WRITE;
|
||||
std::fstream::operator<<(version) << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File :: open(OpenMode m)
|
||||
{
|
||||
if ((filename == "") || (m == NOT_OPEN) || (mode != NOT_OPEN))
|
||||
return false;
|
||||
|
||||
else if (m == OPEN_READ)
|
||||
return open_read();
|
||||
else /* m == OPEN_WRITE */
|
||||
return open_write();
|
||||
}
|
||||
|
||||
void File :: close()
|
||||
{
|
||||
if (mode != NOT_OPEN)
|
||||
std::fstream::close();
|
||||
mode = NOT_OPEN;
|
||||
}
|
||||
|
||||
std::string File :: getline()
|
||||
{
|
||||
char c;
|
||||
std::string res;
|
||||
|
||||
/* Ignore leading whitespace */
|
||||
while (peek() == ' ')
|
||||
read(&c, 1);
|
||||
|
||||
std::getline(*static_cast<std::fstream *>(this), res);
|
||||
return res;
|
||||
}
|
|
@ -2,9 +2,8 @@
|
|||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
|
||||
#include <filter.h>
|
||||
#include <index.h>
|
||||
#include <print.h>
|
||||
#include <core/filter.h>
|
||||
#include <core/index.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
@ -93,7 +92,7 @@ std::string filter :: add(const std::string &text, unsigned int track_id)
|
|||
|
||||
static void find_intersection(std::string &text, std::set<unsigned int> &res)
|
||||
{
|
||||
Index::iterator it = filter_index.find(text);
|
||||
IndexEntry *it = filter_index.find(text);
|
||||
std::set<unsigned int> tmp;
|
||||
|
||||
set_intersection(it->values.begin(), it->values.end(),
|
||||
|
@ -106,17 +105,17 @@ void filter :: search(const std::string &text, std::set<unsigned int> &res)
|
|||
{
|
||||
std::list<std::string> parsed;
|
||||
std::list<std::string>::iterator it;
|
||||
IndexEntry *found;
|
||||
|
||||
parse_text(text, parsed);
|
||||
if (parsed.size() == 0)
|
||||
return;
|
||||
|
||||
it = parsed.begin();
|
||||
try {
|
||||
res = filter_index.find(*it)->values;
|
||||
} catch (...) {
|
||||
found = filter_index.find(*it);
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
res = found->values;
|
||||
|
||||
for (it++; it != parsed.end(); it++)
|
||||
find_intersection(*it, res);
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <idle.h>
|
||||
#include <core/idle.h>
|
||||
|
||||
#include <queue>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <index.h>
|
||||
#include <core/index.h>
|
||||
|
||||
|
||||
IndexEntry :: IndexEntry() {}
|
||||
|
@ -9,7 +9,7 @@ IndexEntry :: IndexEntry(const std::string &k)
|
|||
: key(k)
|
||||
{}
|
||||
|
||||
const std::string IndexEntry :: primary_key()
|
||||
const std::string IndexEntry :: primary_key() const
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ Index :: Index(const std::string &filepath, bool autosave)
|
|||
|
||||
void Index :: insert(const std::string &key, unsigned int val)
|
||||
{
|
||||
iterator it = find(key);
|
||||
if (it == end())
|
||||
it = at(Database :: insert(IndexEntry(key)));
|
||||
IndexEntry *it = find(key);
|
||||
if (it == NULL)
|
||||
it = Database :: insert(IndexEntry(key));
|
||||
|
||||
it->insert(val);
|
||||
autosave();
|
||||
|
@ -61,9 +61,9 @@ void Index :: insert(const std::string &key, unsigned int val)
|
|||
|
||||
void Index :: remove(const std::string &key, unsigned int val)
|
||||
{
|
||||
iterator it = find(key);
|
||||
IndexEntry *it = find(key);
|
||||
|
||||
if (it == end())
|
||||
if (it == NULL)
|
||||
return;
|
||||
|
||||
it->remove(val);
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/library.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
class LibraryQueue : public Queue {
|
||||
private:
|
||||
File f;
|
||||
|
||||
public:
|
||||
|
||||
LibraryQueue() : Queue(Q_ENABLED | Q_REPEAT), f("library.q", 0)
|
||||
{
|
||||
Queue :: sort(SORT_ARTIST, true);
|
||||
Queue :: sort(SORT_YEAR, false);
|
||||
Queue :: sort(SORT_TRACK, false);
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
std::vector<struct sort_info>::iterator it;
|
||||
|
||||
f.open(OPEN_WRITE);
|
||||
f << _flags << " " << _sort_order.size();
|
||||
for (it = _sort_order.begin(); it != _sort_order.end(); it++)
|
||||
f << " " << it->field << " " << it->ascending;
|
||||
f << std::endl;
|
||||
f.close();
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
unsigned int field;
|
||||
bool ascending;
|
||||
unsigned int n;
|
||||
|
||||
if (!f.open(OPEN_READ))
|
||||
return;
|
||||
|
||||
f >> _flags >> n;
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
f >> field >> ascending;
|
||||
Queue :: sort((sort_t)field, (i == 0) ? true : false);
|
||||
if (ascending == false)
|
||||
Queue :: sort((sort_t)field, false);
|
||||
}
|
||||
}
|
||||
|
||||
void set_flag(queue_flags f) { Queue :: set_flag(f); save(); }
|
||||
void unset_flag(queue_flags f) { Queue :: unset_flag(f); save(); }
|
||||
|
||||
void sort(sort_t field, bool ascending)
|
||||
{
|
||||
Queue :: sort(field, ascending);
|
||||
save();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
static LibraryQueue library_q;
|
||||
|
||||
struct scan_info {
|
||||
Library *library;
|
||||
std :: string path;
|
||||
};
|
||||
|
||||
static void scan_path(struct scan_info &);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Scanning functions are here
|
||||
*/
|
||||
|
||||
static void process_path(Library *library, const std :: string &dir,
|
||||
const std :: string &name)
|
||||
{
|
||||
struct scan_info scan = {
|
||||
.library = library,
|
||||
.path = dir + "/" + name,
|
||||
};
|
||||
|
||||
if (g_file_test(scan.path.c_str(), G_FILE_TEST_IS_DIR) == true)
|
||||
idle :: schedule (scan_path, scan);
|
||||
else {
|
||||
Track *track = tagdb :: add_track(scan.path, library);
|
||||
if (track)
|
||||
library_q.add(track);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_path(struct scan_info &scan)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *name;
|
||||
|
||||
dir = g_dir_open(scan.path.c_str(), 0, NULL);
|
||||
if (dir == NULL)
|
||||
return;
|
||||
|
||||
name = g_dir_read_name(dir);
|
||||
while (name != NULL) {
|
||||
process_path(scan.library, scan.path, name);
|
||||
name = g_dir_read_name(dir);
|
||||
}
|
||||
|
||||
tagdb :: commit();
|
||||
}
|
||||
|
||||
static void validate_library(Library *&library)
|
||||
{
|
||||
Track *track;
|
||||
Database<Track>::iterator it;
|
||||
Database<Track> *db = &tagdb :: get_track_db();
|
||||
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it)) {
|
||||
track = *it;
|
||||
if (track->library != library)
|
||||
continue;
|
||||
|
||||
if (g_file_test(track->path().c_str(), G_FILE_TEST_EXISTS) == false) {
|
||||
library_q.del(track);
|
||||
tagdb :: remove_track(track->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* External API begins here
|
||||
*/
|
||||
|
||||
void library :: init()
|
||||
{
|
||||
Database<Track>::iterator it;
|
||||
Database<Track> *db = &tagdb :: get_track_db();
|
||||
|
||||
library_q.load();
|
||||
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it)) {
|
||||
if ((*it)->library->enabled)
|
||||
library_q.add(*it);
|
||||
}
|
||||
}
|
||||
|
||||
Library *library :: add(const std::string &dir)
|
||||
{
|
||||
Library *library = NULL;
|
||||
|
||||
if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false)
|
||||
return library;
|
||||
|
||||
library = tagdb :: add_library(dir);
|
||||
if (library)
|
||||
update(library);
|
||||
return library;
|
||||
}
|
||||
|
||||
void library :: remove(Library *library)
|
||||
{
|
||||
if (library) {
|
||||
set_enabled(library, false);
|
||||
tagdb :: remove_library(library->id);
|
||||
}
|
||||
}
|
||||
|
||||
void library :: update(Library *library)
|
||||
{
|
||||
struct scan_info scan = {
|
||||
.library = library,
|
||||
};
|
||||
|
||||
if (library) {
|
||||
scan.path = library->root_path;
|
||||
idle :: schedule(validate_library, library);
|
||||
idle :: schedule(scan_path, scan);
|
||||
}
|
||||
}
|
||||
|
||||
void library :: update_all()
|
||||
{
|
||||
Database<Library>::iterator it;
|
||||
Database<Library> *db = &tagdb :: get_library_db();
|
||||
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it))
|
||||
update(*it);
|
||||
}
|
||||
|
||||
void library :: set_enabled(Library *library, bool enabled)
|
||||
{
|
||||
Database<Track>::iterator it;
|
||||
Database<Track> *db = &(tagdb :: get_track_db());
|
||||
|
||||
if (!library || (library->enabled == enabled))
|
||||
return;
|
||||
|
||||
library->enabled = enabled;
|
||||
tagdb :: commit_library();
|
||||
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it)) {
|
||||
if ((*it)->library == library) {
|
||||
if (enabled)
|
||||
library_q.add(*it);
|
||||
else
|
||||
library_q.del(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Queue *library :: get_queue()
|
||||
{
|
||||
return &library_q;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/library.h>
|
||||
#include <core/playlist.h>
|
||||
|
||||
|
||||
class PlaylistQueue : public Queue {
|
||||
public:
|
||||
|
||||
PlaylistQueue() : Queue(Q_ENABLED | Q_REPEAT)
|
||||
{
|
||||
sort(SORT_ARTIST, true);
|
||||
sort(SORT_YEAR, false);
|
||||
sort(SORT_TRACK, false);
|
||||
set_flag(Q_NO_SORT);
|
||||
}
|
||||
|
||||
void fill(IndexEntry *ent)
|
||||
{
|
||||
std::set<unsigned int>::iterator it;
|
||||
|
||||
while (size() > 0)
|
||||
del((unsigned)0);
|
||||
|
||||
for (it = ent->values.begin(); it != ent->values.end(); it++)
|
||||
add(tagdb :: lookup(*it));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
static Index playlist_db("playlist.db", true);
|
||||
static PlaylistQueue playlist_q;
|
||||
static std::string cur_plist;
|
||||
|
||||
|
||||
void playlist :: init()
|
||||
{
|
||||
std::set<unsigned int>::iterator it;
|
||||
|
||||
playlist_db.load();
|
||||
|
||||
IndexEntry *ent = get_tracks("Banned");
|
||||
if (!ent)
|
||||
return;
|
||||
|
||||
for (it = ent->values.begin(); it != ent->values.end(); it++)
|
||||
library :: get_queue()->del(tagdb :: lookup(*it));
|
||||
}
|
||||
|
||||
bool playlist :: has(Track *track, const std::string &name)
|
||||
{
|
||||
std::set<unsigned int>::iterator it;
|
||||
IndexEntry *ent = playlist_db.find(name);
|
||||
|
||||
if (ent == NULL)
|
||||
return false;
|
||||
|
||||
it = ent->values.find(track->id);
|
||||
return it != ent->values.end();
|
||||
}
|
||||
|
||||
void playlist :: add(Track *track, const std::string &name)
|
||||
{
|
||||
if (!( (name == "Banned") || (name == "Favorites") ))
|
||||
return;
|
||||
|
||||
if (!has(track, name)) {
|
||||
playlist_db.insert(name, track->id);
|
||||
if (cur_plist == name)
|
||||
playlist_q.add(track);
|
||||
if (name == "Banned")
|
||||
library :: get_queue()->del(track);
|
||||
}
|
||||
}
|
||||
|
||||
void playlist :: del(Track *track, const std::string &name)
|
||||
{
|
||||
playlist_db.remove(name, track->id);
|
||||
if (cur_plist == name)
|
||||
playlist_q.del(track);
|
||||
if (name == "Banned")
|
||||
library :: get_queue()->add(track);
|
||||
|
||||
}
|
||||
|
||||
void playlist :: select(const std::string &name)
|
||||
{
|
||||
IndexEntry *ent;
|
||||
|
||||
if (cur_plist == name)
|
||||
return;
|
||||
|
||||
ent = playlist_db.find(name);
|
||||
if (ent == NULL)
|
||||
return;
|
||||
|
||||
playlist_q.fill(ent);
|
||||
cur_plist = name;
|
||||
}
|
||||
|
||||
IndexEntry *playlist :: get_tracks(const std::string &name)
|
||||
{
|
||||
return playlist_db.find(name);
|
||||
}
|
||||
|
||||
Queue *playlist :: get_queue()
|
||||
{
|
||||
return &playlist_q;
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/callback.h>
|
||||
#include <core/queue.h>
|
||||
#include <core/random.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#define O_MINUTES (60)
|
||||
#define O_HOURS (60 * O_MINUTES)
|
||||
#define O_DAYS (24 * O_HOURS)
|
||||
|
||||
Queue :: Queue()
|
||||
: _cur(-1), _flags(0), _length(0)
|
||||
{
|
||||
}
|
||||
|
||||
Queue :: Queue(unsigned int f)
|
||||
: _cur(-1), _flags(f & Q_FLAG_MASK), _length(0)
|
||||
{
|
||||
}
|
||||
|
||||
Queue :: ~Queue()
|
||||
{
|
||||
}
|
||||
|
||||
void Queue :: write(File &f)
|
||||
{
|
||||
f << _flags << " " << _tracks.size();
|
||||
for (unsigned int i = 0; i < _tracks.size(); i++)
|
||||
f << " " << _tracks[i]->id;
|
||||
}
|
||||
|
||||
void Queue :: read(File &f)
|
||||
{
|
||||
unsigned int n, id;
|
||||
f >> _flags >> n;
|
||||
_tracks.resize(n);
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
f >> id;
|
||||
_tracks[i] = tagdb :: lookup(id);
|
||||
_length += _tracks[i]->length;
|
||||
}
|
||||
}
|
||||
|
||||
void Queue :: set_flag(queue_flags f)
|
||||
{
|
||||
_flags |= f;
|
||||
}
|
||||
|
||||
void Queue :: unset_flag(queue_flags f)
|
||||
{
|
||||
_flags &= ~f;
|
||||
}
|
||||
|
||||
bool Queue :: has_flag(queue_flags f)
|
||||
{
|
||||
return (_flags & f) == (unsigned int)f;
|
||||
}
|
||||
|
||||
static bool track_less_than(Track *lhs, Track *rhs,
|
||||
std::vector<struct sort_info> &order)
|
||||
{
|
||||
int res;
|
||||
|
||||
for (unsigned int i = 0; i < order.size(); i++) {
|
||||
if (order[i].ascending == true)
|
||||
res = lhs->less_than(rhs, order[i].field);
|
||||
else
|
||||
res = rhs->less_than(lhs, order[i].field);
|
||||
if (res == 0)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
return res < 0;
|
||||
}
|
||||
|
||||
unsigned int Queue :: find_sorted_id(Track *rhs)
|
||||
{
|
||||
Track *lhs;
|
||||
unsigned int begin = 0, end = (_tracks.size() - 1), mid;
|
||||
|
||||
if (_tracks.size() == 0)
|
||||
return 0;
|
||||
|
||||
while (end > begin) {
|
||||
mid = begin + ((end - begin) / 2);
|
||||
lhs = _tracks[mid];
|
||||
if (track_less_than(lhs, rhs, _sort_order))
|
||||
begin = mid + 1;
|
||||
else {
|
||||
if (mid == begin)
|
||||
return begin;
|
||||
else
|
||||
end = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
lhs = _tracks[begin];
|
||||
if (track_less_than(lhs, rhs, _sort_order))
|
||||
return begin + 1;
|
||||
return begin;
|
||||
}
|
||||
|
||||
unsigned int Queue :: _add_at(Track *track, unsigned int pos)
|
||||
{
|
||||
_tracks.insert(_tracks.begin() + pos, track);
|
||||
_length += track->length;
|
||||
get_callbacks()->on_queue_track_add(this, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void Queue :: _del_at(Track *track, unsigned int pos)
|
||||
{
|
||||
_tracks.erase(_tracks.begin() + pos);
|
||||
_length -= track->length;
|
||||
get_callbacks()->on_queue_track_del(this, pos);
|
||||
}
|
||||
|
||||
unsigned int Queue :: add(Track *track)
|
||||
{
|
||||
unsigned int id = _tracks.size();
|
||||
if (_sort_order.size() > 0)
|
||||
id = find_sorted_id(track);
|
||||
return _add_at(track, id);
|
||||
}
|
||||
|
||||
void Queue :: del(Track *track)
|
||||
{
|
||||
for (unsigned int i = 0; i < _tracks.size(); i++) {
|
||||
if (_tracks[i] == track)
|
||||
_del_at(track, i);
|
||||
}
|
||||
}
|
||||
|
||||
void Queue :: del(unsigned int id)
|
||||
{
|
||||
_del_at(_tracks[id], id);
|
||||
}
|
||||
|
||||
void Queue :: updated(Track *track)
|
||||
{
|
||||
for (unsigned int i = 0; i < _tracks.size(); i++) {
|
||||
if (_tracks[i] == track)
|
||||
get_callbacks()->on_queue_track_changed(this, i);
|
||||
}
|
||||
}
|
||||
|
||||
Track *Queue :: next()
|
||||
{
|
||||
Track *res;
|
||||
|
||||
if (_tracks.size() == 0)
|
||||
return NULL;
|
||||
else if (_tracks.size() == 1)
|
||||
_cur = 0;
|
||||
else if (_flags & Q_RANDOM)
|
||||
_cur += random(1, _tracks.size() / 2);
|
||||
else
|
||||
_cur++;
|
||||
|
||||
_cur %= _tracks.size();
|
||||
res = _tracks[_cur];
|
||||
if (!(_flags & Q_REPEAT)) {
|
||||
del(_cur);
|
||||
_cur--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int Queue :: size()
|
||||
{
|
||||
return _tracks.size();
|
||||
}
|
||||
|
||||
const std::string Queue :: size_str()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << size();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const std::string Queue :: length_str()
|
||||
{
|
||||
std::stringstream ss;
|
||||
unsigned int factor[4] = { O_DAYS, O_HOURS, O_MINUTES, 1 };
|
||||
std::string fields[4] = { "day", "hour", "minute", "second" };
|
||||
unsigned int len = _length;
|
||||
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
unsigned int dur = len / factor[i];
|
||||
len -= dur * factor[i];
|
||||
|
||||
if (dur > 0) {
|
||||
ss << dur << " " << fields[i];
|
||||
if (dur > 1)
|
||||
ss << "s";
|
||||
if (len > 0)
|
||||
ss << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
class SortTracks {
|
||||
public:
|
||||
std::vector<struct sort_info> fields;
|
||||
SortTracks(std::vector<sort_info> f) : fields(f) {}
|
||||
bool operator()(Track *lhs, Track *rhs)
|
||||
{
|
||||
return track_less_than(lhs, rhs, fields);
|
||||
}
|
||||
};
|
||||
|
||||
void Queue :: sort(sort_t field, bool reset)
|
||||
{
|
||||
bool found = false;
|
||||
struct sort_info info = { field, true };
|
||||
|
||||
if (_flags & Q_NO_SORT)
|
||||
return;
|
||||
if (reset)
|
||||
_sort_order.clear();
|
||||
|
||||
for (unsigned int i = 0; i < _sort_order.size(); i++) {
|
||||
if (_sort_order[i].field == info.field) {
|
||||
_sort_order[i].ascending = !_sort_order[i].ascending;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
_sort_order.push_back(info);
|
||||
|
||||
std::stable_sort(_tracks.begin(), _tracks.end(), SortTracks(_sort_order));
|
||||
|
||||
for (unsigned int i = 0; i < _tracks.size(); i++)
|
||||
get_callbacks()->on_queue_track_changed(this, i);
|
||||
}
|
||||
|
||||
Track *Queue :: operator[](unsigned int i)
|
||||
{
|
||||
return _tracks[i];
|
||||
}
|
||||
|
||||
void Queue :: track_selected(unsigned int id)
|
||||
{
|
||||
_cur = id;
|
||||
if (has_flag(Q_REPEAT) == false) {
|
||||
del(_cur);
|
||||
_cur--;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker
|
||||
*/
|
||||
#ifdef CONFIG_TEST
|
||||
|
||||
#include <core/random.h>
|
||||
|
||||
static unsigned int _random_value = 0;
|
||||
|
||||
|
||||
void _seed_random(unsigned int n)
|
||||
{
|
||||
_random_value = n;
|
||||
}
|
||||
|
||||
unsigned int _pick_random()
|
||||
{
|
||||
return ++_random_value;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TEST */
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
|
||||
#include <core/tags.h>
|
||||
#include <core/filter.h>
|
||||
#include <core/print.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
|
||||
Database<Artist> artist_db("artist.db", true);
|
||||
Database<Album> album_db("album.db", true);
|
||||
Database<Genre> genre_db("genre.db", true);
|
||||
Database<Library> library_db("library.db", true);
|
||||
Database<Track> track_db("track.db", false);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Artist tag
|
||||
*
|
||||
*/
|
||||
|
||||
Artist :: Artist() {}
|
||||
|
||||
Artist :: Artist(const std::string &s)
|
||||
: name(s), lower(filter :: lowercase(name))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string Artist :: primary_key() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void Artist :: read(File &f)
|
||||
{
|
||||
name = f.getline();
|
||||
lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
void Artist :: write(File &f)
|
||||
{
|
||||
f << name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Album tag
|
||||
*
|
||||
*/
|
||||
|
||||
Album :: Album() {}
|
||||
|
||||
Album :: Album(const std::string &s, unsigned int y)
|
||||
: name(s), lower(filter :: lowercase(name)), year(y)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string Album :: primary_key() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << year << "." << name;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Album :: read(File &f)
|
||||
{
|
||||
f >> year;
|
||||
name = f.getline();
|
||||
lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
void Album :: write(File &f)
|
||||
{
|
||||
f << year << " " << name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Genre tag
|
||||
*
|
||||
*/
|
||||
|
||||
Genre :: Genre() {}
|
||||
|
||||
Genre :: Genre(const std::string &s)
|
||||
: name(s), lower(filter :: lowercase(name))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string Genre :: primary_key() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void Genre :: read(File &f)
|
||||
{
|
||||
name = f.getline();
|
||||
lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
void Genre :: write(File &f)
|
||||
{
|
||||
f << name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Library tag
|
||||
*
|
||||
*/
|
||||
|
||||
Library :: Library()
|
||||
: count(0), enabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
Library :: Library(const std::string &s)
|
||||
: root_path(s), count(0), enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string Library :: primary_key() const
|
||||
{
|
||||
return root_path;
|
||||
}
|
||||
|
||||
void Library :: read(File &f)
|
||||
{
|
||||
f >> enabled;
|
||||
root_path = f.getline();
|
||||
}
|
||||
|
||||
void Library :: write(File &f)
|
||||
{
|
||||
f << enabled << " " << root_path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Track tag
|
||||
*
|
||||
*/
|
||||
|
||||
Track :: Track() : library(NULL), artist(NULL), album(NULL), genre(NULL){}
|
||||
|
||||
Track :: Track(const std::string &f, Library *l)
|
||||
: library(l), artist(NULL), album(NULL), genre(NULL),
|
||||
play_count(0), last_year(0), last_month(0), last_day(0),
|
||||
filepath(f.substr(l->root_path.size() + 1))
|
||||
{
|
||||
library->count++;
|
||||
}
|
||||
|
||||
Track :: ~Track()
|
||||
{
|
||||
library->count--;
|
||||
}
|
||||
|
||||
const std::string Track :: primary_key() const
|
||||
{
|
||||
return path();
|
||||
}
|
||||
|
||||
void Track :: read(File &f)
|
||||
{
|
||||
unsigned int library_id, artist_id, album_id, genre_id;
|
||||
|
||||
f >> library_id >> artist_id >> album_id >> genre_id;
|
||||
f >> track >> last_year >> last_month >> last_day;
|
||||
f >> play_count >> length;
|
||||
|
||||
title = f.getline();
|
||||
filepath = f.getline();
|
||||
|
||||
library = library_db.at(library_id);
|
||||
artist = artist_db.at(artist_id);
|
||||
album = album_db.at(album_id);
|
||||
genre = genre_db.at(genre_id);
|
||||
|
||||
title_lower = filter :: add(title, id);
|
||||
filter :: add(artist->name, id);
|
||||
filter :: add(album->name, id);
|
||||
library->count++;
|
||||
set_length_str();
|
||||
}
|
||||
|
||||
void Track :: write(File &f)
|
||||
{
|
||||
f << library->id << " " << artist->id << " " << album->id << " ";
|
||||
f << genre->id << " " << track << " ";
|
||||
f << last_year << " " << last_month << " " << last_day << " ";
|
||||
f << play_count << " " << length << " " << title << std::endl;
|
||||
f << filepath << std::endl;
|
||||
}
|
||||
|
||||
void Track :: set_length_str()
|
||||
{
|
||||
std::stringstream ss;
|
||||
unsigned int minutes = length / 60;
|
||||
unsigned int seconds = length % 60;
|
||||
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
length_str = ss.str();
|
||||
}
|
||||
|
||||
static inline const std::string format_tag(const TagLib::String &str)
|
||||
{
|
||||
return str.stripWhiteSpace().to8Bit(true);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T *find_or_insert(const T &tag, Database<T> &db)
|
||||
{
|
||||
T *ret = db.find(tag.primary_key());
|
||||
if (!ret)
|
||||
ret = db.insert(tag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Track :: tag()
|
||||
{
|
||||
TagLib :: Tag *tag;
|
||||
TagLib :: AudioProperties *audio;
|
||||
TagLib :: FileRef ref(path().c_str(), true, TagLib::AudioProperties::Fast);
|
||||
|
||||
if (ref.isNull()) {
|
||||
print("ERROR: Could not read tags for file %s\n", path().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
tag = ref.tag();
|
||||
audio = ref.audioProperties();
|
||||
|
||||
artist = find_or_insert(Artist(format_tag(tag->artist())), artist_db);
|
||||
album = find_or_insert(Album(format_tag(tag->album()), tag->year()), album_db);
|
||||
genre = find_or_insert(Genre(format_tag(tag->genre())), genre_db);
|
||||
track = tag->track();
|
||||
length = audio->length();
|
||||
title = format_tag(tag->title());
|
||||
|
||||
title_lower = filter :: add(title, id);
|
||||
set_length_str();
|
||||
|
||||
filter :: add(artist->name, id);
|
||||
filter :: add(album->name, id);
|
||||
|
||||
library->count++;
|
||||
}
|
||||
|
||||
const std::string Track :: path() const
|
||||
{
|
||||
return library->root_path + "/" + filepath;
|
||||
}
|
||||
|
||||
void Track :: played()
|
||||
{
|
||||
time_t the_time = time(NULL);
|
||||
struct tm *now = localtime(&the_time);
|
||||
|
||||
play_count++;
|
||||
last_day = now->tm_mday;
|
||||
last_month = now->tm_mon + 1;
|
||||
last_year = now->tm_year + 1900;
|
||||
|
||||
tagdb :: commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* 0: lhs == rhs
|
||||
* < 0: lhs < rhs, or rhs is empty
|
||||
* > 0: lhs > rhs, or lhs is empty
|
||||
*/
|
||||
static inline int compare_string(const std::string &a, const std::string &b)
|
||||
{
|
||||
if (a.size() == 0)
|
||||
return 1;
|
||||
else if (b.size() == 0)
|
||||
return -1;
|
||||
return a.compare(b);
|
||||
}
|
||||
|
||||
static inline int compare_uint(unsigned int a, unsigned int b)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (a < b)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Track :: less_than(Track *rhs, sort_t field)
|
||||
{
|
||||
int ret;
|
||||
switch (field) {
|
||||
case SORT_ARTIST:
|
||||
return compare_string(artist->lower, rhs->artist->lower);
|
||||
case SORT_ALBUM:
|
||||
return compare_string(album->lower, rhs->album->lower);
|
||||
case SORT_COUNT:
|
||||
return compare_uint(play_count, rhs->play_count);
|
||||
case SORT_GENRE:
|
||||
return compare_string(genre->lower, rhs->genre->lower);
|
||||
case SORT_LENGTH:
|
||||
return compare_uint(length, rhs->length);
|
||||
case SORT_PLAYED:
|
||||
ret = compare_uint(last_year, rhs->last_year);
|
||||
if (ret == 0) {
|
||||
ret = compare_uint(last_month, rhs->last_month);
|
||||
if (ret == 0)
|
||||
ret = compare_uint(last_day, rhs->last_day);
|
||||
}
|
||||
return ret;
|
||||
case SORT_TITLE:
|
||||
return compare_string(title_lower, rhs->title_lower);
|
||||
case SORT_TRACK:
|
||||
return compare_uint(track, rhs->track);
|
||||
case SORT_YEAR:
|
||||
return compare_uint(album->year, rhs->album->year);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Tagdb functions
|
||||
*
|
||||
*/
|
||||
|
||||
void tagdb :: init()
|
||||
{
|
||||
artist_db.load();
|
||||
album_db.load();
|
||||
genre_db.load();
|
||||
library_db.load();
|
||||
track_db.load();
|
||||
}
|
||||
|
||||
void tagdb :: commit()
|
||||
{
|
||||
track_db.save();
|
||||
}
|
||||
|
||||
void tagdb :: commit_library()
|
||||
{
|
||||
library_db.save();
|
||||
}
|
||||
|
||||
Track *tagdb :: add_track(const std::string &filepath, Library *library)
|
||||
{
|
||||
Track *track = track_db.insert(Track(filepath, library));
|
||||
if (track)
|
||||
track->tag();
|
||||
return track;
|
||||
}
|
||||
|
||||
Library *tagdb :: add_library(const std::string &filepath)
|
||||
{
|
||||
return library_db.insert(Library(filepath));
|
||||
}
|
||||
|
||||
void tagdb :: remove_track(unsigned int track_id)
|
||||
{
|
||||
track_db.remove(track_id);
|
||||
}
|
||||
|
||||
void tagdb :: remove_library(unsigned int library_id)
|
||||
{
|
||||
Database<Track>::iterator it;
|
||||
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||
if ((*it)->library->id == library_id)
|
||||
track_db.remove((*it)->id);
|
||||
}
|
||||
tagdb :: commit();
|
||||
library_db.remove(library_id);
|
||||
}
|
||||
|
||||
Track *tagdb :: lookup(unsigned int track_id)
|
||||
{
|
||||
return track_db.at(track_id);
|
||||
}
|
||||
|
||||
Library *tagdb :: lookup_library(unsigned int library_id)
|
||||
{
|
||||
return library_db.at(library_id);
|
||||
}
|
||||
|
||||
Database<Track> &tagdb :: get_track_db()
|
||||
{
|
||||
return track_db;
|
||||
}
|
||||
|
||||
Database<Library> &tagdb :: get_library_db()
|
||||
{
|
||||
return library_db;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/python
|
||||
Import("use_package")
|
||||
Import("env")
|
||||
|
||||
use_package("gtkmm-3.0")
|
||||
env.UsePackage("gtkmm-3.0")
|
||||
|
||||
res = Glob("*.cpp")
|
||||
Return("res")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <deck.h>
|
||||
#include <playlist.h>
|
||||
#include <tabs.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/library.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
|
||||
class CollectionTab : public Tab {
|
||||
|
@ -16,7 +17,7 @@ public:
|
|||
|
||||
|
||||
CollectionTab :: CollectionTab()
|
||||
: Tab(deck::get_library_pq())
|
||||
: Tab(library::get_queue())
|
||||
{
|
||||
tab_random = get_widget<Gtk::ToggleButton>("o_collection_random");
|
||||
tab_search = get_widget<Gtk::SearchEntry>("o_collection_entry");
|
||||
|
@ -46,7 +47,7 @@ bool CollectionTab :: on_key_press_event(const std::string &key)
|
|||
|
||||
tab_selected_ids(ids);
|
||||
for (unsigned int i = 0; i < ids.size(); i++)
|
||||
playlist :: add("Banned", ids[i]);
|
||||
playlist :: add(tagdb :: lookup(ids[i]), "Banned");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <library.h>
|
||||
#include <ocarina.h>
|
||||
#include <core/callback.h>
|
||||
#include <core/library.h>
|
||||
#include <gui/ocarina.h>
|
||||
|
||||
static void on_library_add(unsigned int id, Library *library);
|
||||
|
||||
static class CollectionColumns : public Gtk::TreeModelColumnRecord {
|
||||
public:
|
||||
|
@ -29,8 +30,9 @@ static void on_collection_ok()
|
|||
|
||||
chooser = get_widget<Gtk::FileChooserWidget>("o_collection_chooser");
|
||||
path = chooser->get_filename();
|
||||
library::add_path(path);
|
||||
Library *library = library::add(path);
|
||||
enable_idle();
|
||||
on_library_add(library->id, library);
|
||||
}
|
||||
|
||||
static void on_collection_update()
|
||||
|
@ -39,12 +41,6 @@ static void on_collection_update()
|
|||
enable_idle();
|
||||
}
|
||||
|
||||
static void on_collection_import()
|
||||
{
|
||||
library :: import();
|
||||
enable_idle();
|
||||
}
|
||||
|
||||
static void on_collection_row_activated(const Gtk::TreePath &path,
|
||||
Gtk::TreeViewColumn *col)
|
||||
{
|
||||
|
@ -69,7 +65,7 @@ void do_collection_delete()
|
|||
if (path) {
|
||||
Glib::RefPtr<Gtk::ListStore> list = get_collection_list();
|
||||
Gtk::TreeModel::Row row = *(list->get_iter(path));
|
||||
library :: del_path(row[collection_cols.c_col_id]);
|
||||
library :: remove(tagdb :: lookup_library(row[collection_cols.c_col_id]));
|
||||
list->erase(row);
|
||||
}
|
||||
}
|
||||
|
@ -92,23 +88,23 @@ void on_collection_toggled(const Glib::ustring &path)
|
|||
Glib::RefPtr<Gtk::ListStore> list = get_collection_list();
|
||||
Gtk::TreeModel::Row row = *(list->get_iter(path));
|
||||
row[collection_cols.c_col_enabled] = !row[collection_cols.c_col_enabled];
|
||||
library :: set_enabled(row[collection_cols.c_col_id],
|
||||
library :: set_enabled(tagdb :: lookup_library(row[collection_cols.c_col_id]),
|
||||
row[collection_cols.c_col_enabled]);
|
||||
}
|
||||
|
||||
static void on_library_add(unsigned int id, library :: Library *path)
|
||||
static void on_library_add(unsigned int id, Library *library)
|
||||
{
|
||||
Gtk::TreeModel::Row row;
|
||||
Glib::RefPtr<Gtk::ListStore> list = get_collection_list();
|
||||
|
||||
row = *(list->append());
|
||||
row[collection_cols.c_col_id] = id;
|
||||
row[collection_cols.c_col_enabled] = path->enabled;
|
||||
row[collection_cols.c_col_size] = path->size;
|
||||
row[collection_cols.c_col_path] = path->root_path;
|
||||
row[collection_cols.c_col_enabled] = library->enabled;
|
||||
row[collection_cols.c_col_size] = library->count;
|
||||
row[collection_cols.c_col_path] = library->root_path;
|
||||
}
|
||||
|
||||
static void on_library_update(unsigned int id, library :: Library *path)
|
||||
static void on_library_update(unsigned int id, Library *library)
|
||||
{
|
||||
Gtk::TreeModel::Row row;
|
||||
Glib::RefPtr<Gtk::ListStore> list = get_collection_list();
|
||||
|
@ -118,29 +114,43 @@ static void on_library_update(unsigned int id, library :: Library *path)
|
|||
it != children.end(); it++) {
|
||||
row = *it;
|
||||
if (row[collection_cols.c_col_id] == id)
|
||||
row[collection_cols.c_col_size] = path->size;
|
||||
row[collection_cols.c_col_size] = library->count;
|
||||
}
|
||||
}
|
||||
|
||||
void collection_mgr_init()
|
||||
{
|
||||
struct Callbacks *cb = get_callbacks();
|
||||
|
||||
Gtk::TreeView *treeview = get_widget<Gtk::TreeView>("o_collection_treeview");
|
||||
Glib::RefPtr<Gtk::ListStore> list = get_collection_list();
|
||||
Glib::RefPtr<Gtk::CellRendererToggle> toggle;
|
||||
|
||||
toggle = get_object<Gtk::CellRendererToggle>("o_collection_toggle");
|
||||
|
||||
cb->on_library_add = on_library_add;
|
||||
cb->on_library_update = on_library_update;
|
||||
|
||||
connect_button("o_collection_ok", on_collection_ok);
|
||||
connect_button("o_collection_update", on_collection_update);
|
||||
connect_button("o_collection_import", on_collection_import);
|
||||
|
||||
list->set_sort_column(collection_cols.c_col_path, Gtk::SORT_ASCENDING);
|
||||
treeview->signal_row_activated().connect(sigc::ptr_fun(on_collection_row_activated));
|
||||
treeview->signal_key_press_event().connect(sigc::ptr_fun(on_collection_key_pressed));
|
||||
toggle->signal_toggled().connect(sigc::ptr_fun(on_collection_toggled));
|
||||
}
|
||||
|
||||
void collection_mgr_init2()
|
||||
{
|
||||
Database<Library> *db;
|
||||
Database<Library>::iterator it;
|
||||
|
||||
db = &tagdb :: get_library_db();
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it))
|
||||
on_library_add((*it)->id, *it);
|
||||
}
|
||||
|
||||
void collection_mgr_update()
|
||||
{
|
||||
Database<Library> *db;
|
||||
Database<Library>::iterator it;
|
||||
|
||||
db = &tagdb :: get_library_db();
|
||||
for (it = db->begin(); it != db->end(); it = db->next(it))
|
||||
on_library_update((*it)->id, *it);
|
||||
}
|
||||
|
|
69
gui/gui.cpp
69
gui/gui.cpp
|
@ -1,15 +1,16 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <callback.h>
|
||||
#include <deck.h>
|
||||
#include <idle.h>
|
||||
#include <playlist.h>
|
||||
#include <core/audio.h>
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/driver.h>
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/print.h>
|
||||
|
||||
#include <ocarina.h>
|
||||
#include <print.h>
|
||||
#include <tabs.h>
|
||||
#include <gui/ocarina.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
static bool audio_playing = false;
|
||||
static Glib::RefPtr<Gtk::Builder> builder;
|
||||
|
@ -64,7 +65,7 @@ static void set_label_text(Gtk::Label *label, const std::string &size,
|
|||
Glib::Markup::escape_text(text) + "</span>");
|
||||
}
|
||||
|
||||
static void on_track_loaded(library :: Song &song)
|
||||
static void on_track_loaded(Track *track)
|
||||
{
|
||||
Gtk::ToggleButton *ban = get_widget<Gtk::ToggleButton>("o_ban");
|
||||
Gtk::ToggleButton *fav = get_widget<Gtk::ToggleButton>("o_favorite");
|
||||
|
@ -73,16 +74,13 @@ static void on_track_loaded(library :: Song &song)
|
|||
Gtk::Label *album = get_widget<Gtk::Label>("o_album");
|
||||
Gtk::Label *duration = get_widget<Gtk::Label>("o_total_time");
|
||||
|
||||
set_label_text(title, "xx-large", song.track->title);
|
||||
set_label_text(artist, "x-large", "By: " + song.artist->name);
|
||||
set_label_text(album, "x-large", "From: " + song.album->name);
|
||||
duration->set_text(song.track->length_str);
|
||||
set_label_text(title, "xx-large", track->title);
|
||||
set_label_text(artist, "x-large", "By: " + track->artist->name);
|
||||
set_label_text(album, "x-large", "From: " + track->album->name);
|
||||
duration->set_text(track->length_str);
|
||||
|
||||
std::set<unsigned int> ids = playlist :: get_tracks("Banned");
|
||||
bool banned = ids.find(song.track_id) != ids.end();
|
||||
|
||||
ids = playlist :: get_tracks("Favorites");
|
||||
bool favorite = ids.find(song.track_id) != ids.end();
|
||||
bool banned = playlist :: has(track, "Banned");
|
||||
bool favorite = playlist :: has(track, "Favorites");
|
||||
|
||||
ban_connection.block();
|
||||
fav_connection.block();
|
||||
|
@ -107,12 +105,12 @@ static void on_pause_count_changed(bool enabled, unsigned int count)
|
|||
|
||||
static void on_ban_toggled()
|
||||
{
|
||||
Gtk::ToggleButton *ban = get_widget<Gtk::ToggleButton>("o_pan");
|
||||
Gtk::ToggleButton *ban = get_widget<Gtk::ToggleButton>("o_ban");
|
||||
|
||||
if (ban->get_active() == true)
|
||||
playlist :: add("Banned", audio::current_trackid());
|
||||
playlist :: add(audio :: current_track(), "Banned");
|
||||
else
|
||||
playlist :: del("Banned", audio::current_trackid());
|
||||
playlist :: del(audio::current_track(), "Banned");
|
||||
}
|
||||
|
||||
static void on_fav_toggled()
|
||||
|
@ -120,9 +118,9 @@ static void on_fav_toggled()
|
|||
Gtk::ToggleButton *fav = get_widget<Gtk::ToggleButton>("o_favorite");
|
||||
|
||||
if (fav->get_active() == true)
|
||||
playlist :: add("Favorites", audio::current_trackid());
|
||||
playlist :: add(audio::current_track(), "Favorites");
|
||||
else
|
||||
playlist :: del("Favorites", audio::current_trackid());
|
||||
playlist :: del(audio::current_track(), "Favorites");
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,20 +145,20 @@ static bool on_window_key_pressed(GdkEventKey *event)
|
|||
tab_focus_search();
|
||||
else if (key >= "0" && key <= "9") {
|
||||
unsigned int n = atoi(key.c_str());
|
||||
if (n < deck::size())
|
||||
if (n < deck::get_queues().size())
|
||||
notebook->set_current_page(n);
|
||||
} else if (key == "c")
|
||||
notebook->set_current_page(deck::size());
|
||||
notebook->set_current_page(deck::get_queues().size());
|
||||
else if (key == "h")
|
||||
notebook->set_current_page(deck::size() + 1);
|
||||
notebook->set_current_page(deck::get_queues().size() + 1);
|
||||
else if (key == "m")
|
||||
notebook->set_current_page(deck::size() + 3);
|
||||
notebook->set_current_page(deck::get_queues().size() + 3);
|
||||
else if (key == "n")
|
||||
on_next();
|
||||
else if (key == "N")
|
||||
audio :: previous();
|
||||
audio :: prev();
|
||||
else if (key == "p")
|
||||
notebook->set_current_page(deck::size() + 2);
|
||||
notebook->set_current_page(deck::get_queues().size() + 2);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
@ -170,9 +168,12 @@ static bool on_window_key_released(GdkEventKey *event)
|
|||
{
|
||||
std::string key = gdk_keyval_name(event->keyval);
|
||||
|
||||
if (key == "space")
|
||||
audio :: toggle_play();
|
||||
else
|
||||
if (key == "space") {
|
||||
if (driver :: get_driver()->is_playing())
|
||||
audio :: pause();
|
||||
else
|
||||
audio :: play();
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -193,6 +194,8 @@ bool on_idle()
|
|||
prog->show();
|
||||
prog->set_fraction(idle::get_progress());
|
||||
}
|
||||
|
||||
collection_mgr_update();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -258,7 +261,7 @@ Gtk::Window *setup_gui()
|
|||
connect_button("o_play", audio::play);
|
||||
connect_button("o_pause", audio::pause);
|
||||
connect_button("o_stop", audio::stop);
|
||||
connect_button("o_prev", audio::previous);
|
||||
connect_button("o_prev", audio::prev);
|
||||
connect_button("o_next", on_next);
|
||||
|
||||
count->signal_changed().connect(sigc::ptr_fun(on_config_pause));
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <tabs.h>
|
||||
#include <core/deck.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
|
||||
class HistoryTab : public Tab {
|
||||
|
@ -13,7 +13,7 @@ public:
|
|||
|
||||
|
||||
HistoryTab :: HistoryTab()
|
||||
: Tab(audio::get_recent_pq())
|
||||
: Tab(deck :: get_queue())
|
||||
{
|
||||
tab_search = get_widget<Gtk::SearchEntry>("o_history_entry");
|
||||
tab_size = get_widget<Gtk::Label>("o_history_size");
|
||||
|
|
20
gui/main.cpp
20
gui/main.cpp
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <deck.h>
|
||||
#include <ocarina.h>
|
||||
#include <playlist.h>
|
||||
#include <tabs.h>
|
||||
#include <core/audio.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/library.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/ocarina.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
const std::string share_file(const std::string &file)
|
||||
{
|
||||
|
@ -27,15 +28,16 @@ const std::string share_file(const std::string &file)
|
|||
Gtk::Window *ocarina_init(int *argc, char ***argv)
|
||||
{
|
||||
Gtk::Window *window = setup_gui();
|
||||
audio::init(argc, argv);
|
||||
deck::init();
|
||||
tagdb :: init();
|
||||
library::init();
|
||||
playlist::init();
|
||||
deck::init2();
|
||||
deck::init();
|
||||
audio::init(argc, argv);
|
||||
|
||||
playlist :: select("Favorites");
|
||||
share_file("ocarina6.glade");
|
||||
post_init_tabs();
|
||||
audio::load_state();
|
||||
collection_mgr_init2();
|
||||
return window;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
* See the example at:
|
||||
* https://git.gnome.org/browse/gtkmm-documentation/tree/examples/others/treemodelcustom
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <ocarina.h>
|
||||
#include <core/audio.h>
|
||||
#include <gui/ocarina.h>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
PlayqueueModel::PlayqueueModel(Playqueue *q)
|
||||
: Glib::ObjectBase( typeid(PlayqueueModel) ),
|
||||
QueueModel::QueueModel(Queue *q)
|
||||
: Glib::ObjectBase( typeid(QueueModel) ),
|
||||
Glib::Object(), queue(q)
|
||||
{
|
||||
do {
|
||||
|
@ -19,14 +19,14 @@ PlayqueueModel::PlayqueueModel(Playqueue *q)
|
|||
} while (stamp == 0);
|
||||
}
|
||||
|
||||
void PlayqueueModel::increment_stamp()
|
||||
void QueueModel::increment_stamp()
|
||||
{
|
||||
do {
|
||||
stamp++;
|
||||
} while (stamp == 0);
|
||||
}
|
||||
|
||||
void PlayqueueModel::on_row_inserted(unsigned int row)
|
||||
void QueueModel::on_row_inserted(unsigned int row)
|
||||
{
|
||||
Gtk::TreePath path;
|
||||
Gtk::TreeIter iter;
|
||||
|
@ -36,7 +36,7 @@ void PlayqueueModel::on_row_inserted(unsigned int row)
|
|||
row_inserted(path, iter);
|
||||
}
|
||||
|
||||
void PlayqueueModel::on_row_deleted(unsigned int row)
|
||||
void QueueModel::on_row_deleted(unsigned int row)
|
||||
{
|
||||
Gtk::TreePath path;
|
||||
|
||||
|
@ -45,7 +45,7 @@ void PlayqueueModel::on_row_deleted(unsigned int row)
|
|||
row_deleted(path);
|
||||
}
|
||||
|
||||
void PlayqueueModel::on_row_changed(unsigned int row)
|
||||
void QueueModel::on_row_changed(unsigned int row)
|
||||
{
|
||||
Gtk::TreePath path;
|
||||
Gtk::TreeIter iter;
|
||||
|
@ -55,21 +55,21 @@ void PlayqueueModel::on_row_changed(unsigned int row)
|
|||
row_changed(path, iter);
|
||||
}
|
||||
|
||||
void PlayqueueModel::on_path_selected(const Gtk::TreePath &path)
|
||||
void QueueModel::on_path_selected(const Gtk::TreePath &path)
|
||||
{
|
||||
audio :: load_trackid(path_to_id(path));
|
||||
queue->path_selected(path[0]);
|
||||
audio :: load_track(tagdb :: lookup(path_to_id(path)));
|
||||
queue->track_selected(path[0]);
|
||||
audio :: play();
|
||||
}
|
||||
|
||||
unsigned int PlayqueueModel :: iter_to_id(const Gtk::TreeIter &iter)
|
||||
unsigned int QueueModel :: iter_to_id(const Gtk::TreeIter &iter)
|
||||
{
|
||||
return GPOINTER_TO_UINT(iter.gobj()->user_data);
|
||||
}
|
||||
|
||||
unsigned int PlayqueueModel::path_to_id(const Gtk::TreePath &path)
|
||||
unsigned int QueueModel::path_to_id(const Gtk::TreePath &path)
|
||||
{
|
||||
return queue->operator[](path[0]);
|
||||
return queue->operator[](path[0])->id;
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,17 +80,17 @@ unsigned int PlayqueueModel::path_to_id(const Gtk::TreePath &path)
|
|||
*
|
||||
*/
|
||||
|
||||
Gtk::TreeModelFlags PlayqueueModel::get_flags_vfunc() const
|
||||
Gtk::TreeModelFlags QueueModel::get_flags_vfunc() const
|
||||
{
|
||||
return Gtk::TREE_MODEL_LIST_ONLY;
|
||||
}
|
||||
|
||||
int PlayqueueModel::get_n_columns_vfunc() const
|
||||
int QueueModel::get_n_columns_vfunc() const
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
GType PlayqueueModel::get_column_type_vfunc(int index) const
|
||||
GType QueueModel::get_column_type_vfunc(int index) const
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
|
@ -110,7 +110,7 @@ GType PlayqueueModel::get_column_type_vfunc(int index) const
|
|||
}
|
||||
}
|
||||
|
||||
void PlayqueueModel::get_value_uint(struct library::Song &song, int column,
|
||||
void QueueModel::get_value_uint(Track *track, int column,
|
||||
Glib::ValueBase &value) const
|
||||
{
|
||||
Glib::Value<unsigned int> specific;
|
||||
|
@ -118,20 +118,20 @@ void PlayqueueModel::get_value_uint(struct library::Song &song, int column,
|
|||
|
||||
switch (column) {
|
||||
case 0:
|
||||
specific.set(song.track->track);
|
||||
specific.set(track->track);
|
||||
break;
|
||||
case 5:
|
||||
specific.set(song.album->year);
|
||||
specific.set(track->album->year);
|
||||
break;
|
||||
case 7:
|
||||
specific.set(song.track->play_count);
|
||||
specific.set(track->play_count);
|
||||
}
|
||||
|
||||
value.init(Glib::Value<unsigned int>::value_type());
|
||||
value = specific;
|
||||
}
|
||||
|
||||
void PlayqueueModel::get_value_str(struct library::Song &song, int column,
|
||||
void QueueModel::get_value_str(Track *track, int column,
|
||||
Glib::ValueBase &value) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -140,32 +140,32 @@ void PlayqueueModel::get_value_str(struct library::Song &song, int column,
|
|||
|
||||
switch (column) {
|
||||
case 1:
|
||||
specific.set(song.track->title);
|
||||
specific.set(track->title);
|
||||
break;
|
||||
case 2:
|
||||
specific.set(song.track->length_str);
|
||||
specific.set(track->length_str);
|
||||
break;
|
||||
case 3:
|
||||
specific.set(song.artist->name);
|
||||
specific.set(track->artist->name);
|
||||
break;
|
||||
case 4:
|
||||
specific.set(song.album->name);
|
||||
specific.set(track->album->name);
|
||||
break;
|
||||
case 6:
|
||||
specific.set(song.genre->name);
|
||||
specific.set(track->genre->name);
|
||||
break;
|
||||
case 8:
|
||||
if (song.track->play_count == 0)
|
||||
if (track->play_count == 0)
|
||||
specific.set("Never");
|
||||
else {
|
||||
ss << song.track->last_month << " / ";
|
||||
ss << song.track->last_day << " / ";
|
||||
ss << song.track->last_year;
|
||||
ss << track->last_month << " / ";
|
||||
ss << track->last_day << " / ";
|
||||
ss << track->last_year;
|
||||
specific.set(ss.str());
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
specific.set(Glib::Markup::escape_text(song.track->full_path));
|
||||
specific.set(Glib::Markup::escape_text(track->path()));
|
||||
}
|
||||
|
||||
value.init(Glib::Value<std::string>::value_type());
|
||||
|
@ -173,11 +173,11 @@ void PlayqueueModel::get_value_str(struct library::Song &song, int column,
|
|||
|
||||
}
|
||||
|
||||
void PlayqueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column,
|
||||
void QueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column,
|
||||
Glib::ValueBase &value) const
|
||||
{
|
||||
unsigned int row;
|
||||
struct library::Song song;
|
||||
Track *track;
|
||||
|
||||
if (!check_iter_validity(iter))
|
||||
return;
|
||||
|
@ -186,20 +186,20 @@ void PlayqueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column,
|
|||
return;
|
||||
|
||||
row = GPOINTER_TO_UINT(iter.gobj()->user_data);
|
||||
library :: lookup((*queue)[row], &song);
|
||||
track = queue->operator[](row);
|
||||
|
||||
switch (column) {
|
||||
case 0:
|
||||
case 5:
|
||||
case 7:
|
||||
get_value_uint(song, column, value);
|
||||
get_value_uint(track, column, value);
|
||||
break;
|
||||
default:
|
||||
get_value_str(song, column, value);
|
||||
get_value_str(track, column, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_next_vfunc(const Gtk::TreeIter &iter,
|
||||
bool QueueModel::iter_next_vfunc(const Gtk::TreeIter &iter,
|
||||
Gtk::TreeIter &iter_next) const
|
||||
{
|
||||
unsigned int index;
|
||||
|
@ -213,35 +213,35 @@ bool PlayqueueModel::iter_next_vfunc(const Gtk::TreeIter &iter,
|
|||
return iter_nth_root_child_vfunc(++index, iter_next);
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_children_vfunc(const Gtk::TreeIter &parent,
|
||||
bool QueueModel::iter_children_vfunc(const Gtk::TreeIter &parent,
|
||||
Gtk::TreeIter &iter) const
|
||||
{
|
||||
return iter_nth_child_vfunc(parent, 0, iter);
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_has_child_vfunc(const Gtk::TreeIter &iter) const
|
||||
bool QueueModel::iter_has_child_vfunc(const Gtk::TreeIter &iter) const
|
||||
{
|
||||
return (iter_n_children_vfunc(iter) > 0);
|
||||
}
|
||||
|
||||
int PlayqueueModel::iter_n_children_vfunc(const Gtk::TreeIter &iter) const
|
||||
int QueueModel::iter_n_children_vfunc(const Gtk::TreeIter &iter) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PlayqueueModel::iter_n_root_children_vfunc() const
|
||||
int QueueModel::iter_n_root_children_vfunc() const
|
||||
{
|
||||
return queue->size();
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_nth_child_vfunc(const Gtk::TreeIter &parent,
|
||||
bool QueueModel::iter_nth_child_vfunc(const Gtk::TreeIter &parent,
|
||||
int n, Gtk::TreeIter &iter) const
|
||||
{
|
||||
iter = Gtk::TreeIter();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const
|
||||
bool QueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const
|
||||
{
|
||||
iter = Gtk::TreeIter();
|
||||
if (n >= (int)queue->size())
|
||||
|
@ -252,14 +252,14 @@ bool PlayqueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PlayqueueModel::iter_parent_vfunc(const Gtk::TreeIter &child,
|
||||
bool QueueModel::iter_parent_vfunc(const Gtk::TreeIter &child,
|
||||
Gtk::TreeIter &iter) const
|
||||
{
|
||||
iter = Gtk::TreeIter();
|
||||
return false;
|
||||
}
|
||||
|
||||
Gtk::TreeModel::Path PlayqueueModel::get_path_vfunc(const Gtk::TreeIter &iter) const
|
||||
Gtk::TreeModel::Path QueueModel::get_path_vfunc(const Gtk::TreeIter &iter) const
|
||||
{
|
||||
Gtk::TreeModel::Path path;
|
||||
|
||||
|
@ -268,7 +268,7 @@ Gtk::TreeModel::Path PlayqueueModel::get_path_vfunc(const Gtk::TreeIter &iter) c
|
|||
return path;
|
||||
}
|
||||
|
||||
bool PlayqueueModel::get_iter_vfunc(const Gtk::TreePath &path,
|
||||
bool QueueModel::get_iter_vfunc(const Gtk::TreePath &path,
|
||||
Gtk::TreeIter &iter) const
|
||||
{
|
||||
if (path.size() != 1) {
|
||||
|
@ -278,7 +278,7 @@ bool PlayqueueModel::get_iter_vfunc(const Gtk::TreePath &path,
|
|||
return iter_nth_root_child_vfunc(path[0], iter);
|
||||
}
|
||||
|
||||
bool PlayqueueModel::check_iter_validity(const Gtk::TreeIter &iter) const
|
||||
bool QueueModel::check_iter_validity(const Gtk::TreeIter &iter) const
|
||||
{
|
||||
return stamp == iter.get_stamp();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <playlist.h>
|
||||
#include <tabs.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
|
||||
static const std::string current_playlist();
|
||||
|
@ -20,7 +20,7 @@ public:
|
|||
|
||||
|
||||
PlaylistTab :: PlaylistTab()
|
||||
: Tab(playlist :: get_pq())
|
||||
: Tab(playlist :: get_queue())
|
||||
{
|
||||
tab_search = get_widget<Gtk::SearchEntry>("o_playlist_entry");
|
||||
tab_treeview = get_widget<Gtk::TreeView>("o_playlist_pq_treeview");
|
||||
|
@ -43,7 +43,7 @@ bool PlaylistTab :: on_key_press_event(const std::string &key)
|
|||
|
||||
tab_selected_ids(ids);
|
||||
for (unsigned int i = 0; i < ids.size(); i++)
|
||||
playlist :: del(current_playlist(), ids[i]);
|
||||
playlist :: del(tagdb :: lookup(ids[i]), current_playlist());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <deck.h>
|
||||
#include <tabs.h>
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
|
@ -63,7 +63,7 @@ private:
|
|||
|
||||
|
||||
public:
|
||||
QueueTab(Playqueue *, unsigned int num);
|
||||
QueueTab(Queue *, unsigned int num);
|
||||
~QueueTab();
|
||||
|
||||
/**
|
||||
|
@ -88,7 +88,7 @@ public:
|
|||
static std::map<Gtk::Widget *, QueueTab *> queue_mapping;
|
||||
|
||||
|
||||
QueueTab :: QueueTab(Playqueue *pq, unsigned int num)
|
||||
QueueTab :: QueueTab(Queue *pq, unsigned int num)
|
||||
: Tab(pq)
|
||||
{
|
||||
/*
|
||||
|
@ -218,7 +218,7 @@ void QueueTab :: on_post_init()
|
|||
tab_init_random();
|
||||
tab_init_repeat();
|
||||
|
||||
bool active = (tab_pq->get_flags() & PQ_ENABLED) == PQ_ENABLED;
|
||||
bool active = (tab_pq->has_flag(Q_ENABLED));
|
||||
q_switch.set_active(active);
|
||||
q_switch.property_active().signal_changed().connect(
|
||||
sigc::mem_fun(*this, &QueueTab :: on_switch_changed));
|
||||
|
@ -291,16 +291,16 @@ void QueueTab :: queue_set_sensitive(bool sensitive)
|
|||
|
||||
void QueueTab :: on_close_clicked()
|
||||
{
|
||||
deck :: remove(tab_page_num());
|
||||
deck :: destroy(tab_pq);
|
||||
}
|
||||
|
||||
void QueueTab :: on_switch_changed()
|
||||
{
|
||||
if (q_switch.get_active()) {
|
||||
tab_pq->set_flag(PQ_ENABLED);
|
||||
tab_pq->set_flag(Q_ENABLED);
|
||||
queue_set_sensitive(true);
|
||||
} else {
|
||||
tab_pq->unset_flag(PQ_ENABLED);
|
||||
tab_pq->unset_flag(Q_ENABLED);
|
||||
queue_set_sensitive(false);
|
||||
}
|
||||
}
|
||||
|
@ -319,13 +319,13 @@ static void renumber_queues()
|
|||
it->second->on_tab_reordered();
|
||||
}
|
||||
|
||||
static void on_pq_created(Playqueue *pq, unsigned int num)
|
||||
void on_pq_created(Queue *pq, unsigned int num)
|
||||
{
|
||||
QueueTab *tab = new QueueTab(pq, num);
|
||||
tab->on_post_init();
|
||||
}
|
||||
|
||||
static void on_pq_removed(Playqueue *pq)
|
||||
static void on_pq_removed(Queue *pq)
|
||||
{
|
||||
Tab *tab = find_tab(pq);
|
||||
if (tab) {
|
||||
|
@ -338,8 +338,8 @@ static void on_page_reordered(Gtk::Widget *page, int num)
|
|||
{
|
||||
Gtk::Notebook *notebook = get_widget<Gtk::Notebook>("o_notebook");
|
||||
|
||||
if ((unsigned int)num >= deck :: size()) {
|
||||
notebook->reorder_child(*page, deck::size() - 1);
|
||||
if ((unsigned int)num >= deck :: get_queues().size()) {
|
||||
notebook->reorder_child(*page, deck::get_queues().size() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -354,9 +354,17 @@ static void on_page_reordered(Gtk::Widget *page, int num)
|
|||
void init_queue_tabs()
|
||||
{
|
||||
struct Callbacks *cb = get_callbacks();
|
||||
cb->on_pq_created = on_pq_created;
|
||||
cb->on_pq_removed = on_pq_removed;
|
||||
|
||||
Gtk::Notebook *notebook = get_widget<Gtk::Notebook>("o_notebook");
|
||||
notebook->signal_page_reordered().connect(sigc::ptr_fun(on_page_reordered));
|
||||
}
|
||||
|
||||
void post_init_queue_tabs()
|
||||
{
|
||||
std::list<TempQueue>::iterator it;
|
||||
unsigned int i = 0;
|
||||
|
||||
for (it = deck :: get_queues().begin(); it != deck :: get_queues().end(); it++)
|
||||
on_pq_created(&(*it), i++);
|
||||
}
|
||||
|
|
73
gui/tabs.cpp
73
gui/tabs.cpp
|
@ -1,17 +1,17 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <deck.h>
|
||||
#include <filter.h>
|
||||
#include <playlist.h>
|
||||
#include <tabs.h>
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/filter.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/tabs.h>
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
static std::map<Playqueue *, Tab *> queue_mapping;
|
||||
static std::map<Queue *, Tab *> queue_mapping;
|
||||
|
||||
static sort_t sort_fields[] = {
|
||||
SORT_TRACK, SORT_TITLE, SORT_LENGTH,
|
||||
|
@ -27,10 +27,10 @@ static sort_t sort_fields[] = {
|
|||
*
|
||||
*/
|
||||
|
||||
Tab :: Tab(Playqueue *pq)
|
||||
Tab :: Tab(Queue *pq)
|
||||
: tab_sorting_count(0), tab_pq(pq), tab_size(NULL)
|
||||
{
|
||||
tab_model = Glib::RefPtr<PlayqueueModel>(new PlayqueueModel(tab_pq));
|
||||
tab_model = Glib::RefPtr<QueueModel>(new QueueModel(tab_pq));
|
||||
queue_mapping[tab_pq] = this;
|
||||
}
|
||||
|
||||
|
@ -71,19 +71,19 @@ void Tab :: tab_finish_init()
|
|||
|
||||
void Tab :: tab_init_random()
|
||||
{
|
||||
tab_random->set_active(tab_pq->get_flags() & PQ_RANDOM);
|
||||
tab_random->set_active(tab_pq->has_flag(Q_RANDOM));
|
||||
tab_random->signal_toggled().connect(sigc::mem_fun(*this,
|
||||
&Tab::on_random_toggled));
|
||||
}
|
||||
|
||||
void Tab :: tab_init_repeat()
|
||||
{
|
||||
tab_repeat->set_active(tab_pq->get_flags() & PQ_REPEAT);
|
||||
tab_repeat->set_active(tab_pq->has_flag(Q_REPEAT));
|
||||
tab_repeat->signal_toggled().connect(sigc::mem_fun(*this,
|
||||
&Tab::on_repeat_toggled));
|
||||
}
|
||||
|
||||
void Tab :: tab_toggle_button(Gtk::ToggleButton *button, playqueue_flags flag)
|
||||
void Tab :: tab_toggle_button(Gtk::ToggleButton *button, queue_flags flag)
|
||||
{
|
||||
if (button->get_active())
|
||||
tab_pq->set_flag(flag);
|
||||
|
@ -106,7 +106,7 @@ bool Tab :: tab_is_cur()
|
|||
void Tab :: tab_runtime_changed()
|
||||
{
|
||||
if (tab_is_cur())
|
||||
get_widget<Gtk::Label>("o_queue_time")->set_text(tab_pq->get_length_str());
|
||||
get_widget<Gtk::Label>("o_queue_time")->set_text(tab_pq->length_str());
|
||||
}
|
||||
|
||||
void Tab :: tab_display_sorting()
|
||||
|
@ -155,31 +155,32 @@ void Tab :: tab_selected_ids(std::vector<unsigned int> &ids)
|
|||
sel->unselect_all();
|
||||
}
|
||||
|
||||
void Tab :: tab_queue_add(Playqueue *pq)
|
||||
void Tab :: tab_queue_add(Queue *pq)
|
||||
{
|
||||
std::vector<unsigned int> ids;
|
||||
|
||||
tab_selected_ids(ids);
|
||||
for (unsigned int i = 0; i < ids.size(); i++)
|
||||
pq->add(ids[i]);
|
||||
pq->add(tagdb :: lookup(ids[i]));
|
||||
}
|
||||
|
||||
bool Tab :: tab_queue_selected(bool random)
|
||||
{
|
||||
if (deck :: size() >= 10)
|
||||
if (deck :: get_queues().size() >= 10)
|
||||
return true;
|
||||
|
||||
Playqueue *pq = deck :: create(random);
|
||||
Queue *pq = deck :: create(random);
|
||||
on_pq_created(pq, deck :: get_queues().size() - 1);
|
||||
tab_queue_add(pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tab :: tab_add_to_queue(unsigned int n)
|
||||
{
|
||||
if (n >= deck :: size())
|
||||
if (n >= deck :: get_queues().size())
|
||||
return true;
|
||||
|
||||
Playqueue *pq = deck :: get(n);
|
||||
Queue *pq = deck :: get(n);
|
||||
tab_queue_add(pq);
|
||||
return true;
|
||||
}
|
||||
|
@ -190,7 +191,7 @@ bool Tab :: tab_add_to_playlist(const std::string &playlist)
|
|||
|
||||
tab_selected_ids(ids);
|
||||
for (unsigned int i = 0; i < ids.size(); i++)
|
||||
playlist :: add(playlist, ids[i]);
|
||||
playlist :: add(tagdb :: lookup(ids[i]), playlist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ bool Tab :: on_key_press_event(const std::string &key)
|
|||
void Tab :: on_show_rc_menu()
|
||||
{
|
||||
std::string item;
|
||||
unsigned int size = deck :: size();
|
||||
unsigned int size = deck :: get_queues().size();
|
||||
|
||||
if (size == 0) {
|
||||
get_widget<Gtk::MenuItem>("o_add_to_pq")->hide();
|
||||
|
@ -284,12 +285,12 @@ void Tab :: on_show_rc_menu()
|
|||
|
||||
void Tab :: on_random_toggled()
|
||||
{
|
||||
tab_toggle_button(tab_random, PQ_RANDOM);
|
||||
tab_toggle_button(tab_random, Q_RANDOM);
|
||||
}
|
||||
|
||||
void Tab :: on_repeat_toggled()
|
||||
{
|
||||
tab_toggle_button(tab_repeat, PQ_REPEAT);
|
||||
tab_toggle_button(tab_repeat, Q_REPEAT);
|
||||
}
|
||||
|
||||
void Tab :: on_row_activated(const Gtk::TreePath &path, Gtk::TreeViewColumn *col)
|
||||
|
@ -314,9 +315,9 @@ void Tab :: on_column_clicked(unsigned int col)
|
|||
{
|
||||
if (tab_sorting_count == 0) {
|
||||
tab_sorting_title = tab_treeview->get_column(col)->get_title();
|
||||
tab_pq->reset_sort(sort_fields[col]);
|
||||
tab_pq->sort(sort_fields[col], true);
|
||||
} else
|
||||
tab_pq->add_sort(sort_fields[col]);
|
||||
tab_pq->sort(sort_fields[col], false);
|
||||
|
||||
tab_sorting_count++;
|
||||
tab_display_sorting();
|
||||
|
@ -355,7 +356,7 @@ bool Tab :: on_filter_visible(const Gtk::TreeIter &iter)
|
|||
return true;
|
||||
|
||||
pq_id = tab_model->iter_to_id(iter);
|
||||
it = visible_ids.find(tab_pq->operator[](pq_id));
|
||||
it = visible_ids.find(tab_pq->operator[](pq_id)->id);
|
||||
return it != visible_ids.end();
|
||||
}
|
||||
|
||||
|
@ -379,9 +380,9 @@ void Tab :: on_entry_changed()
|
|||
*
|
||||
*/
|
||||
|
||||
Tab *find_tab(Playqueue *pq)
|
||||
Tab *find_tab(Queue *pq)
|
||||
{
|
||||
std::map<Playqueue *, Tab *>::iterator it;
|
||||
std::map<Queue *, Tab *>::iterator it;
|
||||
it = queue_mapping.find(pq);
|
||||
if (it != queue_mapping.end())
|
||||
return it->second;
|
||||
|
@ -390,7 +391,7 @@ Tab *find_tab(Playqueue *pq)
|
|||
|
||||
static Tab *find_tab(int num)
|
||||
{
|
||||
std::map<Playqueue *, Tab *>::iterator it;
|
||||
std::map<Queue *, Tab *>::iterator it;
|
||||
for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) {
|
||||
if (it->second->tab_page_num() == num)
|
||||
return it->second;
|
||||
|
@ -400,7 +401,7 @@ static Tab *find_tab(int num)
|
|||
|
||||
static Tab *cur_tab()
|
||||
{
|
||||
std::map<Playqueue *, Tab *>::iterator it;
|
||||
std::map<Queue *, Tab *>::iterator it;
|
||||
for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) {
|
||||
if (it->second->tab_is_cur())
|
||||
return it->second;
|
||||
|
@ -408,21 +409,21 @@ static Tab *cur_tab()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void on_track_added(Playqueue *pq, unsigned int row)
|
||||
static void on_track_added(Queue *pq, unsigned int row)
|
||||
{
|
||||
Tab *tab = find_tab(pq);
|
||||
if (tab)
|
||||
tab->on_track_added(row);
|
||||
}
|
||||
|
||||
static void on_track_deleted(Playqueue *pq, unsigned int row)
|
||||
static void on_track_deleted(Queue *pq, unsigned int row)
|
||||
{
|
||||
Tab *tab = find_tab(pq);
|
||||
if (tab)
|
||||
tab->on_track_deleted(row);
|
||||
}
|
||||
|
||||
static void on_track_changed(Playqueue *pq, unsigned int row)
|
||||
static void on_track_changed(Queue *pq, unsigned int row)
|
||||
{
|
||||
Tab *tab = find_tab(pq);
|
||||
if (tab)
|
||||
|
@ -516,13 +517,15 @@ void init_tabs()
|
|||
|
||||
void post_init_tabs()
|
||||
{
|
||||
std::map<Playqueue *, Tab *>::iterator it;
|
||||
std::map<Queue *, Tab *>::iterator it;
|
||||
for (it = queue_mapping.begin(); it != queue_mapping.end(); it++)
|
||||
it->second->on_post_init();
|
||||
|
||||
post_init_queue_tabs();
|
||||
|
||||
unsigned int tab = 0;
|
||||
for (tab = 0; tab < deck::size(); tab++) {
|
||||
if ((deck :: get(tab)->get_flags() & PQ_ENABLED) == PQ_ENABLED)
|
||||
for (tab = 0; tab < deck::get_queues().size(); tab++) {
|
||||
if ((deck :: get(tab)->has_flag(Q_ENABLED)))
|
||||
break;
|
||||
}
|
||||
get_widget<Gtk::Notebook>("o_notebook")->set_current_page(tab);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#!/usr/bin/python
|
||||
Import("env", "CONFIG_DEBUG", "CONFIG_VERSION")
|
||||
Import("env", "test_env")
|
||||
|
||||
version = str(CONFIG_VERSION)
|
||||
if CONFIG_DEBUG == True:
|
||||
version = str(env.Version)
|
||||
if env.Debug == True:
|
||||
version += "-debug"
|
||||
|
||||
env.Append( CCFLAGS = [ "-DCONFIG_VERSION='\"%s\"'" % version ] )
|
||||
for e in (env, test_env):
|
||||
e.Append( CCFLAGS = [ "-DCONFIG_VERSION='\"%s\"'" % version ] )
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_CALLBACK_H
|
||||
#define OCARINA_CALLBACK_H
|
||||
|
||||
#include <library.h>
|
||||
#include <playqueue.h>
|
||||
|
||||
|
||||
struct Callbacks {
|
||||
/* Audio callbacks */
|
||||
void (*on_play)();
|
||||
void (*on_pause)();
|
||||
void (*on_track_loaded)(library :: Song &);
|
||||
void (*on_pause_count_changed)(bool, unsigned int);
|
||||
|
||||
/* Deck callbacks */
|
||||
void (*on_pq_created)(Playqueue *, unsigned int);
|
||||
void (*on_pq_removed)(Playqueue *);
|
||||
|
||||
/* Library callbacks */
|
||||
void (*on_library_add)(unsigned int, library :: Library *);
|
||||
void (*on_library_update)(unsigned int, library :: Library *);
|
||||
void (*on_library_track_add)(unsigned int);
|
||||
void (*on_library_track_del)(unsigned int);
|
||||
void (*on_library_track_updated)(unsigned int);
|
||||
void (*on_library_import_ban)(unsigned int);
|
||||
|
||||
/* Playlist callbacks */
|
||||
void (*on_playlist_ban)(unsigned int);
|
||||
void (*on_playlist_unban)(unsigned int);
|
||||
|
||||
/* Playqueue callbacks */
|
||||
void (*on_queue_track_add)(Playqueue *, unsigned int);
|
||||
void (*on_queue_track_del)(Playqueue *, unsigned int);
|
||||
void (*on_queue_track_changed)(Playqueue *, unsigned int);
|
||||
void (*on_queue_changed)();
|
||||
};
|
||||
|
||||
|
||||
struct Callbacks *get_callbacks();
|
||||
|
||||
|
||||
#endif /* OCARINA_CALLBACK_H */
|
|
@ -4,35 +4,27 @@
|
|||
#ifndef OCARINA_AUDIO_H
|
||||
#define OCARINA_AUDIO_H
|
||||
|
||||
#include <playqueue.h>
|
||||
|
||||
extern "C" {
|
||||
#include <gst/gst.h>
|
||||
}
|
||||
|
||||
#include <core/tags.h>
|
||||
#include <string>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
|
||||
void init(int *, char ***);
|
||||
void load_state();
|
||||
void quit();
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
void toggle_play();
|
||||
void stop();
|
||||
void next();
|
||||
void previous();
|
||||
void load_trackid(unsigned int);
|
||||
unsigned int current_trackid();
|
||||
Playqueue *get_recent_pq();
|
||||
|
||||
void seek_to(long);
|
||||
void stop();
|
||||
|
||||
long position();
|
||||
std::string position_str();
|
||||
long duration();
|
||||
std::string position_str();
|
||||
|
||||
void next();
|
||||
void prev();
|
||||
void load_track(Track *track);
|
||||
Track *current_track();
|
||||
|
||||
void pause_after(bool, unsigned int);
|
||||
bool pause_enabled();
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_CALLBACK_H
|
||||
#define OCARINA_CALLBACK_H
|
||||
|
||||
#include <core/library.h>
|
||||
#include <core/queue.h>
|
||||
|
||||
|
||||
struct Callbacks {
|
||||
/* Audio callbacks */
|
||||
void (*on_play)();
|
||||
void (*on_pause)();
|
||||
void (*on_track_loaded)(Track *);
|
||||
void (*on_pause_count_changed)(bool, unsigned int);
|
||||
|
||||
/* Deck callbacks */
|
||||
void (*on_pq_removed)(Queue *);
|
||||
|
||||
/* Queue callbacks */
|
||||
void (*on_queue_track_add)(Queue *, unsigned int);
|
||||
void (*on_queue_track_del)(Queue *, unsigned int);
|
||||
void (*on_queue_track_changed)(Queue *, unsigned int);
|
||||
};
|
||||
|
||||
|
||||
struct Callbacks *get_callbacks();
|
||||
|
||||
|
||||
#endif /* OCARINA_CALLBACK_H */
|
|
@ -4,8 +4,7 @@
|
|||
#ifndef OCARINA_DATABASE_H
|
||||
#define OCARINA_DATABASE_H
|
||||
|
||||
#include <file.h>
|
||||
#include <print.h>
|
||||
#include <core/file.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
@ -13,11 +12,11 @@
|
|||
|
||||
class DatabaseEntry {
|
||||
public:
|
||||
bool valid;
|
||||
unsigned int id;
|
||||
|
||||
DatabaseEntry();
|
||||
virtual const std::string primary_key() = 0;
|
||||
virtual ~DatabaseEntry() = 0;
|
||||
virtual const std::string primary_key() const = 0;
|
||||
virtual void write(File &) = 0;
|
||||
virtual void read(File &) = 0;
|
||||
};
|
||||
|
@ -27,15 +26,15 @@ public:
|
|||
template <class T>
|
||||
class Database {
|
||||
private:
|
||||
std::vector<T> _db;
|
||||
std::vector<T *> _db;
|
||||
std::map<const std::string, unsigned int> _keys;
|
||||
unsigned int _size;
|
||||
bool _autosave;
|
||||
File _file;
|
||||
|
||||
public:
|
||||
typedef typename std::vector<T>::iterator iterator;
|
||||
typedef typename std::vector<T>::const_iterator const_iterator;
|
||||
typedef typename std::vector<T *>::iterator iterator;
|
||||
typedef typename std::vector<T *>::const_iterator const_iterator;
|
||||
|
||||
Database(std::string, bool);
|
||||
~Database();
|
||||
|
@ -43,7 +42,7 @@ public:
|
|||
void autosave();
|
||||
void load();
|
||||
|
||||
unsigned int insert(T);
|
||||
T *insert(const T &);
|
||||
void remove(unsigned int);
|
||||
unsigned int size();
|
||||
unsigned int actual_size();
|
||||
|
@ -52,8 +51,8 @@ public:
|
|||
iterator end();
|
||||
iterator next(iterator &);
|
||||
|
||||
iterator at(unsigned int);
|
||||
iterator find(const std::string &);
|
||||
T *at(unsigned int);
|
||||
T *find(const std::string &);
|
||||
};
|
||||
|
||||
#include "database.hpp"
|
|
@ -7,17 +7,18 @@
|
|||
#ifndef OCARINA_DATABASE_HPP
|
||||
#define OCARINA_DATABASE_HPP
|
||||
|
||||
#include <error.h>
|
||||
|
||||
template <class T>
|
||||
Database<T> :: Database(std::string filepath, bool autosave)
|
||||
: _size(0), _autosave(autosave), _file(filepath, FILE_TYPE_DATA)
|
||||
: _size(0), _autosave(autosave), _file(filepath, 0)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Database<T> :: ~Database()
|
||||
{
|
||||
iterator it;
|
||||
for (it = begin(); it != end(); it = next(it))
|
||||
delete (*it);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
@ -26,12 +27,13 @@ void Database<T> :: save()
|
|||
if (_file.open(OPEN_WRITE) == false)
|
||||
return;
|
||||
|
||||
_file << _db.size() << std::endl;
|
||||
_file << actual_size() << std::endl;
|
||||
for (unsigned int i = 0; i < _db.size(); i++) {
|
||||
_file << _db[i].valid;
|
||||
if (_db[i].valid == true) {
|
||||
_file << " ";
|
||||
_db[i].write(_file);
|
||||
if (_db[i] == NULL)
|
||||
_file << false;
|
||||
else {
|
||||
_file << true << " ";
|
||||
_db[i]->write(_file);
|
||||
}
|
||||
_file << std::endl;
|
||||
}
|
||||
|
@ -50,6 +52,7 @@ template <class T>
|
|||
void Database<T> :: load()
|
||||
{
|
||||
unsigned int db_size;
|
||||
bool valid;
|
||||
|
||||
if (_file.exists() == false)
|
||||
return;
|
||||
|
@ -59,11 +62,14 @@ void Database<T> :: load()
|
|||
_file >> db_size;
|
||||
_db.resize(db_size);
|
||||
for (unsigned int i = 0; i < db_size; i++) {
|
||||
_file >> _db[i].valid;
|
||||
if (_db[i].valid == true) {
|
||||
_db[i].read(_file);
|
||||
_db[i].id = i;
|
||||
_keys.insert(std::pair<std::string, unsigned int>(_db[i].primary_key(), i));
|
||||
_file >> valid;
|
||||
if (valid == false)
|
||||
_db[i] = NULL;
|
||||
else {
|
||||
_db[i] = new T;
|
||||
_db[i]->id = i;
|
||||
_db[i]->read(_file);
|
||||
_keys[_db[i]->primary_key()] = i;
|
||||
_size++;
|
||||
}
|
||||
}
|
||||
|
@ -72,25 +78,21 @@ void Database<T> :: load()
|
|||
}
|
||||
|
||||
template <class T>
|
||||
unsigned int Database<T> :: insert(T val)
|
||||
T *Database<T> :: insert(const T &val)
|
||||
{
|
||||
unsigned int id;
|
||||
iterator it = find(val.primary_key());
|
||||
T *t = find(val.primary_key());
|
||||
|
||||
if (it != end())
|
||||
return it - _db.begin();
|
||||
if (t != NULL)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Check primary key stuff here
|
||||
*/
|
||||
id = _db.size();
|
||||
_db.push_back(val);
|
||||
_keys[val.primary_key()] = id;
|
||||
_db[id].valid = true;
|
||||
_db[id].id = id;
|
||||
t = new T(val);
|
||||
_db.push_back(t);
|
||||
t->id = actual_size() - 1;
|
||||
|
||||
_keys[t->primary_key()] = t->id;
|
||||
_size++;
|
||||
autosave();
|
||||
return id;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
@ -98,10 +100,11 @@ void Database<T> :: remove(unsigned int id)
|
|||
{
|
||||
if (id >= actual_size())
|
||||
return;
|
||||
if (_db[id].valid == false)
|
||||
if (_db[id] == NULL)
|
||||
return;
|
||||
_keys.erase(_db[id].primary_key());
|
||||
_db[id].valid = false;
|
||||
_keys.erase(_db[id]->primary_key());
|
||||
delete _db[id];
|
||||
_db[id] = NULL;
|
||||
_size--;
|
||||
autosave();
|
||||
}
|
||||
|
@ -125,7 +128,7 @@ typename Database<T>::iterator Database<T> :: begin()
|
|||
return end();
|
||||
|
||||
iterator it = _db.begin();
|
||||
if ( (*it).valid == true )
|
||||
if ( (*it) != NULL )
|
||||
return it;
|
||||
return next(it);
|
||||
}
|
||||
|
@ -141,28 +144,25 @@ typename Database<T>::iterator Database<T> :: next(iterator &it)
|
|||
{
|
||||
do {
|
||||
it++;
|
||||
} while ((it != end()) && ((*it).valid == false));
|
||||
} while ((it != end()) && (*it) == NULL);
|
||||
return it;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename Database<T>::iterator Database<T> :: at(unsigned int id)
|
||||
T *Database<T> :: at(unsigned int id)
|
||||
{
|
||||
if (id >= actual_size())
|
||||
return end();
|
||||
|
||||
if (_db[id].valid == false)
|
||||
return end();
|
||||
return _db.begin() + id;
|
||||
return NULL;
|
||||
return _db[id];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename Database<T>::iterator Database<T> :: find(const std::string &key)
|
||||
T *Database<T> :: find(const std::string &key)
|
||||
{
|
||||
std::map<const std::string, unsigned int>::iterator it;
|
||||
it = _keys.find(key);
|
||||
if (it == _keys.end())
|
||||
return end();
|
||||
return _db.begin() + it->second;
|
||||
return NULL;
|
||||
return _db[it->second];
|
||||
}
|
||||
#endif /* OCARINA_DATABASE_HPP */
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_DECK_H
|
||||
#define OCARINA_DECK_H
|
||||
|
||||
#include <core/queue.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
class TempQueue : public Queue
|
||||
{
|
||||
public:
|
||||
TempQueue();
|
||||
TempQueue(bool random);
|
||||
|
||||
void set_flag(queue_flags);
|
||||
void unset_flag(queue_flags);
|
||||
|
||||
unsigned int add(Track *);
|
||||
void del(Track *);
|
||||
void del(unsigned int);
|
||||
|
||||
void sort(sort_t, bool);
|
||||
};
|
||||
|
||||
namespace deck
|
||||
{
|
||||
|
||||
void init();
|
||||
void write();
|
||||
|
||||
Queue *create(bool);
|
||||
void destroy(Queue *);
|
||||
void move(Queue *, unsigned int);
|
||||
|
||||
unsigned int index(Queue *);
|
||||
Queue *get(unsigned int);
|
||||
|
||||
Track *next();
|
||||
Track *prev();
|
||||
|
||||
std::list<TempQueue> &get_queues();
|
||||
Queue *get_queue();
|
||||
|
||||
};
|
||||
|
||||
#endif /* OCARINA_DECK_H */
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
static const unsigned long O_SECOND = 1000000000;
|
||||
|
||||
|
||||
class Driver {
|
||||
protected:
|
||||
void (*on_eos)();
|
||||
void (*on_error)();
|
||||
|
||||
public:
|
||||
Driver();
|
||||
~Driver();
|
||||
virtual void init(int *, char ***, void (*)(), void (*)()) = 0;
|
||||
|
||||
virtual void load(const std::string &) = 0;
|
||||
virtual bool play() = 0;
|
||||
virtual bool pause() = 0;
|
||||
virtual bool is_playing() = 0;
|
||||
|
||||
virtual void seek_to(long) = 0;
|
||||
virtual long position() = 0;
|
||||
virtual long duration() = 0;
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
class TestDriver : public Driver
|
||||
{
|
||||
public:
|
||||
bool playing;
|
||||
long cur_pos;
|
||||
long cur_duration;
|
||||
std::string cur_file;
|
||||
|
||||
TestDriver();
|
||||
~TestDriver();
|
||||
void init(int *, char ***, void (*)(), void (*)());
|
||||
|
||||
void load(const std::string &);
|
||||
bool play();
|
||||
bool pause();
|
||||
bool is_playing();
|
||||
|
||||
void seek_to(long);
|
||||
long position();
|
||||
long duration();
|
||||
|
||||
void eos();
|
||||
void error();
|
||||
};
|
||||
#else /* !CONFIG_TEST */
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
class GSTDriver : public Driver
|
||||
{
|
||||
private:
|
||||
GstElement *player;
|
||||
std::string cur_file;
|
||||
bool change_state(GstState state);
|
||||
|
||||
public:
|
||||
GSTDriver();
|
||||
~GSTDriver();
|
||||
void init(int *, char ***, void (*)(), void (*)());
|
||||
|
||||
void load(const std::string &);
|
||||
bool play();
|
||||
bool pause();
|
||||
bool is_playing();
|
||||
|
||||
void seek_to(long);
|
||||
long position();
|
||||
long duration();
|
||||
|
||||
void on_message(GstMessage *);
|
||||
};
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
|
||||
namespace driver
|
||||
{
|
||||
|
||||
Driver *get_driver();
|
||||
|
||||
}
|
|
@ -7,13 +7,6 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#define FILE_VERSION 0
|
||||
|
||||
enum FileLocHint {
|
||||
FILE_TYPE_DATA,
|
||||
FILE_TYPE_LEGACY,
|
||||
FILE_TYPE_INVALID,
|
||||
};
|
||||
|
||||
enum OpenMode {
|
||||
OPEN_READ,
|
||||
|
@ -21,21 +14,23 @@ enum OpenMode {
|
|||
NOT_OPEN,
|
||||
};
|
||||
|
||||
|
||||
class File : public std::fstream {
|
||||
private:
|
||||
OpenMode mode;
|
||||
FileLocHint hint;
|
||||
std::string filepath;
|
||||
std::string filename;
|
||||
unsigned int version;
|
||||
unsigned int prev_version;
|
||||
|
||||
std::string find_dir();
|
||||
const std::string find_dir();
|
||||
bool open_read();
|
||||
bool open_write();
|
||||
|
||||
public:
|
||||
File(const std::string &, FileLocHint);
|
||||
|
||||
File(const std::string &, unsigned int);
|
||||
~File();
|
||||
const char *get_filepath();
|
||||
const std::string get_filepath();
|
||||
const unsigned int get_version();
|
||||
bool exists();
|
||||
bool open(OpenMode);
|
|
@ -4,7 +4,7 @@
|
|||
#ifndef OCARINA_INDEX_H
|
||||
#define OCARINA_INDEX_H
|
||||
|
||||
#include <database.h>
|
||||
#include <core/database.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -17,7 +17,7 @@ public:
|
|||
|
||||
IndexEntry();
|
||||
IndexEntry(const std::string &);
|
||||
const std::string primary_key();
|
||||
const std::string primary_key() const;
|
||||
void insert(unsigned int);
|
||||
void remove(unsigned int);
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_LIBRARY_H
|
||||
#define OCARINA_LIBRARY_H
|
||||
|
||||
#include <core/queue.h>
|
||||
#include <core/tags.h>
|
||||
#include <string>
|
||||
|
||||
namespace library
|
||||
{
|
||||
|
||||
void init();
|
||||
|
||||
Library *add(const std::string &);
|
||||
void remove(Library *);
|
||||
void update(Library *);
|
||||
void update_all();
|
||||
|
||||
void set_enabled(Library *, bool);
|
||||
Queue *get_queue();
|
||||
|
||||
};
|
||||
|
||||
#endif /* OCARINA_LIBRARY_H */
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_PLAYLIST_H
|
||||
#define OCARINA_PLAYLIST_H
|
||||
|
||||
#include <core/index.h>
|
||||
#include <core/queue.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace playlist
|
||||
{
|
||||
|
||||
void init();
|
||||
bool has(Track *, const std::string &);
|
||||
void add(Track *, const std::string &);
|
||||
void del(Track *, const std::string &);
|
||||
void select(const std::string &);
|
||||
IndexEntry *get_tracks(const std::string &);
|
||||
Queue *get_queue();
|
||||
|
||||
};
|
||||
|
||||
#endif /* OCARINA_PLAYLIST_H */
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_QUEUE_H
|
||||
#define OCARINA_QUEUE_H
|
||||
|
||||
#include <core/file.h>
|
||||
#include <core/tags.h>
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
enum queue_flags {
|
||||
Q_ENABLED = (1 << 0),
|
||||
Q_RANDOM = (1 << 1),
|
||||
Q_REPEAT = (1 << 2),
|
||||
Q_NO_SORT = (1 << 3),
|
||||
};
|
||||
|
||||
static const unsigned int Q_FLAG_MASK = Q_ENABLED | Q_RANDOM | Q_REPEAT | Q_NO_SORT;
|
||||
|
||||
struct sort_info {
|
||||
sort_t field;
|
||||
bool ascending;
|
||||
};
|
||||
|
||||
class Queue {
|
||||
protected:
|
||||
std :: vector <Track *> _tracks;
|
||||
std :: vector <struct sort_info> _sort_order;
|
||||
unsigned int _cur;
|
||||
unsigned int _flags;
|
||||
unsigned int _length;
|
||||
|
||||
unsigned int find_sorted_id(Track *);
|
||||
unsigned int _add_at(Track *, unsigned int);
|
||||
void _del_at(Track *, unsigned int);
|
||||
|
||||
public:
|
||||
Queue();
|
||||
Queue(unsigned int);
|
||||
~Queue();
|
||||
void write(File &);
|
||||
void read(File &);
|
||||
|
||||
virtual void set_flag(queue_flags);
|
||||
virtual void unset_flag(queue_flags);
|
||||
bool has_flag(queue_flags);
|
||||
|
||||
virtual unsigned int add(Track *);
|
||||
virtual void del(Track *);
|
||||
virtual void del(unsigned int);
|
||||
void updated(Track *);
|
||||
Track *next();
|
||||
|
||||
unsigned int size();
|
||||
const std::string size_str();
|
||||
const std::string length_str();
|
||||
|
||||
virtual void sort(sort_t, bool);
|
||||
Track *operator[](unsigned int);
|
||||
void track_selected(unsigned int);
|
||||
};
|
||||
|
||||
#endif /* OCARINA_QUEUE_H */
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker
|
||||
*/
|
||||
|
||||
#ifndef OCARINA_RANDOM_H
|
||||
#define OCARINA_RANDOM_H
|
||||
|
||||
#ifndef CONFIG_TEST
|
||||
|
||||
#include <stdlib.h>
|
||||
static inline void _seed_random(unsigned int n) { srand(time(NULL) + n); }
|
||||
static inline unsigned int _pick_random() { return rand(); }
|
||||
|
||||
#else /* CONFIG_TEST */
|
||||
|
||||
void _seed_random(unsigned int);
|
||||
unsigned int _pick_random();
|
||||
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
|
||||
|
||||
static inline void random_seed(unsigned int n)
|
||||
{
|
||||
_seed_random(n);
|
||||
}
|
||||
|
||||
static inline unsigned int random(unsigned int min, unsigned int max)
|
||||
{
|
||||
if (min >= max)
|
||||
return min;
|
||||
return min + (_pick_random() % (max - min));
|
||||
}
|
||||
|
||||
|
||||
#endif /* OCARINA_RANDOM_H */
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_TAGS_H
|
||||
#define OCARINA_TAGS_H
|
||||
|
||||
#include <core/database.h>
|
||||
|
||||
|
||||
enum sort_t {
|
||||
SORT_ARTIST,
|
||||
SORT_ALBUM,
|
||||
SORT_COUNT,
|
||||
SORT_GENRE,
|
||||
SORT_LENGTH,
|
||||
SORT_PLAYED,
|
||||
SORT_TITLE,
|
||||
SORT_TRACK,
|
||||
SORT_YEAR,
|
||||
};
|
||||
|
||||
|
||||
class Artist : public DatabaseEntry {
|
||||
public:
|
||||
std::string name;
|
||||
std::string lower;
|
||||
|
||||
Artist();
|
||||
Artist(const std::string &);
|
||||
const std::string primary_key() const;
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Album : public DatabaseEntry {
|
||||
public:
|
||||
std::string name;
|
||||
std::string lower;
|
||||
unsigned int year;
|
||||
|
||||
Album();
|
||||
Album(const std::string &, unsigned int);
|
||||
const std::string primary_key() const;
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Genre : public DatabaseEntry {
|
||||
public:
|
||||
std::string name;
|
||||
std::string lower;
|
||||
|
||||
Genre();
|
||||
Genre(const std::string &);
|
||||
const std::string primary_key() const;
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Library : public DatabaseEntry {
|
||||
public:
|
||||
std::string root_path;
|
||||
unsigned int count;
|
||||
bool enabled;
|
||||
|
||||
Library();
|
||||
Library(const std::string &);
|
||||
const std::string primary_key() const;
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Track : public DatabaseEntry {
|
||||
private:
|
||||
void set_length_str();
|
||||
|
||||
public:
|
||||
Library *library;
|
||||
Artist *artist;
|
||||
Album *album;
|
||||
Genre *genre;
|
||||
|
||||
unsigned int track;
|
||||
unsigned int length;
|
||||
unsigned int play_count;
|
||||
unsigned int last_year;
|
||||
unsigned int last_month;
|
||||
unsigned int last_day;
|
||||
|
||||
std :: string title;
|
||||
std :: string title_lower;
|
||||
std :: string filepath;
|
||||
std :: string length_str;
|
||||
|
||||
Track();
|
||||
Track(const std::string &, Library *);
|
||||
~Track();
|
||||
const std::string primary_key() const;
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
|
||||
void tag();
|
||||
const std::string path() const;
|
||||
void played();
|
||||
int less_than(Track *, sort_t);
|
||||
};
|
||||
|
||||
|
||||
namespace tagdb
|
||||
{
|
||||
|
||||
void init();
|
||||
void commit();
|
||||
void commit_library();
|
||||
|
||||
Track *add_track(const std::string &, Library *);
|
||||
Library *add_library(const std::string &);
|
||||
void remove_track(unsigned int);
|
||||
void remove_library(unsigned int);
|
||||
Track *lookup(unsigned int);
|
||||
Library *lookup_library(unsigned int);
|
||||
Database<Track> &get_track_db();
|
||||
Database<Library> &get_library_db();
|
||||
|
||||
}
|
||||
|
||||
#endif /* OCARINA_TAGS_H */
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_DECK_H
|
||||
#define OCARINA_DECK_H
|
||||
|
||||
#include <playqueue.h>
|
||||
|
||||
namespace deck
|
||||
{
|
||||
|
||||
void init();
|
||||
void init2();
|
||||
void read();
|
||||
void write();
|
||||
|
||||
Playqueue *create(bool);
|
||||
void remove(unsigned int);
|
||||
Playqueue *get(unsigned int);
|
||||
unsigned int size();
|
||||
void move(unsigned int, unsigned int);
|
||||
void move(Playqueue *, unsigned int);
|
||||
unsigned int next();
|
||||
Playqueue *get_library_pq();
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void reset();
|
||||
void print_info();
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
};
|
||||
|
||||
#endif /* OCARINA_DECK_H */
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_ERROR_H
|
||||
#define OCARINA_ERROR_H
|
||||
|
||||
enum o_error {
|
||||
E_AUDIO = 1, /* Audio error */
|
||||
E_EXIST, /* Requested object does not exist */
|
||||
E_INVAL, /* Invalid operation requested */
|
||||
};
|
||||
|
||||
#endif /* OCARINA_ERROR_H */
|
|
@ -4,12 +4,14 @@
|
|||
#ifndef OCARINA_H
|
||||
#define OCARINA_H
|
||||
|
||||
#include <playqueue.h>
|
||||
#include <core/queue.h>
|
||||
#include <gtkmm.h>
|
||||
|
||||
|
||||
/* collection_mgr.cpp */
|
||||
void collection_mgr_init();
|
||||
void collection_mgr_init2();
|
||||
void collection_mgr_update();
|
||||
|
||||
|
||||
/* main.cpp */
|
||||
|
@ -18,11 +20,11 @@ Gtk::Window *ocarina_init(int *, char ***);
|
|||
|
||||
|
||||
/* model.cpp */
|
||||
class PlayqueueModel : public Gtk::TreeModel, public Glib::Object {
|
||||
class QueueModel : public Gtk::TreeModel, public Glib::Object {
|
||||
private:
|
||||
void increment_stamp();
|
||||
void get_value_uint(struct library::Song &, int, Glib::ValueBase &) const;
|
||||
void get_value_str(struct library::Song &, int, Glib::ValueBase &) const;
|
||||
void get_value_uint(Track *, int, Glib::ValueBase &) const;
|
||||
void get_value_str(Track *, int, Glib::ValueBase &) const;
|
||||
bool check_iter_validity(const Gtk::TreeIter &) const;
|
||||
|
||||
protected:
|
||||
|
@ -44,8 +46,8 @@ protected:
|
|||
|
||||
public:
|
||||
int stamp;
|
||||
Playqueue *queue;
|
||||
PlayqueueModel(Playqueue *);
|
||||
Queue *queue;
|
||||
QueueModel(Queue *);
|
||||
|
||||
void on_row_inserted(unsigned int);
|
||||
void on_row_deleted(unsigned int);
|
||||
|
@ -76,7 +78,8 @@ static Glib::RefPtr<T> get_object(const std::string &name)
|
|||
return Glib::RefPtr<T>::cast_static(get_builder()->get_object(name));
|
||||
}
|
||||
|
||||
|
||||
void on_pq_created(Queue *, unsigned int);
|
||||
void post_init_queue_tabs();
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void do_collection_delete();
|
|
@ -4,8 +4,8 @@
|
|||
#ifndef OCARINA_TABS_H
|
||||
#define OCARINA_TABS_H
|
||||
|
||||
#include <ocarina.h>
|
||||
#include <playqueue.h>
|
||||
#include <gui/ocarina.h>
|
||||
#include <core/queue.h>
|
||||
#include <set>
|
||||
|
||||
class Tab {
|
||||
|
@ -15,8 +15,8 @@ private:
|
|||
std::string tab_sorting_title;
|
||||
|
||||
protected:
|
||||
Playqueue *tab_pq;
|
||||
Glib::RefPtr<PlayqueueModel> tab_model;
|
||||
Queue *tab_pq;
|
||||
Glib::RefPtr<QueueModel> tab_model;
|
||||
Glib::RefPtr<Gtk::TreeModelFilter> tab_filter;
|
||||
|
||||
|
||||
|
@ -40,13 +40,13 @@ protected:
|
|||
*/
|
||||
void tab_init_random();
|
||||
void tab_init_repeat();
|
||||
void tab_toggle_button(Gtk::ToggleButton *, playqueue_flags);
|
||||
void tab_toggle_button(Gtk::ToggleButton *, queue_flags);
|
||||
void tab_dec_sort_count();
|
||||
virtual void tab_set_size();
|
||||
void tab_unmap();
|
||||
|
||||
public:
|
||||
Tab(Playqueue *);
|
||||
Tab(Queue *);
|
||||
virtual ~Tab();
|
||||
|
||||
/**
|
||||
|
@ -59,7 +59,7 @@ public:
|
|||
void tab_display_sorting();
|
||||
void tab_focus_search();
|
||||
void tab_selected_ids(std::vector<unsigned int> &);
|
||||
void tab_queue_add(Playqueue *);
|
||||
void tab_queue_add(Queue *);
|
||||
bool tab_queue_selected(bool);
|
||||
bool tab_add_to_queue(unsigned int);
|
||||
bool tab_add_to_playlist(const std::string &);
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
};
|
||||
|
||||
|
||||
Tab *find_tab(Playqueue *);
|
||||
Tab *find_tab(Queue *);
|
||||
void tab_focus_search();
|
||||
void init_tabs();
|
||||
void post_init_tabs();
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_LIBRARY_H
|
||||
#define OCARINA_LIBRARY_H
|
||||
|
||||
#include <database.h>
|
||||
#include <string>
|
||||
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/fileref.h>
|
||||
|
||||
struct ImportData;
|
||||
|
||||
namespace library
|
||||
{
|
||||
|
||||
enum DB_Type {
|
||||
DB_ALBUM,
|
||||
DB_ARTIST,
|
||||
DB_GENRE,
|
||||
DB_LIBRARY,
|
||||
DB_TRACK,
|
||||
};
|
||||
|
||||
|
||||
class AGInfo : public DatabaseEntry {
|
||||
public:
|
||||
DB_Type db_type;
|
||||
std :: string name;
|
||||
std :: string key_lower;
|
||||
|
||||
AGInfo();
|
||||
AGInfo(DB_Type, TagLib :: Tag *);
|
||||
AGInfo(DB_Type, const std::string &);
|
||||
const std::string primary_key();
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Album : public DatabaseEntry {
|
||||
public:
|
||||
std :: string name;
|
||||
std :: string name_lower;
|
||||
unsigned int year;
|
||||
|
||||
Album();
|
||||
Album(TagLib :: Tag *, unsigned int);
|
||||
Album(const std::string &, unsigned int, unsigned int);
|
||||
const std::string primary_key();
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Library : public DatabaseEntry {
|
||||
public:
|
||||
std::string root_path;
|
||||
unsigned int size;
|
||||
bool enabled;
|
||||
|
||||
Library();
|
||||
Library(const std::string &, bool);
|
||||
const std::string primary_key();
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
class Track : public DatabaseEntry {
|
||||
public:
|
||||
unsigned int library_id;
|
||||
unsigned int artist_id;
|
||||
unsigned int album_id;
|
||||
unsigned int genre_id;
|
||||
|
||||
unsigned int track;
|
||||
unsigned int last_year;
|
||||
unsigned int last_month;
|
||||
unsigned int last_day;
|
||||
unsigned int play_count;
|
||||
unsigned int length;
|
||||
|
||||
std :: string title;
|
||||
std :: string title_lower;
|
||||
std :: string length_str;
|
||||
std :: string filepath;
|
||||
std :: string full_path;
|
||||
|
||||
Track();
|
||||
Track(TagLib :: Tag *, TagLib :: AudioProperties *,
|
||||
unsigned int, unsigned int, unsigned int,
|
||||
unsigned int, const std :: string &);
|
||||
Track(ImportData *, unsigned int, unsigned int,
|
||||
unsigned int, unsigned int);
|
||||
const std::string primary_key();
|
||||
void read(File &);
|
||||
void write(File &);
|
||||
};
|
||||
|
||||
|
||||
struct Song {
|
||||
unsigned int track_id;
|
||||
library :: Album *album;
|
||||
library :: AGInfo *artist;
|
||||
library :: AGInfo *genre;
|
||||
library :: Library *library;
|
||||
library :: Track *track;
|
||||
};
|
||||
|
||||
|
||||
void init();
|
||||
void add_path(const std::string &);
|
||||
void del_path(unsigned int);
|
||||
void update_path(unsigned int);
|
||||
void update_all();
|
||||
void set_enabled(unsigned int, bool);
|
||||
void lookup(unsigned int, library :: Song *);
|
||||
Library *lookup_path(unsigned int);
|
||||
void import();
|
||||
void track_played(unsigned int);
|
||||
#ifdef CONFIG_TEST
|
||||
void print_db(DB_Type);
|
||||
void reset();
|
||||
#endif /* CONFIG_TEST */
|
||||
};
|
||||
|
||||
#endif /* OCARINA_LIBRARY_H */
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_PLAYLIST_H
|
||||
#define OCARINA_PLAYLIST_H
|
||||
|
||||
#include <playqueue.h>
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace playlist
|
||||
{
|
||||
|
||||
void init();
|
||||
void add(const std::string &, unsigned int);
|
||||
void del(const std::string &, unsigned int);
|
||||
void select(const std::string &);
|
||||
const std::set<unsigned int> &get_tracks(const std::string &);
|
||||
Playqueue *get_pq();
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void clear();
|
||||
#endif /* CONFIG_TEST */
|
||||
};
|
||||
|
||||
#endif /* OCARINA_PLAYLIST_H */
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_PLAYQUEUE_H
|
||||
#define OCARINA_PLAYQUEUE_H
|
||||
|
||||
#include <file.h>
|
||||
#include <library.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
enum playqueue_flags {
|
||||
PQ_ENABLED = (1 << 0),
|
||||
PQ_RANDOM = (1 << 1),
|
||||
PQ_REPEAT = (1 << 2),
|
||||
PQ_NEVER_SORT = (1 << 3),
|
||||
PQ_DISABLE_CHANGED_SIZE = (1 << 31),
|
||||
};
|
||||
|
||||
enum sort_t {
|
||||
SORT_ARTIST,
|
||||
SORT_ALBUM,
|
||||
SORT_COUNT,
|
||||
SORT_GENRE,
|
||||
SORT_LENGTH,
|
||||
SORT_PLAYED,
|
||||
SORT_TITLE,
|
||||
SORT_TRACK,
|
||||
SORT_YEAR,
|
||||
};
|
||||
|
||||
struct sort_info {
|
||||
sort_t field;
|
||||
bool ascending;
|
||||
};
|
||||
|
||||
class Playqueue {
|
||||
private:
|
||||
std :: vector <unsigned int> tracks;
|
||||
std :: list <sort_info> sort_order;
|
||||
unsigned int flags;
|
||||
unsigned int cur;
|
||||
unsigned int length;
|
||||
|
||||
unsigned int find_sorted_id(library :: Song &);
|
||||
void _add_sort(sort_t, bool);
|
||||
|
||||
public:
|
||||
Playqueue();
|
||||
Playqueue(playqueue_flags);
|
||||
~Playqueue();
|
||||
void write(File &);
|
||||
void read(File &);
|
||||
|
||||
void set_flag(playqueue_flags);
|
||||
void unset_flag(playqueue_flags);
|
||||
const unsigned int get_flags();
|
||||
unsigned int get_length();
|
||||
std::string get_length_str();
|
||||
|
||||
unsigned int add(unsigned int);
|
||||
unsigned int add_front(unsigned int);
|
||||
void del(unsigned int);
|
||||
void del_track(unsigned int);
|
||||
void track_updated(unsigned int);
|
||||
unsigned int size();
|
||||
void recalculate_length();
|
||||
|
||||
void add_sort(sort_t, bool ascending = true);
|
||||
void reset_sort(sort_t, bool ascending = true);
|
||||
void force_clear_sort();
|
||||
std::list <sort_info> &get_sort_order();
|
||||
|
||||
unsigned int operator[](unsigned int);
|
||||
unsigned int next();
|
||||
void set_cur(unsigned int);
|
||||
void path_selected(unsigned int);
|
||||
#ifdef CONFIG_TEST
|
||||
void reset();
|
||||
#endif /* CONFIG_TEST */
|
||||
};
|
||||
|
||||
#endif /* OCARINA_PLAYQUEUE_H */
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
Import("use_package")
|
||||
|
||||
use_package("gstreamer-1.0")
|
||||
use_package("taglib")
|
||||
|
||||
res = Glob("*.cpp")
|
||||
Return("res")
|
314
lib/audio.cpp
314
lib/audio.cpp
|
@ -1,314 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <callback.h>
|
||||
#include <deck.h>
|
||||
#include <library.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
static GstElement *ocarina_player;
|
||||
|
||||
static bool player_playing = false;
|
||||
static bool track_loaded = false;
|
||||
static unsigned int cur_trackid = 0;
|
||||
|
||||
static bool o_pause_enabled = false;
|
||||
static unsigned int o_pause_count = 0;
|
||||
static bool o_should_pause = false;
|
||||
|
||||
static Playqueue o_recently_played(PQ_ENABLED);
|
||||
static File f_cur_track("cur_track", FILE_TYPE_DATA);
|
||||
|
||||
static void parse_error(GstMessage *error)
|
||||
{
|
||||
GError *err;
|
||||
gchar *debug;
|
||||
library :: Song song;
|
||||
|
||||
library :: lookup(cur_trackid, &song);
|
||||
gst_message_parse_error(error, &err, &debug);
|
||||
g_print("Error playing file: %s\n", song.track->primary_key().c_str());
|
||||
g_print("Error: %s\n", err->message);
|
||||
g_error_free(err);
|
||||
g_free(debug);
|
||||
}
|
||||
|
||||
static void handle_pause_count()
|
||||
{
|
||||
if (o_pause_enabled == false)
|
||||
return;
|
||||
else if (o_pause_count == 0) {
|
||||
o_should_pause = true;
|
||||
o_pause_enabled = false;
|
||||
get_callbacks()->on_pause();
|
||||
} else
|
||||
o_pause_count--;
|
||||
get_callbacks()->on_pause_count_changed(o_pause_enabled, o_pause_count);
|
||||
}
|
||||
|
||||
static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data)
|
||||
{
|
||||
switch (GST_MESSAGE_TYPE(message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
parse_error(message);
|
||||
audio :: next();
|
||||
audio :: play();
|
||||
break;
|
||||
case GST_MESSAGE_EOS:
|
||||
handle_pause_count();
|
||||
library :: track_played(cur_trackid);
|
||||
audio :: next();
|
||||
audio :: seek_to(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool change_state(GstState state)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state(GST_ELEMENT(ocarina_player), state);
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_SUCCESS:
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
player_playing = (state == GST_STATE_PLAYING);
|
||||
return true;
|
||||
default:
|
||||
player_playing = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void save_state()
|
||||
{
|
||||
f_cur_track.open(OPEN_WRITE);
|
||||
f_cur_track << cur_trackid << std::endl;
|
||||
f_cur_track.close();
|
||||
}
|
||||
|
||||
static bool load_song(library :: Song &song)
|
||||
{
|
||||
GstState state;
|
||||
gchar *uri;
|
||||
std::string filepath = song.library->root_path + "/" + song.track->filepath;
|
||||
|
||||
if (o_should_pause == true) {
|
||||
state = GST_STATE_PAUSED;
|
||||
o_should_pause = false;
|
||||
} else {
|
||||
gst_element_get_state(GST_ELEMENT(ocarina_player), &state,
|
||||
NULL, GST_CLOCK_TIME_NONE);
|
||||
}
|
||||
|
||||
change_state(GST_STATE_NULL);
|
||||
uri = gst_filename_to_uri(filepath.c_str(), NULL);
|
||||
g_object_set(G_OBJECT(ocarina_player), "uri", uri, NULL);
|
||||
g_free(uri);
|
||||
|
||||
get_callbacks()->on_track_loaded(song);
|
||||
return change_state(state);
|
||||
}
|
||||
|
||||
void audio :: init(int *argc, char ***argv)
|
||||
{
|
||||
GstBus *bus;
|
||||
|
||||
o_recently_played.set_flag(PQ_REPEAT);
|
||||
o_recently_played.set_flag(PQ_NEVER_SORT);
|
||||
o_recently_played.set_flag(PQ_DISABLE_CHANGED_SIZE);
|
||||
gst_init(argc, argv);
|
||||
|
||||
ocarina_player = gst_element_factory_make("playbin", "ocarina_player");
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(ocarina_player));
|
||||
|
||||
gst_bus_add_watch(bus, on_message, NULL);
|
||||
}
|
||||
|
||||
void audio :: load_state()
|
||||
{
|
||||
unsigned int id;
|
||||
if (f_cur_track.exists()) {
|
||||
f_cur_track.open(OPEN_READ);
|
||||
f_cur_track >> id;
|
||||
f_cur_track.close();
|
||||
audio :: load_trackid(id);
|
||||
}
|
||||
}
|
||||
|
||||
void audio :: quit()
|
||||
{
|
||||
change_state(GST_STATE_NULL);
|
||||
gst_deinit();
|
||||
}
|
||||
|
||||
void audio :: play()
|
||||
{
|
||||
if (track_loaded == false)
|
||||
return;
|
||||
if (change_state(GST_STATE_PLAYING))
|
||||
get_callbacks()->on_play();
|
||||
}
|
||||
|
||||
void audio :: pause()
|
||||
{
|
||||
if (track_loaded == false)
|
||||
return;
|
||||
if (change_state(GST_STATE_PAUSED))
|
||||
get_callbacks()->on_pause();
|
||||
}
|
||||
|
||||
void audio :: toggle_play()
|
||||
{
|
||||
if (player_playing == true)
|
||||
pause();
|
||||
else
|
||||
play();
|
||||
}
|
||||
|
||||
void audio :: stop()
|
||||
{
|
||||
pause();
|
||||
seek_to(0);
|
||||
}
|
||||
|
||||
void audio :: seek_to(long pos)
|
||||
{
|
||||
bool ret;
|
||||
if (track_loaded == false)
|
||||
return;
|
||||
ret = gst_element_seek_simple(GST_ELEMENT(ocarina_player),
|
||||
GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_FLUSH,
|
||||
pos);
|
||||
if (!ret)
|
||||
throw -E_AUDIO;
|
||||
}
|
||||
|
||||
void audio :: next()
|
||||
{
|
||||
library :: Song song;
|
||||
unsigned int id;
|
||||
|
||||
track_loaded = false;
|
||||
id = deck :: next();
|
||||
library :: lookup(id, &song);
|
||||
load_song(song);
|
||||
track_loaded = true;
|
||||
|
||||
cur_trackid = id;
|
||||
save_state();
|
||||
o_recently_played.del_track(id);
|
||||
o_recently_played.add_front(id);
|
||||
o_recently_played.set_cur(0);
|
||||
}
|
||||
|
||||
void audio :: previous()
|
||||
{
|
||||
library :: Song song;
|
||||
unsigned int id;
|
||||
|
||||
id = o_recently_played.next();
|
||||
if (id == cur_trackid)
|
||||
return;
|
||||
|
||||
library :: lookup(id, &song);
|
||||
load_song(song);
|
||||
cur_trackid = id;
|
||||
save_state();
|
||||
}
|
||||
|
||||
void audio :: load_trackid(unsigned int track_id)
|
||||
{
|
||||
library :: Song song;
|
||||
|
||||
if ((track_id == cur_trackid) && (track_loaded == true))
|
||||
return;
|
||||
|
||||
track_loaded = false;
|
||||
try {
|
||||
library :: lookup(track_id, &song);
|
||||
} catch (int err) {
|
||||
return;
|
||||
}
|
||||
load_song(song);
|
||||
track_loaded = true;
|
||||
|
||||
cur_trackid = track_id;
|
||||
save_state();
|
||||
o_recently_played.del_track(track_id);
|
||||
o_recently_played.add_front(track_id);
|
||||
o_recently_played.set_cur(0);
|
||||
}
|
||||
|
||||
unsigned int audio :: current_trackid()
|
||||
{
|
||||
if (track_loaded == false)
|
||||
throw -E_EXIST;
|
||||
return cur_trackid;
|
||||
}
|
||||
|
||||
Playqueue *audio :: get_recent_pq()
|
||||
{
|
||||
return &o_recently_played;
|
||||
}
|
||||
|
||||
long audio :: position()
|
||||
{
|
||||
long position;
|
||||
|
||||
if (track_loaded == false)
|
||||
return 0;
|
||||
if (!gst_element_query_position(GST_ELEMENT(ocarina_player), GST_FORMAT_TIME, &position))
|
||||
return 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
std::string audio :: position_str()
|
||||
{
|
||||
std::stringstream ss;
|
||||
long cur = position() / GST_SECOND;
|
||||
unsigned int minutes = cur / 60;
|
||||
unsigned int seconds = cur % 60;
|
||||
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
long audio :: duration()
|
||||
{
|
||||
long duration;
|
||||
|
||||
if (track_loaded == false)
|
||||
return 0;
|
||||
if (!gst_element_query_duration(ocarina_player, GST_FORMAT_TIME, &duration))
|
||||
return 0;
|
||||
return duration;
|
||||
}
|
||||
|
||||
void audio :: pause_after(bool enabled, unsigned int n)
|
||||
{
|
||||
if (n > o_pause_count)
|
||||
enabled = true;
|
||||
|
||||
o_pause_enabled = enabled;
|
||||
o_pause_count = n;
|
||||
get_callbacks()->on_pause_count_changed(enabled, n);
|
||||
}
|
||||
|
||||
bool audio :: pause_enabled()
|
||||
{
|
||||
return o_pause_enabled;
|
||||
}
|
||||
|
||||
unsigned int audio :: pause_count()
|
||||
{
|
||||
return o_pause_count;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
|
||||
|
||||
static void no_op() {}
|
||||
static void no_op(unsigned int) {}
|
||||
static void no_op(bool, unsigned int) {}
|
||||
static void no_op(unsigned int id, library :: Library *path) {}
|
||||
static void no_op(Playqueue *, unsigned int) {}
|
||||
static void no_op(Playqueue *) {}
|
||||
static void no_op(library :: Song &) {}
|
||||
|
||||
|
||||
static struct Callbacks callbacks = {
|
||||
.on_play = no_op,
|
||||
.on_pause = no_op,
|
||||
.on_track_loaded = no_op,
|
||||
.on_pause_count_changed = no_op,
|
||||
|
||||
.on_pq_created = no_op,
|
||||
.on_pq_removed = no_op,
|
||||
|
||||
.on_library_add = no_op,
|
||||
.on_library_update = no_op,
|
||||
.on_library_track_add = no_op,
|
||||
.on_library_track_del = no_op,
|
||||
.on_library_track_updated = no_op,
|
||||
.on_library_import_ban = no_op,
|
||||
|
||||
.on_playlist_ban = no_op,
|
||||
.on_playlist_unban = no_op,
|
||||
|
||||
.on_queue_track_add = no_op,
|
||||
.on_queue_track_del = no_op,
|
||||
.on_queue_track_changed = no_op,
|
||||
.on_queue_changed = no_op,
|
||||
};
|
||||
|
||||
|
||||
struct Callbacks *get_callbacks()
|
||||
{
|
||||
return &callbacks;
|
||||
}
|
241
lib/deck.cpp
241
lib/deck.cpp
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <deck.h>
|
||||
#include <error.h>
|
||||
#include <file.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
static std::list<Playqueue> playqueue_deck;
|
||||
static Playqueue library_playqueue(PQ_ENABLED);
|
||||
static File deck_file("deck", FILE_TYPE_DATA);
|
||||
|
||||
static void add_library_track(unsigned int id)
|
||||
{
|
||||
library_playqueue.add(id);
|
||||
}
|
||||
|
||||
static void del_library_track(unsigned int id)
|
||||
{
|
||||
library_playqueue.del_track(id);
|
||||
}
|
||||
|
||||
static void change_library_track(unsigned int id)
|
||||
{
|
||||
library_playqueue.track_updated(id);
|
||||
}
|
||||
|
||||
void deck :: init()
|
||||
{
|
||||
library_playqueue.set_flag(PQ_REPEAT);
|
||||
library_playqueue.set_flag(PQ_DISABLE_CHANGED_SIZE);
|
||||
library_playqueue.add_sort(SORT_ARTIST);
|
||||
library_playqueue.add_sort(SORT_YEAR);
|
||||
library_playqueue.add_sort(SORT_TRACK);
|
||||
read();
|
||||
|
||||
get_callbacks()->on_playlist_ban = del_library_track;
|
||||
get_callbacks()->on_playlist_unban = add_library_track;
|
||||
get_callbacks()->on_library_track_add = add_library_track;
|
||||
get_callbacks()->on_library_track_del = del_library_track;
|
||||
get_callbacks()->on_library_track_updated = change_library_track;
|
||||
get_callbacks()->on_queue_changed = write;
|
||||
}
|
||||
|
||||
void deck :: init2()
|
||||
{
|
||||
std::list<Playqueue>::iterator it;
|
||||
|
||||
library_playqueue.recalculate_length();
|
||||
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++)
|
||||
it->recalculate_length();
|
||||
}
|
||||
|
||||
void deck :: read()
|
||||
{
|
||||
unsigned int num;
|
||||
int random;
|
||||
unsigned int field;
|
||||
bool ascending;
|
||||
std::list<Playqueue>::iterator it;
|
||||
|
||||
if (!deck_file.exists())
|
||||
return;
|
||||
|
||||
deck_file.open(OPEN_READ);
|
||||
deck_file >> random >> num;
|
||||
library_playqueue.force_clear_sort();
|
||||
for (unsigned int i = 0; i < num; i++) {
|
||||
deck_file >> field >> ascending;
|
||||
if (i == 0)
|
||||
library_playqueue.reset_sort((sort_t)field, ascending);
|
||||
else
|
||||
library_playqueue.add_sort((sort_t)field, ascending);
|
||||
}
|
||||
|
||||
deck_file >> num;
|
||||
if (random)
|
||||
library_playqueue.set_flag(PQ_RANDOM);
|
||||
|
||||
playqueue_deck.resize(num);
|
||||
|
||||
num = 0;
|
||||
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) {
|
||||
it->read(deck_file);
|
||||
get_callbacks()->on_pq_created(&(*it), num);
|
||||
num++;
|
||||
}
|
||||
deck_file.close();
|
||||
}
|
||||
|
||||
void deck :: write()
|
||||
{
|
||||
std::list<Playqueue>::iterator it;
|
||||
std::list<sort_info>::iterator st;
|
||||
std::list<sort_info> sort_order;
|
||||
|
||||
deck_file.open(OPEN_WRITE);
|
||||
|
||||
/* Save library playqueue */
|
||||
sort_order = library_playqueue.get_sort_order();
|
||||
deck_file << (library_playqueue.get_flags() & PQ_RANDOM) << " ";
|
||||
deck_file << 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);
|
||||
deck_file << std::endl;
|
||||
}
|
||||
deck_file.close();
|
||||
}
|
||||
|
||||
Playqueue *deck :: create(bool random)
|
||||
{
|
||||
Playqueue *pq;
|
||||
playqueue_deck.push_back(Playqueue(PQ_ENABLED));
|
||||
pq = &playqueue_deck.back();
|
||||
if (random == true)
|
||||
pq->set_flag(PQ_RANDOM);
|
||||
get_callbacks()->on_pq_created(pq, playqueue_deck.size() - 1);
|
||||
return pq;
|
||||
}
|
||||
|
||||
void deck :: remove(unsigned int id)
|
||||
{
|
||||
std::list<Playqueue>::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();
|
||||
}
|
||||
|
||||
Playqueue *deck :: get(unsigned int id)
|
||||
{
|
||||
std::list<Playqueue>::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<Playqueue>::iterator it_old = playqueue_deck.begin();
|
||||
std::list<Playqueue>::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(Playqueue *pq, unsigned int new_pos)
|
||||
{
|
||||
unsigned int old_pos = 0;
|
||||
std::list<Playqueue>::iterator it_old = playqueue_deck.begin();
|
||||
std::list<Playqueue>::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);
|
||||
}
|
||||
|
||||
unsigned int deck :: next()
|
||||
{
|
||||
unsigned int id = 0;
|
||||
std::list<Playqueue>::iterator it;
|
||||
|
||||
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) {
|
||||
if (it->get_flags() & PQ_ENABLED) {
|
||||
if (it->size() == 0) {
|
||||
playqueue_deck.erase(it);
|
||||
get_callbacks()->on_pq_removed(&(*it));
|
||||
} else {
|
||||
id = it->next();
|
||||
if (it->size() == 0) {
|
||||
playqueue_deck.erase(it);
|
||||
get_callbacks()->on_pq_removed(&(*it));
|
||||
}
|
||||
}
|
||||
write();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return library_playqueue.next();
|
||||
}
|
||||
|
||||
Playqueue *deck :: get_library_pq()
|
||||
{
|
||||
return &library_playqueue;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
static void no_op() {}
|
||||
|
||||
void deck :: reset()
|
||||
{
|
||||
get_callbacks()->on_queue_changed = no_op;
|
||||
playqueue_deck.clear();
|
||||
library_playqueue.reset();
|
||||
}
|
||||
|
||||
void deck :: print_info()
|
||||
{
|
||||
unsigned int i = 0;
|
||||
std::list<Playqueue>::iterator it;
|
||||
|
||||
for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) {
|
||||
print("deck[%u] = Playqueue { size = %u, flags = %u }\n",
|
||||
i, it->size(), it->get_flags());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_TEST */
|
148
lib/file.cpp
148
lib/file.cpp
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <error.h>
|
||||
#include <file.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
#define OCARINA_DIR "ocarina-test"
|
||||
#elif CONFIG_DEBUG
|
||||
#define OCARINA_DIR "ocarina-debug"
|
||||
#else
|
||||
#define OCARINA_DIR "ocarina"
|
||||
#endif
|
||||
|
||||
File :: File(const std::string &path, FileLocHint file_hint)
|
||||
: mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION)
|
||||
{
|
||||
if (path.size() == 0)
|
||||
hint = FILE_TYPE_INVALID;
|
||||
|
||||
if (hint == FILE_TYPE_INVALID)
|
||||
filepath = "INVALID";
|
||||
else
|
||||
filepath = find_dir() + "/" + path;
|
||||
}
|
||||
|
||||
File :: ~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
const char *File :: get_filepath()
|
||||
{
|
||||
return filepath.c_str();
|
||||
}
|
||||
|
||||
const unsigned int File :: get_version()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
bool File :: exists()
|
||||
{
|
||||
return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS);
|
||||
}
|
||||
|
||||
std::string File :: find_dir()
|
||||
{
|
||||
std::string res;
|
||||
|
||||
if (hint == FILE_TYPE_DATA) {
|
||||
res = g_get_user_data_dir();
|
||||
res += "/";
|
||||
res += OCARINA_DIR;
|
||||
} else /* FILE_TYPE_LEGACY */ {
|
||||
res = g_get_home_dir();
|
||||
res += "/.";
|
||||
res += OCARINA_DIR;
|
||||
res += "/library";
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool File :: open_read()
|
||||
{
|
||||
if (!exists()) {
|
||||
dprint("ERROR: File does not exist\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fstream::open(filepath.c_str(), std::fstream::in);
|
||||
if (std::fstream::fail()) {
|
||||
dprint("ERROR: File could not be opened for reading\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mode = OPEN_READ;
|
||||
std::fstream::operator>>(version);
|
||||
getline();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File :: open_write()
|
||||
{
|
||||
if (hint == FILE_TYPE_LEGACY) {
|
||||
dprint("ERROR: Cannot write to legacy files\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string dir = find_dir();
|
||||
if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) {
|
||||
dprint("ERROR: Could not make directory\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fstream::open(filepath.c_str(), std::fstream::out);
|
||||
if (std::fstream::fail()) {
|
||||
dprint("ERROR: Could not open file for writing\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mode = OPEN_WRITE;
|
||||
std::fstream::operator<<(FILE_VERSION) << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File :: open(OpenMode m)
|
||||
{
|
||||
if (hint == FILE_TYPE_INVALID) {
|
||||
dprint("ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened\n");
|
||||
return false;
|
||||
} else if (m == NOT_OPEN) {
|
||||
dprint("ERROR: NOT_OPEN is not a legal OpenMode\n");
|
||||
return false;
|
||||
} else if (mode != NOT_OPEN) {
|
||||
dprint("ERROR: File is already open\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (m == OPEN_READ)
|
||||
return open_read();
|
||||
else /* m == OPEN_WRITE */
|
||||
return open_write();
|
||||
}
|
||||
|
||||
void File :: close()
|
||||
{
|
||||
if (mode != NOT_OPEN)
|
||||
std::fstream::close();
|
||||
mode = NOT_OPEN;
|
||||
}
|
||||
|
||||
std::string File :: getline()
|
||||
{
|
||||
char c;
|
||||
std::string res;
|
||||
|
||||
/* Ignore leading whitespace */
|
||||
while (peek() == ' ')
|
||||
read(&c, 1);
|
||||
|
||||
std::getline(*static_cast<std::fstream *>(this), res);
|
||||
return res;
|
||||
}
|
633
lib/library.cpp
633
lib/library.cpp
|
@ -1,633 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <filter.h>
|
||||
#include <idle.h>
|
||||
#include <library.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
static Database<library :: Album> album_db("album.db", false);
|
||||
static Database<library :: AGInfo> artist_db("artist.db", false);
|
||||
static Database<library :: AGInfo> genre_db("genre.db", false);
|
||||
static Database<library :: Track> track_db("track.db", false);
|
||||
static Database<library :: Library> library_db("library.db", false);
|
||||
|
||||
struct ImportData {
|
||||
std::string filepath;
|
||||
std::string title;
|
||||
unsigned int track;
|
||||
unsigned int last_day;
|
||||
unsigned int last_month;
|
||||
unsigned int last_year;
|
||||
unsigned int length;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* library :: Artist: Artist tag information
|
||||
*/
|
||||
|
||||
library :: AGInfo :: AGInfo()
|
||||
{
|
||||
}
|
||||
|
||||
library :: AGInfo :: AGInfo(DB_Type type, TagLib :: Tag *tag)
|
||||
: db_type(type)
|
||||
{
|
||||
if (db_type == DB_ARTIST)
|
||||
name = tag->artist().stripWhiteSpace().to8Bit(true);
|
||||
else if (db_type == DB_GENRE)
|
||||
name = tag->genre().stripWhiteSpace().to8Bit(true);
|
||||
else
|
||||
throw -E_INVAL;
|
||||
|
||||
key_lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
library :: AGInfo :: AGInfo(DB_Type type, const std::string &str)
|
||||
: db_type(type)
|
||||
{
|
||||
if ((db_type == DB_ARTIST) || (db_type == DB_GENRE)) {
|
||||
name = str;
|
||||
key_lower = filter :: lowercase(name);
|
||||
} else
|
||||
throw -E_INVAL;
|
||||
}
|
||||
|
||||
const std::string library :: AGInfo :: primary_key()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void library :: AGInfo :: read(File &f)
|
||||
{
|
||||
name = f.getline();
|
||||
key_lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
void library :: AGInfo :: write(File &f)
|
||||
{
|
||||
f << name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* library :: Album: Album tag information
|
||||
*/
|
||||
|
||||
library :: Album :: Album()
|
||||
: name(""), year(0)
|
||||
{
|
||||
}
|
||||
|
||||
library :: Album :: Album(TagLib :: Tag *tag, unsigned int artist)
|
||||
: name(tag->album().stripWhiteSpace().to8Bit(true)),
|
||||
year(tag->year())
|
||||
{
|
||||
name_lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
library :: Album :: Album(const std::string &str, unsigned int yr, unsigned int artist)
|
||||
: name(str), year(yr)
|
||||
{
|
||||
name_lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
const std::string library :: Album :: primary_key()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << name << "." << year;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void library :: Album :: read(File &f)
|
||||
{
|
||||
f >> year;
|
||||
name = f.getline();
|
||||
name_lower = filter :: lowercase(name);
|
||||
}
|
||||
|
||||
void library :: Album :: write(File &f)
|
||||
{
|
||||
f << year << " " << name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* library :: Library: Basic information about each directory in the library
|
||||
*/
|
||||
|
||||
library :: Library :: Library()
|
||||
: root_path(""), size(0), enabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
library :: Library :: Library(const std::string &path, bool is_enabled)
|
||||
: root_path(path), size(0), enabled(is_enabled)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string library :: Library :: primary_key()
|
||||
{
|
||||
return root_path;
|
||||
}
|
||||
|
||||
void library :: Library :: read(File &f)
|
||||
{
|
||||
f >> enabled;
|
||||
root_path = f.getline();
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void library :: Library :: write(File &f)
|
||||
{
|
||||
f << enabled << " " << root_path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* library :: Track: Track tag information
|
||||
*/
|
||||
|
||||
library :: Track :: Track()
|
||||
: library_id(0), artist_id(0), album_id(0), genre_id(0)
|
||||
{
|
||||
}
|
||||
|
||||
library :: Track :: Track(TagLib :: Tag *tag, TagLib :: AudioProperties *audio,
|
||||
unsigned int lib, unsigned int artist, unsigned int album,
|
||||
unsigned int genre, const std :: string &path)
|
||||
: library_id(lib), artist_id(artist), album_id(album), genre_id(genre),
|
||||
track(tag->track()), last_year(0), last_month(0), last_day(0),
|
||||
play_count(0), length(audio->length()),
|
||||
title(tag->title().stripWhiteSpace().to8Bit(true))
|
||||
{
|
||||
std::stringstream ss;
|
||||
unsigned int minutes, seconds;
|
||||
|
||||
full_path = path;
|
||||
filepath = path.substr(library_db.at(library_id)->root_path.size() + 1);
|
||||
title_lower = filter :: lowercase(title);
|
||||
|
||||
minutes = length / 60;
|
||||
seconds = length % 60;
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
length_str = ss.str();
|
||||
}
|
||||
|
||||
library :: Track :: Track(struct ImportData *data, unsigned int lib,
|
||||
unsigned int artist, unsigned int album,
|
||||
unsigned int genre)
|
||||
: library_id(lib), artist_id(artist), album_id(album), genre_id(genre),
|
||||
track(data->track), last_year(data->last_year), last_month(data->last_month),
|
||||
last_day(data->last_day), play_count(data->count), length(data->length),
|
||||
title(data->title)
|
||||
{
|
||||
std::stringstream ss;
|
||||
unsigned int minutes, seconds;
|
||||
|
||||
full_path = data->filepath;
|
||||
filepath = full_path.substr(library_db.at(library_id)->root_path.size() + 1);
|
||||
title_lower = filter :: lowercase(title);
|
||||
|
||||
minutes = length / 60;
|
||||
seconds = length % 60;
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
length_str = ss.str();
|
||||
}
|
||||
|
||||
const std::string library :: Track :: primary_key()
|
||||
{
|
||||
return full_path;
|
||||
}
|
||||
|
||||
void library :: Track :: read(File &f)
|
||||
{
|
||||
std::stringstream ss;
|
||||
unsigned int minutes, seconds;
|
||||
|
||||
f >> library_id >> artist_id >> album_id >> genre_id;
|
||||
f >> track >> last_year >> last_month >> last_day;
|
||||
f >> play_count >> length;
|
||||
title = f.getline();
|
||||
filepath = f.getline();
|
||||
|
||||
title_lower = filter :: lowercase(title);
|
||||
full_path = library_db.at(library_id)->root_path + "/" + filepath;
|
||||
library_db.at(library_id)->size++;
|
||||
|
||||
minutes = length / 60;
|
||||
seconds = length % 60;
|
||||
ss << minutes << ":";
|
||||
if (seconds < 10)
|
||||
ss << "0";
|
||||
ss << seconds;
|
||||
length_str = ss.str();
|
||||
}
|
||||
|
||||
void library :: Track :: write(File &f)
|
||||
{
|
||||
f << library_id << " " << artist_id << " " << album_id << " " << genre_id;
|
||||
f << " " << track << " " << last_year << " " << last_month << " " << last_day;
|
||||
f << " " << play_count << " " << length << " "; // << std :: endl;
|
||||
f << title << std :: endl;
|
||||
f << filepath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Internal library functions
|
||||
*/
|
||||
struct scan_info {
|
||||
unsigned int lib_id;
|
||||
std :: string path;
|
||||
};
|
||||
static void do_scan_path(struct scan_info &);
|
||||
|
||||
static void read_tags(unsigned int lib_id, const std :: string &path)
|
||||
{
|
||||
TagLib :: Tag *tag;
|
||||
TagLib :: AudioProperties *audio;
|
||||
TagLib :: FileRef ref(path.c_str(), true, TagLib :: AudioProperties :: Fast);
|
||||
unsigned int artist_id, album_id, genre_id, track_id;
|
||||
|
||||
if (ref.isNull()) {
|
||||
print("ERROR: Could not read tags for file %s\n", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
tag = ref.tag();
|
||||
|
||||
audio = ref.audioProperties();
|
||||
|
||||
artist_id = artist_db.insert(library :: AGInfo(library :: DB_ARTIST, tag));
|
||||
album_id = album_db.insert(library :: Album(tag, artist_id));
|
||||
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, tag));
|
||||
track_id = track_db.insert(library :: Track(tag, audio, lib_id,
|
||||
artist_id, album_id, genre_id, path));
|
||||
|
||||
if (track_db.at(track_id)->valid == false)
|
||||
return;
|
||||
|
||||
library_db.at(lib_id)->size++;
|
||||
|
||||
filter::add(artist_db.at(artist_id)->name, track_id);
|
||||
filter::add(album_db.at(album_id)->name, track_id);
|
||||
filter::add(track_db.at(track_id)->title, track_id);
|
||||
get_callbacks()->on_library_track_add(track_id);
|
||||
}
|
||||
|
||||
static bool process_path(unsigned int lib_id, const std :: string &dir,
|
||||
const std :: string &name)
|
||||
{
|
||||
struct scan_info scan;
|
||||
bool changed = false;
|
||||
std :: string path = dir + "/" + name;
|
||||
|
||||
if (g_file_test(path.c_str(), G_FILE_TEST_IS_DIR) == true) {
|
||||
scan.lib_id = lib_id;
|
||||
scan.path = path;
|
||||
idle :: schedule (do_scan_path, scan);
|
||||
} else {
|
||||
if (track_db.find(path) == track_db.end()) {
|
||||
read_tags(lib_id, path);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void save_all_dbs()
|
||||
{
|
||||
artist_db.save();
|
||||
album_db.save();
|
||||
genre_db.save();
|
||||
track_db.save();
|
||||
}
|
||||
|
||||
static void do_scan_path(struct scan_info &scan)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *name;
|
||||
bool changed = false;
|
||||
|
||||
dir = g_dir_open(scan.path.c_str(), 0, NULL);
|
||||
if (dir == NULL)
|
||||
return;
|
||||
|
||||
name = g_dir_read_name(dir);
|
||||
while (name != NULL) {
|
||||
if (process_path(scan.lib_id, scan.path, name))
|
||||
changed = true;
|
||||
name = g_dir_read_name(dir);
|
||||
}
|
||||
|
||||
if (changed == true) {
|
||||
save_all_dbs();
|
||||
get_callbacks()->on_library_update(scan.lib_id,
|
||||
&(*library_db.at(scan.lib_id)));
|
||||
}
|
||||
}
|
||||
|
||||
static void do_validate_library(unsigned int &lib_id)
|
||||
{
|
||||
std :: string path;
|
||||
bool changed = false;
|
||||
|
||||
if (track_db.size() == 0)
|
||||
return;
|
||||
|
||||
Database<library :: Track>::iterator it;
|
||||
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||
if ((*it).library_id != lib_id)
|
||||
continue;
|
||||
|
||||
path = it->primary_key();
|
||||
if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS) == false) {
|
||||
dprint("Removing file: %s\n", path.c_str());
|
||||
track_db.remove(it - track_db.begin());
|
||||
library_db.at(lib_id)->size--;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed == true) {
|
||||
get_callbacks()->on_library_update(lib_id, &(*library_db.at(lib_id)));
|
||||
save_all_dbs();
|
||||
}
|
||||
}
|
||||
|
||||
static void do_update_library(unsigned int lib_id)
|
||||
{
|
||||
struct scan_info scan = { lib_id, library_db.at(lib_id)->root_path };
|
||||
idle :: schedule(do_validate_library, lib_id);
|
||||
idle :: schedule(do_scan_path, scan);
|
||||
}
|
||||
|
||||
static void do_import_track(File &f, unsigned int lib_id)
|
||||
{
|
||||
struct ImportData data;
|
||||
std::string artist, album, genre;
|
||||
unsigned int artist_id, album_id, genre_id, track_id, year, banned, tmp;
|
||||
|
||||
data.filepath = f.getline();
|
||||
data.title = f.getline();
|
||||
|
||||
artist = f.getline();
|
||||
album = f.getline();
|
||||
f.getline(); /* comment */
|
||||
genre = f.getline();
|
||||
f.getline(); /* lenstr */
|
||||
f >> tmp /* id */ >> year >> data.track >> data.count;
|
||||
f >> data.last_day >> data.last_month >> data.last_year >> data.length;
|
||||
f >> tmp >> tmp >>tmp >> banned; /* bitrate, sample, channels, banned */
|
||||
f.getline(); /* get rest of line */
|
||||
|
||||
artist_id = artist_db.insert(library :: AGInfo(library :: DB_ARTIST, artist));
|
||||
album_id = album_db.insert(library :: Album(album, year, artist_id));
|
||||
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, genre));
|
||||
track_id = track_db.insert(library :: Track(&data, lib_id, artist_id,
|
||||
album_id, genre_id));
|
||||
library_db.at(lib_id)->size++;
|
||||
|
||||
filter::add(artist_db.at(artist_id)->name, track_id);
|
||||
filter::add(album_db.at(album_id)->name, track_id);
|
||||
filter::add(track_db.at(track_id)->title, track_id);
|
||||
get_callbacks()->on_library_track_add(track_id);
|
||||
|
||||
if (banned == true)
|
||||
get_callbacks()->on_library_import_ban(track_id);
|
||||
}
|
||||
|
||||
static void do_import_library(std::string &s)
|
||||
{
|
||||
unsigned int id, next_id, size;
|
||||
std::string path;
|
||||
bool enabled;
|
||||
File f(s, FILE_TYPE_LEGACY);
|
||||
|
||||
print("Importing: %s\n", f.get_filepath());
|
||||
f.open(OPEN_READ);
|
||||
|
||||
if (f.get_version() != 2) {
|
||||
print("Version mismatch: %u != 2\n", f.get_version());
|
||||
return;
|
||||
}
|
||||
|
||||
path = f.getline();
|
||||
f >> id >> enabled >> next_id >> size;
|
||||
|
||||
/* Assign this path a new id */
|
||||
if (library_db.find(path) != library_db.end()) {
|
||||
print("Library already contains path: %s, skipping\n", path.c_str());
|
||||
return;
|
||||
}
|
||||
print("Adding path: %s\n", path.c_str());
|
||||
id = library_db.insert(library :: Library(path, enabled));
|
||||
get_callbacks()->on_library_add(id, &(*library_db.at(id)));
|
||||
library_db.save();
|
||||
|
||||
f.getline(); /* Get rest of line */
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
do_import_track(f, id);
|
||||
save_all_dbs();
|
||||
get_callbacks()->on_library_update(id, &(*library_db.at(id)));
|
||||
|
||||
library :: update_path(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* API used by the GUI begins here
|
||||
*/
|
||||
|
||||
void library :: init()
|
||||
{
|
||||
album_db.load();
|
||||
artist_db.load();
|
||||
genre_db.load();
|
||||
library_db.load();
|
||||
track_db.load();
|
||||
|
||||
Database<Track>::iterator it;
|
||||
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||
filter::add(artist_db.at((*it).artist_id)->name, it->id);
|
||||
filter::add(album_db.at((*it).album_id)->name, it->id);
|
||||
filter::add((*it).title, it->id);
|
||||
|
||||
if (library_db.at((*it).library_id)->enabled)
|
||||
get_callbacks()->on_library_track_add(it->id);
|
||||
}
|
||||
|
||||
Database<Library>::iterator l_it;
|
||||
for (l_it = library_db.begin(); l_it != library_db.end(); l_it = library_db.next(l_it))
|
||||
get_callbacks()->on_library_add(l_it->id, &(*library_db.at(l_it->id)));
|
||||
}
|
||||
|
||||
void library :: add_path(const std::string &dir)
|
||||
{
|
||||
unsigned int id;
|
||||
if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false)
|
||||
throw -E_INVAL;
|
||||
if (library_db.find(dir) != library_db.end())
|
||||
return;
|
||||
|
||||
id = library_db.insert(library :: Library(dir, true));
|
||||
library_db.save();
|
||||
|
||||
get_callbacks()->on_library_add(id, &(*library_db.at(id)));
|
||||
update_path(id);
|
||||
}
|
||||
|
||||
void library :: del_path(unsigned int id)
|
||||
{
|
||||
Database<Track>::iterator it;
|
||||
|
||||
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||
if ((*it).library_id == id) {
|
||||
get_callbacks()->on_library_track_del(it->id);
|
||||
track_db.remove(it->id);
|
||||
}
|
||||
}
|
||||
|
||||
library_db.remove(id);
|
||||
|
||||
track_db.save();
|
||||
library_db.save();
|
||||
}
|
||||
|
||||
void library :: update_path(unsigned int id)
|
||||
{
|
||||
if (id > library_db.size())
|
||||
return;
|
||||
if (library_db.at(id)->valid == false)
|
||||
return;
|
||||
do_update_library(id);
|
||||
}
|
||||
|
||||
void library :: update_all()
|
||||
{
|
||||
Database<Library>::iterator it;
|
||||
|
||||
for (it = library_db.begin(); it != library_db.end(); it = library_db.next(it))
|
||||
update_path(it - library_db.begin());
|
||||
}
|
||||
|
||||
void library :: set_enabled(unsigned int id, bool enabled)
|
||||
{
|
||||
unsigned int t;
|
||||
Database<Track>::iterator it;
|
||||
|
||||
library_db.at(id)->enabled = enabled;
|
||||
library_db.save();
|
||||
|
||||
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||
if ((*it).library_id == id) {
|
||||
t = it - track_db.begin();
|
||||
if (enabled)
|
||||
get_callbacks()->on_library_track_add(t);
|
||||
else
|
||||
get_callbacks()->on_library_track_del(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void library :: lookup(unsigned int id, library :: Song *song)
|
||||
{
|
||||
if (id >= track_db.actual_size())
|
||||
throw -E_EXIST;
|
||||
|
||||
song->track = &(*track_db.at(id));
|
||||
if (song->track->valid == false)
|
||||
throw -E_EXIST;
|
||||
|
||||
song->track_id = id;
|
||||
song->artist = &(*artist_db.at(song->track->artist_id));
|
||||
song->album = &(*album_db.at(song->track->album_id));
|
||||
song->genre = &(*genre_db.at(song->track->genre_id));
|
||||
song->library = &(*library_db.at(song->track->library_id));
|
||||
}
|
||||
|
||||
library :: Library *library :: lookup_path(unsigned int id)
|
||||
{
|
||||
if (id >= library_db.actual_size())
|
||||
throw -E_EXIST;
|
||||
if (library_db.at(id)->valid == false)
|
||||
throw -E_EXIST;
|
||||
return &(*library_db.at(id));
|
||||
}
|
||||
|
||||
void library :: import()
|
||||
{
|
||||
unsigned int i = 0;
|
||||
std::string name;
|
||||
|
||||
do {
|
||||
std::stringstream ss;
|
||||
ss << i;
|
||||
|
||||
name = ss.str();
|
||||
File f(name, FILE_TYPE_LEGACY);
|
||||
|
||||
if (f.exists() == false)
|
||||
break;
|
||||
|
||||
idle :: schedule(do_import_library, name);
|
||||
ss.clear();
|
||||
i++;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void library :: track_played(unsigned int id)
|
||||
{
|
||||
time_t the_time = time(NULL);
|
||||
struct tm *now = localtime(&the_time);
|
||||
|
||||
track_db.at(id)->play_count++;
|
||||
track_db.at(id)->last_day = now->tm_mday;
|
||||
track_db.at(id)->last_month = now->tm_mon + 1;
|
||||
track_db.at(id)->last_year = now->tm_year + 1900;
|
||||
|
||||
track_db.save();
|
||||
get_callbacks()->on_library_track_updated(id);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void library :: print_db(DB_Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case DB_ALBUM:
|
||||
break;
|
||||
case DB_ARTIST:
|
||||
break;
|
||||
case DB_GENRE:
|
||||
break;
|
||||
case DB_LIBRARY:
|
||||
break;
|
||||
case DB_TRACK:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void library :: reset()
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_TEST */
|
105
lib/playlist.cpp
105
lib/playlist.cpp
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <error.h>
|
||||
#include <index.h>
|
||||
#include <playlist.h>
|
||||
|
||||
static std::set<unsigned int> empty_set;
|
||||
static Index playlist_db("playlist.db", false);
|
||||
static Playqueue playlist_pq(PQ_ENABLED);
|
||||
static std::string cur_pq;
|
||||
|
||||
static void import_ban_track(unsigned int track_id)
|
||||
{
|
||||
playlist :: add("Banned", track_id);
|
||||
}
|
||||
|
||||
void playlist :: init()
|
||||
{
|
||||
std::set<unsigned int> ids;
|
||||
std::set<unsigned int>::iterator it;
|
||||
|
||||
playlist_pq.add_sort(SORT_ARTIST);
|
||||
playlist_pq.add_sort(SORT_YEAR);
|
||||
playlist_pq.add_sort(SORT_TRACK);
|
||||
playlist_pq.set_flag(PQ_REPEAT);
|
||||
playlist_pq.set_flag(PQ_NEVER_SORT);
|
||||
|
||||
get_callbacks()->on_library_import_ban = import_ban_track;
|
||||
|
||||
playlist_db.load();
|
||||
|
||||
ids = get_tracks("Banned");
|
||||
for (it = ids.begin(); it != ids.end(); it++)
|
||||
get_callbacks()->on_playlist_ban(*it);
|
||||
|
||||
if (cur_pq == "")
|
||||
return;
|
||||
|
||||
ids = get_tracks(cur_pq);
|
||||
for (it = ids.begin(); it != ids.end(); it++)
|
||||
playlist_pq.add(*it);
|
||||
}
|
||||
|
||||
void playlist :: add(const std::string &name, unsigned int track_id)
|
||||
{
|
||||
if ((name == "Banned") || (name == "Favorites")) {
|
||||
playlist_db.insert(name, track_id);
|
||||
playlist_db.save();
|
||||
if (name == cur_pq)
|
||||
playlist_pq.add(track_id);
|
||||
if (name == "Banned")
|
||||
get_callbacks()->on_playlist_ban(track_id);
|
||||
} else
|
||||
throw -E_EXIST;
|
||||
}
|
||||
|
||||
void playlist :: del(const std::string &name, unsigned int track_id)
|
||||
{
|
||||
if ((name == "Banned") || (name == "Favorites")) {
|
||||
playlist_db.remove(name, track_id);
|
||||
playlist_db.save();
|
||||
if (name == cur_pq)
|
||||
playlist_pq.del_track(track_id);
|
||||
if (name == "Banned")
|
||||
get_callbacks()->on_playlist_unban(track_id);
|
||||
} else
|
||||
throw -E_EXIST;
|
||||
}
|
||||
|
||||
void playlist :: select(const std::string &name)
|
||||
{
|
||||
std::set<unsigned int> ids = get_tracks(name);
|
||||
std::set<unsigned int>::iterator it;
|
||||
|
||||
while (playlist_pq.size() > 0)
|
||||
playlist_pq.del(0);
|
||||
|
||||
for (it = ids.begin(); it != ids.end(); it++)
|
||||
playlist_pq.add(*it);
|
||||
cur_pq = name;
|
||||
}
|
||||
|
||||
const std::set<unsigned int> &playlist :: get_tracks(const std::string &name)
|
||||
{
|
||||
if ((name == "Banned") || (name == "Favorites")) {
|
||||
Database<IndexEntry>::iterator it = playlist_db.find(name);
|
||||
if (it != playlist_db.end())
|
||||
return it->values;
|
||||
return empty_set;
|
||||
}
|
||||
throw -E_EXIST;
|
||||
}
|
||||
|
||||
Playqueue *playlist :: get_pq()
|
||||
{
|
||||
return &playlist_pq;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void playlist :: clear()
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_TEST */
|
|
@ -1,403 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <callback.h>
|
||||
#include <library.h>
|
||||
#include <playqueue.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* 0: lhs == rhs
|
||||
* < 0: lhs < rhs, or rhs is empty
|
||||
* > 0: lhs > rhs, or lhs is empty
|
||||
*/
|
||||
static inline int compare_string(const std::string &a, const std::string &b)
|
||||
{
|
||||
if (a.size() == 0)
|
||||
return 1;
|
||||
else if (b.size() == 0)
|
||||
return -1;
|
||||
return a.compare(b);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 compare_string(lhs.artist->key_lower, rhs.artist->key_lower);
|
||||
case SORT_ALBUM:
|
||||
return compare_string(lhs.album->name_lower, rhs.album->name_lower);
|
||||
case SORT_COUNT:
|
||||
return compare_uint(lhs.track->play_count, rhs.track->play_count);
|
||||
case SORT_GENRE:
|
||||
return compare_string(lhs.genre->key_lower, 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 compare_string(lhs.track->title_lower, rhs.track->title_lower);
|
||||
case SORT_TRACK:
|
||||
return compare_uint(lhs.track->track, rhs.track->track);
|
||||
default: //case SORT_YEAR
|
||||
int ret = compare_uint(lhs.album->year, rhs.album->year);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return compare_string(lhs.album->name_lower, rhs.album->name_lower);
|
||||
}
|
||||
}
|
||||
|
||||
static bool track_less_than(library :: Song &lhs, library :: Song &rhs,
|
||||
std::list<sort_info> &order)
|
||||
{
|
||||
std::list<sort_info>::iterator it;
|
||||
int res;
|
||||
|
||||
for (it = order.begin(); it != order.end(); it++) {
|
||||
if (it->ascending == true)
|
||||
res = track_compare(lhs, rhs, it->field);
|
||||
else
|
||||
res = track_compare(rhs, lhs, it->field);
|
||||
if (res != 0)
|
||||
return res < 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int Playqueue :: find_sorted_id(library :: Song &rhs)
|
||||
{
|
||||
library :: Song lhs;
|
||||
unsigned int begin = 0, end = (tracks.size() - 1), mid;
|
||||
|
||||
if (tracks.size() == 0)
|
||||
return 0;
|
||||
|
||||
while (end > begin) {
|
||||
mid = begin + ((end - begin) / 2);
|
||||
library :: lookup(tracks[mid], &lhs);
|
||||
if (track_less_than(lhs, rhs, sort_order))
|
||||
begin = mid + 1;
|
||||
else {
|
||||
if (mid == begin)
|
||||
return begin;
|
||||
else
|
||||
end = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
library :: lookup(tracks[begin], &lhs);
|
||||
if (track_less_than(lhs, rhs, sort_order))
|
||||
return begin + 1;
|
||||
return begin;
|
||||
}
|
||||
|
||||
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)
|
||||
id = find_sorted_id(song);
|
||||
|
||||
tracks.insert(tracks.begin() + id, 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++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Playqueue :: size()
|
||||
{
|
||||
return tracks.size();
|
||||
}
|
||||
|
||||
void Playqueue :: recalculate_length()
|
||||
{
|
||||
library :: Song song;
|
||||
|
||||
length = 0;
|
||||
for (unsigned int i = 0; i < size(); i++) {
|
||||
library::lookup(tracks[i], &song);
|
||||
length += song.track->length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sorting function */
|
||||
class SortTracks {
|
||||
private:
|
||||
std::list<sort_info> fields;
|
||||
public:
|
||||
SortTracks(std::list<sort_info> f) : fields(f) {}
|
||||
bool operator()(unsigned int a, unsigned int b)
|
||||
{
|
||||
library::Song lhs, rhs;
|
||||
library :: lookup(a, &lhs);
|
||||
library :: lookup(b, &rhs);
|
||||
return track_less_than(lhs, rhs, fields);
|
||||
}
|
||||
};
|
||||
|
||||
void Playqueue :: _add_sort(sort_t field, bool ascending)
|
||||
{
|
||||
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;
|
||||
info.ascending = ascending;
|
||||
sort_order.push_back(info);
|
||||
if (sort_order.size() >= 4)
|
||||
sort_order.erase(sort_order.begin());
|
||||
}
|
||||
|
||||
void Playqueue :: add_sort(sort_t field, bool ascending)
|
||||
{
|
||||
if (flags & PQ_NEVER_SORT)
|
||||
return;
|
||||
|
||||
_add_sort(field, ascending);
|
||||
std::stable_sort(tracks.begin(), tracks.end(), SortTracks(sort_order));
|
||||
|
||||
for (unsigned int i = 0; i < tracks.size(); i++)
|
||||
get_callbacks()->on_queue_track_changed(this, i);
|
||||
get_callbacks()->on_queue_changed();
|
||||
}
|
||||
|
||||
void Playqueue :: reset_sort(sort_t field, bool ascending)
|
||||
{
|
||||
if (flags & PQ_NEVER_SORT)
|
||||
return;
|
||||
if (sort_order.front().field != field)
|
||||
sort_order.clear();
|
||||
add_sort(field, ascending);
|
||||
}
|
||||
|
||||
void Playqueue :: force_clear_sort()
|
||||
{
|
||||
sort_order.clear();
|
||||
}
|
||||
|
||||
std::list<sort_info> &Playqueue :: get_sort_order()
|
||||
{
|
||||
return sort_order;
|
||||
}
|
||||
|
||||
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 :: set_cur(unsigned int c)
|
||||
{
|
||||
cur = c;
|
||||
}
|
||||
|
||||
void Playqueue :: path_selected(unsigned int id)
|
||||
{
|
||||
cur = id;
|
||||
if (!(flags &PQ_REPEAT)) {
|
||||
del(cur);
|
||||
cur--;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEST
|
||||
void Playqueue :: reset()
|
||||
{
|
||||
tracks.clear();
|
||||
set_cur(0);
|
||||
}
|
||||
#endif /* CONFIG_TEST */
|
|
@ -212,7 +212,7 @@
|
|||
<object class="GtkWindow" id="o_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Ocarina 6.0</property>
|
||||
<property name="title" translatable="yes">Ocarina 6.1</property>
|
||||
<property name="default_width">1024</property>
|
||||
<property name="default_height">683</property>
|
||||
<child>
|
||||
|
@ -1242,51 +1242,6 @@ Manager</property>
|
|||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="o_collection_import">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box14">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-convert</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Import</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
version
|
||||
file
|
||||
database
|
||||
index
|
||||
filter
|
||||
idle
|
||||
tags
|
||||
random
|
||||
queue
|
||||
library
|
||||
playlist
|
||||
deck
|
||||
driver
|
||||
audio
|
|
@ -0,0 +1,8 @@
|
|||
0
|
||||
6
|
||||
1 1993 Link's Awakening
|
||||
1 1998 Link's Awakening DX
|
||||
1 2002 Wind Waker
|
||||
1 2013 Wind Waker HD
|
||||
1 2006 Twilight Princess GC
|
||||
1 2006 Twilight Princess Wii
|
|
@ -0,0 +1,5 @@
|
|||
0
|
||||
3
|
||||
1 Link's Awakening
|
||||
1 Wind Waker
|
||||
1 Twilight Princess
|
|
@ -0,0 +1 @@
|
|||
3
|
|
@ -0,0 +1,4 @@
|
|||
0
|
||||
2 3 0 1 8 1 7 1 2
|
||||
1 4 0 1 2 3
|
||||
1 5 4 5 6 7 8
|
|
@ -0,0 +1,3 @@
|
|||
0
|
||||
1
|
||||
1 Zelda Music
|
|
@ -0,0 +1,3 @@
|
|||
0
|
||||
1
|
||||
1 1 /home/Zelda/Music
|
|
@ -0,0 +1,6 @@
|
|||
0
|
||||
2
|
||||
1 Banned
|
||||
4 0 1 2 3
|
||||
1 Favorites
|
||||
8 16 17 18 19 20 21 22 23
|
|
@ -0,0 +1,50 @@
|
|||
0
|
||||
24
|
||||
1 0 0 0 0 1 2001 1 10 0 100 Koholint Island
|
||||
Links Awakening/1 - Koholint Island.ogg
|
||||
1 0 0 0 0 2 2002 2 11 1 110 Animal Village
|
||||
Links Awakening/2 - Animal Village.ogg
|
||||
1 0 0 0 0 3 2003 3 12 2 120 Dream Shrine
|
||||
Links Awakening/3 - Dream Shrine.ogg
|
||||
1 0 0 0 0 4 2004 4 13 3 130 Goponga Swamp
|
||||
Links Awakening/4 - Goponga Swamp.ogg
|
||||
1 0 0 1 0 1 2003 5 14 4 140 Kanalet Castle
|
||||
Links Awakening/1 - Kanalet Castle.ogg
|
||||
1 0 0 1 0 2 2004 6 15 5 150 Mabe Village
|
||||
Links Awakening/2 - Mabe Village.ogg
|
||||
1 0 0 1 0 3 2005 7 16 6 160 Mt. Tamaranch
|
||||
Links Awakening/3 - Mt Tamaranch.ogg
|
||||
1 0 0 1 0 4 2006 8 17 7 170 Tal Tal Heights
|
||||
Links Awakening/4 - Tal Tal Heights.ogg
|
||||
1 0 1 2 0 1 2005 9 18 8 180 Dragon Roost Island
|
||||
Wind Waker/1 - Dragon Roost Island.ogg
|
||||
1 0 1 2 0 2 2006 1 19 9 190 Eastern Fairy Island
|
||||
Wind Waker/2 - Eastern Fairy Island.ogg
|
||||
1 0 1 2 0 3 2007 2 20 0 200 Forsaken Fortress
|
||||
Wind Waker/3 - Forsaken Fortress.ogg
|
||||
1 0 1 2 0 4 2008 3 21 1 210 Ghost Ship
|
||||
Wind Waker/4 - Ghost Ship.ogg
|
||||
1 0 1 3 0 1 2007 4 22 2 220 Outset Isle
|
||||
Wind Waker/1 - Outset Isle.ogg
|
||||
1 0 1 3 0 2 2008 5 23 3 230 Tingle Island
|
||||
Wind Waker/2 - Tingle Island.ogg
|
||||
1 0 1 3 0 3 2009 6 24 4 240 Tower of the Gods
|
||||
Wind Waker/3 - Tower of the Gods.ogg
|
||||
1 0 1 3 0 4 2010 7 25 5 250 Windfall Island
|
||||
Wind Waker/4 - Windfall Island.ogg
|
||||
1 0 2 4 0 1 2009 8 26 6 260 City in the Sky
|
||||
Twilight Princess/1 - City in the Sky.ogg
|
||||
1 0 2 4 0 2 2010 9 27 7 270 Gerudo Desert
|
||||
Twilight Princess/2 - Gerudo Desert.ogg
|
||||
1 0 2 4 0 3 2011 1 28 8 280 Arbiter's Grounds
|
||||
Twilight Princess/3 - Arbiters Grounds.ogg
|
||||
1 0 2 4 0 4 2012 2 29 9 290 Bridge of Eldin
|
||||
Twilight Princess/4 - Bridge of Eldin.ogg
|
||||
1 0 2 5 0 1 2011 3 30 0 300 Death Mountain
|
||||
Twilight Princess/1 - Death Mountain.ogg
|
||||
1 0 2 5 0 2 2012 4 10 1 310 Hidden Village
|
||||
Twilight Princess/2 - Hidden Village.ogg
|
||||
1 0 2 5 0 3 2013 5 11 2 320 Malo Mart
|
||||
Twilight Princess/3 - Malo Mart.ogg
|
||||
1 0 2 5 0 4 2014 6 12 3 330 Faron Woods
|
||||
Twilight Princess/4 - Faron Woods.ogg
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
107
tests/Sconscript
107
tests/Sconscript
|
@ -1,23 +1,100 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
Import("env")
|
||||
import sys, os
|
||||
Import("test_env")
|
||||
tests = [
|
||||
|
||||
if sys.argv.count("tests") > 0:
|
||||
env.Append( CCFLAGS = [ "-DCONFIG_TEST" ] )
|
||||
###
|
||||
#
|
||||
# (source.cpp, use collected lib_files?, [ other files ], [ other packages ])
|
||||
#
|
||||
|
||||
src = SConscript("src/Sconscript")
|
||||
("version.cpp", False, [], [ "glib-2.0" ]),
|
||||
("file.cpp", True, [], []),
|
||||
("database.cpp", True, [], []),
|
||||
("index.cpp", True, [], []),
|
||||
("filter.cpp", True, [], []),
|
||||
("idle.cpp", False, [ "idle.cpp" ], []),
|
||||
("tags.cpp", True, [], [ "taglib" ]),
|
||||
("random.cpp", False, [ "random.cpp" ], []),
|
||||
("queue.cpp", True, [ "callback.cpp", "random.cpp" ], []),
|
||||
("library.cpp", True, [ "idle.cpp" ], []),
|
||||
("playlist.cpp", True, [], []),
|
||||
("deck.cpp", True, [], []),
|
||||
("driver.cpp", False, [ "driver.cpp" ], []),
|
||||
("audio.cpp", True, [ "driver.cpp" ], []),
|
||||
|
||||
tests = [ "version", "file", "db_entry", "database", "index", "filter", "idle" ]
|
||||
#scripts = [ "playlist", "library", "playqueue", "deck", "audio", "gui" ]
|
||||
]
|
||||
|
||||
prev = None
|
||||
env = test_env
|
||||
env.UsePackage("glib-2.0")
|
||||
if env.Coverage == True:
|
||||
env.Append( CCFLAGS = [ "--coverage" ] )
|
||||
env.Append( LINKFLAGS = [ "-lgcov", "-coverage" ] )
|
||||
check_depends = True
|
||||
|
||||
for test in tests:
|
||||
t = Command("%s.out" % test, [], "./tests/%s" % test)
|
||||
for arg in sys.argv:
|
||||
if arg.find("tests") == 0 and len(arg) > 5:
|
||||
check_depends = False
|
||||
break
|
||||
|
||||
if prev:
|
||||
Depends(t, prev)
|
||||
Depends(t, src)
|
||||
AlwaysBuild(t)
|
||||
prev = t
|
||||
def expand_files(extra_files):
|
||||
res = []
|
||||
for f in extra_files:
|
||||
res += [ env.Object("%s-core" % f, "../core/%s" % f) ]
|
||||
return res
|
||||
|
||||
def make_program(src, name, extra_files):
|
||||
return env.Program("%s" % name, [ src ] + expand_files(extra_files))
|
||||
|
||||
def make_test(src, name):
|
||||
cmd = "./tests/%s | tee ./tests/%s.out" % (name, name)
|
||||
if env.Valgrind == True:
|
||||
cmd = "valgrind -q --leak-check=full --error-exitcode=42 %s" % cmd
|
||||
|
||||
test = Command("%s.out" % name, [], "set -o pipefail; %s" % cmd)
|
||||
Alias("tests/%s" % name, test)
|
||||
AlwaysBuild(test)
|
||||
return test
|
||||
|
||||
def prepare_test(name, src, extra_files):
|
||||
exe = make_program(src, name, extra_files)
|
||||
test = make_test(src, name)
|
||||
Depends(test, exe)
|
||||
return test
|
||||
|
||||
res = []
|
||||
lib_files = []
|
||||
|
||||
ignore = open(".gitignore", 'w')
|
||||
for src, lib, extra, pkgs in tests:
|
||||
name = "%s" % src.rsplit(".")[0]
|
||||
|
||||
if lib == True:
|
||||
lib_files += [ src ] + extra
|
||||
extra = lib_files
|
||||
|
||||
for p in pkgs:
|
||||
env.UsePackage(p)
|
||||
|
||||
test = prepare_test(name, src, extra)
|
||||
if (check_depends == True) and (len(res) > 0):
|
||||
Depends(test, res[len(res) - 1])
|
||||
|
||||
res += [ test ]
|
||||
ignore.write(name + "\n")
|
||||
ignore.close();
|
||||
|
||||
if env.Coverage == True:
|
||||
cov = Command("ocarina.gcov", [], "gcov -r tests/*.gcda")
|
||||
Depends(cov, res[len(res) - 1])
|
||||
res += [ cov ]
|
||||
|
||||
if env.CppCheck == True:
|
||||
check = Command("cpp.check", [], "cppcheck -q .")
|
||||
Depends(check, res[len(res) - 1])
|
||||
res += [ check ]
|
||||
|
||||
Return("res")
|
||||
|
||||
##scripts = [ "library", "deck", "audio", "gui" ]
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2014 (c) Anna Schumaker
|
||||
|
||||
function read_config
|
||||
{
|
||||
cat ../Sconstruct | grep ^$1 | awk -F= '{print $2}' | tr -d ' '
|
||||
}
|
||||
|
||||
function config_version
|
||||
{
|
||||
read_config CONFIG_VERSION
|
||||
}
|
||||
|
||||
function config_debug
|
||||
{
|
||||
read_config CONFIG_DEBUG
|
||||
}
|
||||
|
||||
CUR_TEST=0
|
||||
function new_test
|
||||
{
|
||||
echo "$1"
|
||||
CUR_TEST=0
|
||||
}
|
||||
|
||||
function start_test
|
||||
{
|
||||
echo -n " $CUR_TEST: "
|
||||
let CUR_TEST=($CUR_TEST + 1)
|
||||
}
|
||||
|
||||
function assert_equal
|
||||
{
|
||||
if [ "$1" == "$2" ]; then
|
||||
echo "Success!"
|
||||
return 0
|
||||
else
|
||||
echo "FAILED =("
|
||||
echo " Expected: $2"
|
||||
echo " Actual: $1"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function test_equal
|
||||
{
|
||||
start_test
|
||||
assert_equal "$($1)" "$2"
|
||||
}
|
||||
|
||||
function on_exit
|
||||
{
|
||||
ret=$?
|
||||
echo
|
||||
return $ret
|
||||
}; trap "on_exit" EXIT
|
||||
|
||||
[ -z $HOME ] && HOME=$(cat /etc/passwd | grep $(whoami) | awk -F: '{print $6}')
|
||||
[ -z $XDG_DATA_HOME] && XDG_DATA_HOME="$HOME/.local/share"
|
||||
DATA_DIR="$XDG_DATA_HOME/ocarina-test"
|
||||
LEGACY_DIR="$HOME/.ocarina-test/library"
|
||||
|
||||
rm -rf $DATA_DIR 2>/dev/null || true
|
||||
|
||||
set -e
|
||||
cd $(dirname $0)
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/driver.h>
|
||||
#include <core/library.h>
|
||||
#include "test.h"
|
||||
|
||||
Track *TRACK_NULL = NULL;
|
||||
|
||||
void test_pre_init()
|
||||
{
|
||||
TestDriver *driver = (TestDriver *)driver :: get_driver();
|
||||
|
||||
test_equal(audio :: current_track(), TRACK_NULL);
|
||||
|
||||
audio :: play();
|
||||
test_equal(driver->playing, false);
|
||||
|
||||
driver->playing = true;
|
||||
audio :: pause();
|
||||
test_equal(driver->playing, true);
|
||||
audio :: stop();
|
||||
test_equal(driver->playing, true);
|
||||
driver->playing = false;
|
||||
|
||||
audio :: seek_to(4242);
|
||||
test_equal(driver->position(), (long)0);
|
||||
|
||||
driver->cur_pos = 4242;
|
||||
test_equal(audio :: position(), (long)0);
|
||||
driver->cur_pos = 0;
|
||||
|
||||
driver->cur_duration = 4242;
|
||||
test_equal(audio :: duration(), (long)0);
|
||||
driver->cur_duration = 0;
|
||||
|
||||
audio :: next();
|
||||
test_equal(audio :: current_track(), TRACK_NULL);
|
||||
|
||||
audio :: prev();
|
||||
test_equal(audio :: current_track(), TRACK_NULL);
|
||||
}
|
||||
|
||||
void test_init(int argc, char **argv)
|
||||
{
|
||||
Track *track;
|
||||
|
||||
test :: cp_data_dir();
|
||||
audio :: init(&argc, &argv);
|
||||
|
||||
track = audio :: current_track();
|
||||
test_equal(track, TRACK_NULL);
|
||||
|
||||
tagdb :: init();
|
||||
library :: init();
|
||||
audio :: init(&argc, &argv);
|
||||
|
||||
track = audio :: current_track();
|
||||
test_not_equal(track, TRACK_NULL);
|
||||
}
|
||||
|
||||
void test_playback_controls()
|
||||
{
|
||||
TestDriver *driver = (TestDriver *)driver :: get_driver();
|
||||
|
||||
audio :: play();
|
||||
test_equal(driver->playing, true);
|
||||
|
||||
audio :: pause();
|
||||
test_equal(driver->playing, false);
|
||||
|
||||
audio :: seek_to(4242);
|
||||
test_equal(driver->cur_pos, (long)4242);
|
||||
test_equal(audio :: position(), (long)4242);
|
||||
|
||||
audio :: play();
|
||||
audio :: stop();
|
||||
test_equal(driver->playing, false);
|
||||
test_equal(driver->cur_pos, (long)0);
|
||||
|
||||
audio :: seek_to(4242);
|
||||
driver->cur_duration = 424242;
|
||||
test_equal(audio :: position(), (long)4242);
|
||||
test_equal(audio :: duration(), (long)424242);
|
||||
|
||||
audio :: seek_to(83 * O_SECOND);
|
||||
test_equal(audio :: position_str(), (std::string)"1:23");
|
||||
}
|
||||
|
||||
void test_track_controls()
|
||||
{
|
||||
Track *track = NULL;
|
||||
|
||||
TestDriver *driver = (TestDriver *)driver :: get_driver();
|
||||
library :: get_queue()->unset_flag(Q_RANDOM);
|
||||
|
||||
audio :: pause();
|
||||
audio :: next();
|
||||
test_not_equal(audio :: current_track()->id, (unsigned)2);
|
||||
test_equal(driver->is_playing(), false);
|
||||
|
||||
audio :: play();
|
||||
audio :: next();
|
||||
test_equal(driver->is_playing(), true);
|
||||
|
||||
audio :: load_track(track);
|
||||
test_not_equal(audio :: current_track(), track);
|
||||
|
||||
track = tagdb :: lookup(0);
|
||||
audio :: seek_to(4242);
|
||||
audio :: load_track(track);
|
||||
test_equal(driver->is_playing(), true);
|
||||
test_equal(audio :: position(), (long)0);
|
||||
|
||||
audio :: seek_to(4242);
|
||||
audio :: load_track(track);
|
||||
test_equal(driver->is_playing(), true);
|
||||
test_equal(audio :: position(), (long)4242);
|
||||
|
||||
driver->error();
|
||||
test_not_equal(audio :: current_track(), track);
|
||||
|
||||
track = audio :: current_track();
|
||||
driver->eos();
|
||||
test_not_equal(audio :: current_track(), track);
|
||||
}
|
||||
|
||||
void test_autopause()
|
||||
{
|
||||
TestDriver *driver = (TestDriver *)driver :: get_driver();
|
||||
|
||||
audio :: play();
|
||||
test_equal(audio :: pause_enabled(), false);
|
||||
test_equal(audio :: pause_count(), (unsigned)0);
|
||||
|
||||
audio :: pause_after(true, 3);
|
||||
test_equal(audio :: pause_enabled(), true);
|
||||
test_equal(audio :: pause_count(), (unsigned)3);
|
||||
|
||||
audio :: pause_after(false, 3);
|
||||
test_equal(audio :: pause_enabled(), false);
|
||||
test_equal(audio :: pause_count(), (unsigned)3);
|
||||
|
||||
audio :: pause_after(false, 5);
|
||||
test_equal(audio :: pause_enabled(), true);
|
||||
test_equal(audio :: pause_count(), (unsigned)5);
|
||||
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
driver->eos();
|
||||
test_equal(audio :: pause_enabled(), true);
|
||||
test_equal(audio :: pause_count(), (unsigned)i);
|
||||
test_equal(driver->is_playing(), true);
|
||||
}
|
||||
|
||||
driver->eos();
|
||||
test_equal(audio :: pause_enabled(), false);
|
||||
test_equal(audio :: pause_count(), (unsigned)0);
|
||||
test_equal(driver->is_playing(), false);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
run_test("Test Audio Pre-Init", test_pre_init);
|
||||
run_test("Test Audio Init", test_init, argc, argv);
|
||||
run_test("Test Audio Playback Controls", test_playback_controls);
|
||||
run_test("Test Audio Track Controls", test_track_controls);
|
||||
run_test("Test Audio Automatic Pausing", test_autopause);
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
Import("Test", "CONFIG")
|
||||
|
||||
CONFIG.AUDIO = True
|
||||
|
||||
Test("audio", "audio.cpp")
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <deck.h>
|
||||
#include <idle.h>
|
||||
#include <library.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
void gen_library()
|
||||
{
|
||||
system("tests/library/gen_library.sh");
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void check_ret(const std :: string &test, bool ret, bool expected)
|
||||
{
|
||||
print("Test %s: ", test.c_str());
|
||||
if (ret == expected)
|
||||
print("Success!\n");
|
||||
else
|
||||
print("Failed.\n");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void check_ret(const std :: string &test, long ret, long expected)
|
||||
{
|
||||
print("Test %s: ", test.c_str());
|
||||
if (ret == expected)
|
||||
print("Success!\n");
|
||||
else
|
||||
print("Failed (Expected %ld but got %ld)\n", expected, ret);
|
||||
}
|
||||
|
||||
void check_error(int error, int expected)
|
||||
{
|
||||
if (expected == 0) {
|
||||
if (error == 0)
|
||||
print("Success!\n");
|
||||
else
|
||||
print("Failed with error: %d\n", error);
|
||||
} else {
|
||||
if (error == 0)
|
||||
print("Failed (expected error: %d)\n", expected);
|
||||
else
|
||||
print("Success!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void call_func(const std :: string &test, void (*func)(), int expected)
|
||||
{
|
||||
print("Test %s: ", test.c_str());
|
||||
try {
|
||||
func();
|
||||
check_error(0, expected);
|
||||
} catch (int error) {
|
||||
check_error(error, expected);
|
||||
}
|
||||
}
|
||||
|
||||
void call_func(const std :: string &test, void (*func)(long), long arg, int expected)
|
||||
{
|
||||
print("Test %s: ", test.c_str());
|
||||
try {
|
||||
func(arg);
|
||||
check_error(0, expected);
|
||||
} catch (int error) {
|
||||
check_error(error, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call various functions without a track loaded */
|
||||
void test_0()
|
||||
{
|
||||
call_func("0a", audio :: play, 0);
|
||||
call_func("0b", audio :: pause, 0);
|
||||
call_func("0c", audio :: seek_to, 10, 0);
|
||||
call_func("0d", audio :: next, -E_EXIST);
|
||||
call_func("0e", audio :: stop, 0);
|
||||
check_ret("0f", audio :: position(), 0);
|
||||
check_ret("0g", audio :: duration(), 0);
|
||||
try {
|
||||
print("Test 0h: ");
|
||||
audio :: current_trackid();
|
||||
check_error(0, -E_EXIST);
|
||||
} catch (int error) {
|
||||
check_error(error, -E_EXIST);
|
||||
}
|
||||
call_func("0i", audio :: previous, -E_EXIST);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void test_1()
|
||||
{
|
||||
library :: Song song;
|
||||
library :: lookup(0, &song);
|
||||
|
||||
call_func("1a", audio :: next, 0);
|
||||
call_func("1b", audio :: play, 0);
|
||||
call_func("1c", audio :: pause, 0);
|
||||
call_func("1d", audio :: seek_to, 1 * GST_SECOND, 0);
|
||||
call_func("1e", audio :: stop, 0);
|
||||
check_ret("1f", audio :: current_trackid() == 0, true);
|
||||
check_ret("1g", audio :: position(), 0);
|
||||
call_func("1h", audio :: previous, 0);
|
||||
check_ret("1i", audio :: current_trackid() == 0, true);
|
||||
audio :: next();
|
||||
audio :: next();
|
||||
call_func("1j", audio :: previous, 0);
|
||||
check_ret("1k", audio :: current_trackid() == 1, true);
|
||||
call_func("1l", audio :: previous, 0);
|
||||
check_ret("1m", audio :: current_trackid() == 0, true);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
/* Test pause_after() */
|
||||
unsigned int test_2_count = 0;
|
||||
int test_2_cb(gpointer data)
|
||||
{
|
||||
long seek_pos, pos, max;
|
||||
library :: Song song;
|
||||
GMainLoop *loop = (GMainLoop *)data;
|
||||
|
||||
library :: lookup(audio :: current_trackid(), &song);
|
||||
pos = audio :: position();
|
||||
|
||||
switch (test_2_count) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
check_ret("2g", audio :: duration(), song.track->length * GST_SECOND);
|
||||
seek_pos = (song.track->length * GST_SECOND) - GST_SECOND;
|
||||
call_func("2h", audio :: seek_to, seek_pos, 0);
|
||||
break;
|
||||
case 2:
|
||||
max = (song.track->length * GST_SECOND) - GST_SECOND + (501 * GST_MSECOND);
|
||||
|
||||
check_ret("2i", pos <= max, true);
|
||||
call_func("2j", audio :: stop, 0);
|
||||
break;
|
||||
case 3:
|
||||
check_ret("2k", pos, 0);
|
||||
call_func("2l", audio :: play, 0);
|
||||
call_func("2m", audio :: seek_to, audio :: duration() - 1, 0);
|
||||
break;
|
||||
case 4:
|
||||
check_ret("2n", audio :: pause_count(), (long)2);
|
||||
call_func("2o", audio :: seek_to, audio :: duration() - 1, 0);
|
||||
break;
|
||||
case 5:
|
||||
check_ret("2p", audio :: pause_count(), (long)1);
|
||||
call_func("2q", audio :: seek_to, audio :: duration() - 1, 0);
|
||||
break;
|
||||
case 6:
|
||||
check_ret("2r", audio :: pause_count(), (long)0);
|
||||
call_func("2s", audio :: seek_to, audio :: duration() - 1, 0);
|
||||
break;
|
||||
case 7:
|
||||
check_ret("2t", audio :: pause_enabled(), false);
|
||||
check_ret("2u", audio :: pause_count(), (long)0);
|
||||
break;
|
||||
case 8:
|
||||
pos = audio :: position();
|
||||
check_ret("2v", (0 <= pos) && (pos <= GST_MSECOND), true);
|
||||
break;
|
||||
case 9:
|
||||
pos = audio :: position();
|
||||
check_ret("2w", (0 <= pos) && (pos <= GST_MSECOND), true);
|
||||
default:
|
||||
g_main_quit(loop);
|
||||
}
|
||||
|
||||
test_2_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_2()
|
||||
{
|
||||
GMainLoop *loop;
|
||||
|
||||
check_ret("2a", audio :: pause_enabled(), false);
|
||||
check_ret("2b", audio :: pause_count(), (long)0);
|
||||
audio :: pause_after(true, 3);
|
||||
check_ret("2c", audio :: pause_enabled(), true);
|
||||
check_ret("2d", audio :: pause_count(), (long)3);
|
||||
audio :: next();
|
||||
check_ret("2e", audio :: pause_enabled(), true);
|
||||
check_ret("2f", audio :: pause_count(), (long)3);
|
||||
|
||||
audio :: play();
|
||||
loop = g_main_loop_new(NULL, FALSE);
|
||||
g_timeout_add(500, test_2_cb, loop);
|
||||
g_main_loop_run(loop);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Playqueue *pqueue;
|
||||
|
||||
gen_library();
|
||||
|
||||
/* Initialize before testing */
|
||||
audio :: init(&argc, &argv);
|
||||
test_0();
|
||||
|
||||
/* Read in library, set up a playlist */
|
||||
library::init();
|
||||
deck::init();
|
||||
library :: reset();
|
||||
library :: add_path("/tmp/library/0");
|
||||
while (idle :: run_task());
|
||||
|
||||
pqueue = deck :: create(false);
|
||||
for (unsigned int i = 0; i < 150; i++)
|
||||
pqueue->add(i);
|
||||
|
||||
test_1();
|
||||
test_2();
|
||||
|
||||
audio :: quit();
|
||||
return 0;
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
Generating library: 0
|
||||
Generating library: 1
|
||||
Generating library: 2
|
||||
Generating library: 3
|
||||
Generating library: 4
|
||||
|
||||
Test 0a: Success!
|
||||
Test 0b: Success!
|
||||
Test 0c: Success!
|
||||
Test 0d: Success!
|
||||
Test 0e: Success!
|
||||
Test 0f: Success!
|
||||
Test 0g: Success!
|
||||
Test 0h: Success!
|
||||
Test 0i: Success!
|
||||
|
||||
Test 1a: Success!
|
||||
Test 1b: Success!
|
||||
Test 1c: Success!
|
||||
Test 1d: Success!
|
||||
Test 1e: Success!
|
||||
Test 1f: Success!
|
||||
Test 1g: Success!
|
||||
Test 1h: Success!
|
||||
Test 1i: Success!
|
||||
Test 1j: Success!
|
||||
Test 1k: Success!
|
||||
Test 1l: Success!
|
||||
Test 1m: Success!
|
||||
|
||||
Test 2a: Success!
|
||||
Test 2b: Success!
|
||||
Test 2c: Success!
|
||||
Test 2d: Success!
|
||||
Test 2e: Success!
|
||||
Test 2f: Success!
|
||||
Test 2g: Success!
|
||||
Test 2h: Success!
|
||||
Test 2i: Success!
|
||||
Test 2j: Success!
|
||||
Test 2k: Success!
|
||||
Test 2l: Success!
|
||||
Test 2m: Success!
|
||||
Test 2n: Success!
|
||||
Test 2o: Success!
|
||||
Test 2p: Success!
|
||||
Test 2q: Success!
|
||||
Test 2r: Success!
|
||||
Test 2s: Success!
|
||||
Test 2t: Success!
|
||||
Test 2u: Success!
|
||||
Test 2v: Success!
|
||||
Test 2w: Success!
|
|
@ -1,47 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2014 (c) Anna Schumaker
|
||||
|
||||
. $(dirname $0)/_functions
|
||||
|
||||
function test_autosave
|
||||
{
|
||||
new_test "Database Test (n = $1, autosave = true)"
|
||||
src/database.run -a $1
|
||||
if [ ! -f $DATA_DIR/database.db ] && [ $1 != 0 ]; then
|
||||
echo "ERROR: $DATA_DIR/database.db doesn't exist!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function test_noautosave
|
||||
{
|
||||
new_test "Database Test (n = $1, autosave = false)"
|
||||
src/database.run $1
|
||||
if [ -f $DATA_DIR/database.db ]; then
|
||||
echo "ERROR: $DATA_DIR/database.db exists!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function run_test
|
||||
{
|
||||
rm $DATA_DIR/* 2>/dev/null || true
|
||||
|
||||
if [ $1 -le 1000 ]; then
|
||||
test_autosave $1
|
||||
else
|
||||
test_noautosave $1
|
||||
fi
|
||||
}
|
||||
|
||||
run_test 0
|
||||
echo
|
||||
run_test 10
|
||||
echo
|
||||
run_test 100
|
||||
echo
|
||||
run_test 1000
|
||||
echo
|
||||
run_test 10000
|
||||
echo
|
||||
run_test 100000
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
* Test a Database
|
||||
*/
|
||||
|
||||
#include <core/database.h>
|
||||
#include "test.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
/***
|
||||
*
|
||||
* Derive a DatabaseEntry for storing ints
|
||||
*
|
||||
*/
|
||||
|
||||
class IntEntry : public DatabaseEntry {
|
||||
public:
|
||||
unsigned int val;
|
||||
|
||||
IntEntry();
|
||||
IntEntry(unsigned int);
|
||||
const std::string primary_key() const;
|
||||
void write(File &);
|
||||
void read(File &);
|
||||
};
|
||||
|
||||
IntEntry :: IntEntry() : val(0) {}
|
||||
IntEntry :: IntEntry(unsigned int v) : val(v) {}
|
||||
|
||||
const std::string IntEntry :: primary_key() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << val;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void IntEntry :: write(File &f) { f << val; }
|
||||
void IntEntry :: read(File &f) { f >> val; }
|
||||
|
||||
static IntEntry *INT_NULL = NULL;
|
||||
|
||||
|
||||
|
||||
/***
|
||||
*
|
||||
* Struct for passing around arguments to tests
|
||||
*
|
||||
*/
|
||||
|
||||
struct TestArgs {
|
||||
const unsigned int n;
|
||||
unsigned int size;
|
||||
unsigned int actual;
|
||||
bool autosave;
|
||||
|
||||
IntEntry *one;
|
||||
IntEntry *three;
|
||||
Database<IntEntry> *db;
|
||||
|
||||
TestArgs(const unsigned int, bool, Database<IntEntry> *);
|
||||
};
|
||||
|
||||
TestArgs :: TestArgs(const unsigned int _n, bool _asv, Database<IntEntry> *_db)
|
||||
: n(_n), size(0), actual(0), autosave(_asv),
|
||||
one(NULL), three(NULL), db(_db)
|
||||
{}
|
||||
|
||||
|
||||
/***
|
||||
*
|
||||
* Run tests with varying database sizes
|
||||
*
|
||||
*/
|
||||
|
||||
static void test_insertion(struct TestArgs *args)
|
||||
{
|
||||
/*
|
||||
* Initial size should be 0
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i++) {
|
||||
IntEntry *it = args->db->insert(IntEntry(i));
|
||||
check_not_equal(it, INT_NULL);
|
||||
check_equal(it->id, i);
|
||||
check_equal(it->val, i);
|
||||
|
||||
/*
|
||||
* Pointers should still be valid after more insertions.
|
||||
* These will be checked later
|
||||
*/
|
||||
if (i == 1)
|
||||
args->one = it;
|
||||
if (i == 3)
|
||||
args->three = it;
|
||||
|
||||
args->size++;
|
||||
args->actual++;
|
||||
}
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
}
|
||||
|
||||
static void test_insertion2(struct TestArgs *args)
|
||||
{
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i++) {
|
||||
IntEntry *it = args->db->insert(IntEntry(i));
|
||||
check_equal(it, INT_NULL);
|
||||
}
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should not change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
}
|
||||
|
||||
static void test_removal(struct TestArgs *args)
|
||||
{
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i+=2) {
|
||||
args->db->remove(i);
|
||||
args->size--;
|
||||
}
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
|
||||
/*
|
||||
* Test out-of-bounds removal
|
||||
*/
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < 10; i++)
|
||||
args->db->remove(args->n + i);
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should not change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
}
|
||||
|
||||
static void test_removal2(struct TestArgs *args)
|
||||
{
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i+=2)
|
||||
args->db->remove(i);
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should not change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
}
|
||||
|
||||
static void test_iterator(struct TestArgs *args)
|
||||
{
|
||||
unsigned int index = 1;
|
||||
Database<IntEntry>::iterator it;
|
||||
|
||||
test :: begin();
|
||||
for (it = args->db->begin(); it != args->db->end(); it = args->db->next(it)) {
|
||||
check_not_equal((*it), INT_NULL);
|
||||
check_equal((*it)->val, index);
|
||||
check_equal((*it)->id, index);
|
||||
index += 2;
|
||||
};
|
||||
test :: success();
|
||||
}
|
||||
|
||||
static void test_access(struct TestArgs *args)
|
||||
{
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i+=2) {
|
||||
IntEntry *it = args->db->at(i);
|
||||
check_equal(it, INT_NULL);
|
||||
}
|
||||
test :: success();
|
||||
|
||||
test :: begin();
|
||||
for (unsigned int i = 1; i < args->n; i+=2) {
|
||||
IntEntry *it = args->db->at(i);
|
||||
check_not_equal(it, INT_NULL);
|
||||
}
|
||||
test :: success();
|
||||
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < 10; i++) {
|
||||
IntEntry *it = args->db->at(args->n + i);
|
||||
check_equal(it, INT_NULL);
|
||||
}
|
||||
test :: success();
|
||||
}
|
||||
|
||||
static void test_pointers(struct TestArgs *args)
|
||||
{
|
||||
IntEntry *it = args->db->at(1);
|
||||
test_equal(it, args->one);
|
||||
|
||||
it = args->db->at(3);
|
||||
test_equal(it, args->three);
|
||||
}
|
||||
|
||||
static void test_insertion3(struct TestArgs *args)
|
||||
{
|
||||
test :: begin();
|
||||
for (unsigned int i = 0; i < args->n; i+=2) {
|
||||
IntEntry *it = args->db->insert(IntEntry(i));
|
||||
|
||||
args->size++;
|
||||
args->actual++;
|
||||
check_equal(it->id, args->n + (i / 2));
|
||||
}
|
||||
test :: success();
|
||||
|
||||
/*
|
||||
* Size should change
|
||||
*/
|
||||
test_equal(args->db->size(), args->size);
|
||||
test_equal(args->db->actual_size(), args->actual);
|
||||
}
|
||||
|
||||
static void test_autosave(struct TestArgs *args)
|
||||
{
|
||||
Database<IntEntry> db2("database.db", args->autosave);
|
||||
db2.load();
|
||||
|
||||
if (args->autosave == false) {
|
||||
test_equal(db2.size(), (unsigned)0);
|
||||
test_equal(db2.actual_size(), (unsigned)0);
|
||||
|
||||
args->db->save();
|
||||
db2.load();
|
||||
}
|
||||
|
||||
test_equal(db2.size(), args->db->size());
|
||||
test_equal(db2.actual_size(), args->db->actual_size());
|
||||
|
||||
Database<IntEntry>::iterator it1;
|
||||
Database<IntEntry>::iterator it2 = db2.begin();
|
||||
|
||||
test :: begin();
|
||||
for (it1 = args->db->begin(); it1 != args->db->end(); it1++) {
|
||||
if (*it1 == INT_NULL)
|
||||
check_equal(*it2, INT_NULL);
|
||||
else {
|
||||
check_not_equal(*it2, INT_NULL);
|
||||
check_equal((*it1)->id, (*it2)->id);
|
||||
check_equal((*it1)->val, (*it2)->val);
|
||||
}
|
||||
it2++;
|
||||
}
|
||||
test :: success();
|
||||
}
|
||||
|
||||
static void db_test(unsigned int n, bool autosave)
|
||||
{
|
||||
test :: rm_data_dir();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << " (n = " << n << ")";
|
||||
const std::string n_str = ss.str();
|
||||
|
||||
Database<IntEntry> db("database.db", autosave);
|
||||
struct TestArgs args(n, autosave, &db);
|
||||
|
||||
run_test("Database Insertion Test" + n_str, test_insertion, &args);
|
||||
run_test("Database Second Insertion Test" + n_str, test_insertion2, &args);
|
||||
run_test("Database Removal Test" + n_str, test_removal, &args);
|
||||
run_test("Database Second Removal Test" + n_str, test_removal2, &args);
|
||||
run_test("Database Iterator Test" + n_str, test_iterator, &args);
|
||||
run_test("Database Access Test" + n_str, test_access, &args);
|
||||
run_test("Database Pointer Test" + n_str, test_pointers, &args);
|
||||
run_test("Database Third Insertion Test" + n_str, test_insertion3, &args);
|
||||
run_test("Database Save and Load Test" + n_str, test_autosave, &args);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
db_test(0, true);
|
||||
db_test(10, true);
|
||||
db_test(100, true);
|
||||
db_test(1000, true);
|
||||
db_test(10000, false);
|
||||
db_test(100000, false);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2014 (c) Anna Schumaker
|
||||
|
||||
. $(dirname $0)/_functions
|
||||
|
||||
function test_entry
|
||||
{
|
||||
test_equal "./src/db_entry.run $1 $2" "$3"
|
||||
}
|
||||
|
||||
function test_print
|
||||
{
|
||||
test_entry $1 $2 "Value: $1 Key: $2 Valid: 0 Id: 0"
|
||||
}
|
||||
|
||||
function test_primary_key
|
||||
{
|
||||
test_entry "-p $1" $2 "Primary key: $2"
|
||||
}
|
||||
|
||||
function test_write
|
||||
{
|
||||
rm $DATA_DIR/db_entry.txt 2>/dev/null || true
|
||||
./src/db_entry.run -w $1 $2
|
||||
test_equal "tail -1 $DATA_DIR/db_entry.txt" "$1 $2"
|
||||
}
|
||||
|
||||
function test_read
|
||||
{
|
||||
rm $DATA_DIR/db_entry.txt 2>/dev/null || true
|
||||
echo 0 > $DATA_DIR/db_entry.txt
|
||||
echo $1 $2 >> $DATA_DIR/db_entry.txt
|
||||
test_entry "-r $1" $2 "Value: $1 Key: $2 Valid: 0 Id: 0"
|
||||
}
|
||||
|
||||
|
||||
new_test "Database Entry Print Test"
|
||||
test_print 1 1
|
||||
test_print 1 2
|
||||
test_print 2 1
|
||||
test_print 2 2
|
||||
|
||||
|
||||
echo
|
||||
new_test "Database Entry Primary Key Test"
|
||||
test_primary_key 1 1
|
||||
test_primary_key 1 2
|
||||
test_primary_key 2 1
|
||||
test_primary_key 2 2
|
||||
|
||||
|
||||
echo
|
||||
new_test "Database Entry Write Test"
|
||||
test_write 1 1
|
||||
test_write 1 2
|
||||
test_write 2 1
|
||||
test_write 2 2
|
||||
|
||||
|
||||
echo
|
||||
new_test "Database Entry Read Test"
|
||||
test_read 1 1
|
||||
test_read 1 2
|
||||
test_read 2 1
|
||||
test_read 2 2
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/callback.h>
|
||||
#include <core/deck.h>
|
||||
#include <core/library.h>
|
||||
#include "test.h"
|
||||
|
||||
static Queue *Q_NULL = NULL;
|
||||
static Track *TRACK_NULL = NULL;
|
||||
|
||||
static void test_init()
|
||||
{
|
||||
unsigned int val;
|
||||
File f("deck", 0);
|
||||
std::list<TempQueue>::iterator it;
|
||||
|
||||
test_equal(deck :: next(), TRACK_NULL);
|
||||
|
||||
test :: cp_data_dir();
|
||||
tagdb :: init();
|
||||
library :: init();
|
||||
deck :: init();
|
||||
|
||||
test_equal(library :: get_queue()->has_flag(Q_RANDOM), true);
|
||||
test_equal(deck :: get_queues().size(), (size_t)2);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
static unsigned int n = 0;
|
||||
static void on_queue_removed(Queue *queue)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
static void test_create_mv_destroy()
|
||||
{
|
||||
Queue *q1, *q2;
|
||||
|
||||
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);
|
||||
test_equal(deck :: get(2), q1);
|
||||
|
||||
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);
|
||||
test_equal(deck :: get(3), q2);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
test_equal(deck :: get(3), Q_NULL);
|
||||
}
|
||||
|
||||
static void test_next_prev()
|
||||
{
|
||||
std::list<TempQueue>::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 :: rm_data_dir();
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
Import("Test", "CONFIG")
|
||||
|
||||
CONFIG.DECK = True
|
||||
|
||||
Test("deck", "deck.cpp")
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <deck.h>
|
||||
#include <idle.h>
|
||||
#include <library.h>
|
||||
#include <print.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: ");
|
||||
|
||||
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()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
/* Test the next() function for playlists */
|
||||
void test_4()
|
||||
{
|
||||
print("Test 4:\n");
|
||||
|
||||
deck :: get(0)->unset_flag(PQ_ENABLED);
|
||||
deck :: get(1)->unset_flag(PQ_ENABLED);
|
||||
deck :: get(4)->unset_flag(PQ_ENABLED);
|
||||
|
||||
for (unsigned int i = 0; i < 40; i++) {
|
||||
print("Playing id: %u\n", deck :: next());
|
||||
if (i == 11 || i == 25)
|
||||
deck :: print_info();
|
||||
}
|
||||
|
||||
deck :: print_info();
|
||||
print("\n");
|
||||
}
|
||||
|
||||
/* Test load / save functions */
|
||||
void test_5()
|
||||
{
|
||||
print("Test 5:\n");
|
||||
|
||||
deck :: get(1)->set_flag(PQ_ENABLED);
|
||||
deck :: get(2)->set_flag(PQ_ENABLED);
|
||||
deck :: get(2)->set_flag(PQ_RANDOM);
|
||||
|
||||
print("Saving playqueue deck\n");
|
||||
deck :: write();
|
||||
|
||||
print("Clearing deck\n");
|
||||
deck :: reset();
|
||||
deck :: print_info();
|
||||
|
||||
print("Reading back playqueue deck\n");
|
||||
deck :: init();
|
||||
|
||||
deck :: print_info();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
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());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
Library size: 150
|
||||
Test 0:
|
||||
deck[0] = Playqueue { size = 10, flags = 1 }
|
||||
deck[1] = Playqueue { size = 11, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 13, flags = 1 }
|
||||
deck[4] = Playqueue { size = 14, flags = 1 }
|
||||
deck[5] = Playqueue { size = 15, flags = 1 }
|
||||
deck[6] = Playqueue { size = 16, flags = 1 }
|
||||
deck[7] = Playqueue { size = 17, flags = 1 }
|
||||
deck[8] = Playqueue { size = 18, flags = 1 }
|
||||
deck[9] = Playqueue { size = 19, flags = 1 }
|
||||
|
||||
Test 1:
|
||||
deck[0] = Playqueue { size = 10, flags = 1 }
|
||||
deck[1] = Playqueue { size = 12, flags = 1 }
|
||||
deck[2] = Playqueue { size = 14, flags = 1 }
|
||||
deck[3] = Playqueue { size = 15, flags = 1 }
|
||||
deck[4] = Playqueue { size = 16, flags = 1 }
|
||||
deck[5] = Playqueue { size = 19, flags = 1 }
|
||||
|
||||
Test 2: Playqueue { size = 15, flags = 1 }
|
||||
|
||||
Test 3:
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 10, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 14, flags = 1 }
|
||||
deck[4] = Playqueue { size = 15, flags = 1 }
|
||||
deck[5] = Playqueue { size = 19, flags = 1 }
|
||||
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 10, flags = 1 }
|
||||
deck[3] = Playqueue { size = 12, flags = 1 }
|
||||
deck[4] = Playqueue { size = 14, flags = 1 }
|
||||
deck[5] = Playqueue { size = 15, flags = 1 }
|
||||
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 14, flags = 1 }
|
||||
deck[4] = Playqueue { size = 15, flags = 1 }
|
||||
deck[5] = Playqueue { size = 10, flags = 1 }
|
||||
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 15, flags = 1 }
|
||||
deck[4] = Playqueue { size = 14, flags = 1 }
|
||||
deck[5] = Playqueue { size = 10, flags = 1 }
|
||||
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 14, flags = 1 }
|
||||
deck[4] = Playqueue { size = 15, flags = 1 }
|
||||
deck[5] = Playqueue { size = 10, flags = 1 }
|
||||
|
||||
deck[0] = Playqueue { size = 16, flags = 1 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 12, flags = 1 }
|
||||
deck[3] = Playqueue { size = 14, flags = 1 }
|
||||
deck[4] = Playqueue { size = 15, flags = 1 }
|
||||
deck[5] = Playqueue { size = 10, flags = 1 }
|
||||
|
||||
Test 4:
|
||||
Playing id: 0
|
||||
Playing id: 1
|
||||
Playing id: 2
|
||||
Playing id: 3
|
||||
Playing id: 4
|
||||
Playing id: 5
|
||||
Playing id: 6
|
||||
Playing id: 7
|
||||
Playing id: 8
|
||||
Playing id: 9
|
||||
Playing id: 10
|
||||
Playing id: 11
|
||||
deck[0] = Playqueue { size = 16, flags = 0 }
|
||||
deck[1] = Playqueue { size = 19, flags = 0 }
|
||||
deck[2] = Playqueue { size = 14, flags = 1 }
|
||||
deck[3] = Playqueue { size = 15, flags = 0 }
|
||||
deck[4] = Playqueue { size = 10, flags = 1 }
|
||||
Playing id: 0
|
||||
Playing id: 1
|
||||
Playing id: 2
|
||||
Playing id: 3
|
||||
Playing id: 4
|
||||
Playing id: 5
|
||||
Playing id: 6
|
||||
Playing id: 7
|
||||
Playing id: 8
|
||||
Playing id: 9
|
||||
Playing id: 10
|
||||
Playing id: 11
|
||||
Playing id: 12
|
||||
Playing id: 13
|
||||
deck[0] = Playqueue { size = 16, flags = 0 }
|
||||
deck[1] = Playqueue { size = 19, flags = 0 }
|
||||
deck[2] = Playqueue { size = 15, flags = 0 }
|
||||
deck[3] = Playqueue { size = 10, flags = 1 }
|
||||
Playing id: 0
|
||||
Playing id: 1
|
||||
Playing id: 2
|
||||
Playing id: 3
|
||||
Playing id: 4
|
||||
Playing id: 5
|
||||
Playing id: 6
|
||||
Playing id: 7
|
||||
Playing id: 8
|
||||
Playing id: 9
|
||||
Playing id: 149
|
||||
Playing id: 148
|
||||
Playing id: 147
|
||||
Playing id: 146
|
||||
deck[0] = Playqueue { size = 16, flags = 0 }
|
||||
deck[1] = Playqueue { size = 19, flags = 0 }
|
||||
deck[2] = Playqueue { size = 15, flags = 0 }
|
||||
|
||||
Test 5:
|
||||
Saving playqueue deck
|
||||
Clearing deck
|
||||
Reading back playqueue deck
|
||||
deck[0] = Playqueue { size = 16, flags = 0 }
|
||||
deck[1] = Playqueue { size = 19, flags = 1 }
|
||||
deck[2] = Playqueue { size = 15, flags = 3 }
|
||||
Disabling library path
|
||||
Library size: 0
|
||||
Enabling library path
|
||||
Library size: 150
|
||||
Deleting library path
|
||||
Library size: 0
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/driver.h>
|
||||
#include "test.h"
|
||||
|
||||
static TestDriver *DRIVER_NULL = NULL;
|
||||
static unsigned int eos_count = 0;
|
||||
static unsigned int error_count = 0;
|
||||
|
||||
void on_eos()
|
||||
{
|
||||
eos_count++;
|
||||
}
|
||||
|
||||
void on_error()
|
||||
{
|
||||
error_count++;
|
||||
}
|
||||
|
||||
void test_driver()
|
||||
{
|
||||
TestDriver *driver = (TestDriver *)driver :: get_driver();
|
||||
const std::string file = "/home/Zelda/Music/Wind Waker/1 - Outset Isle.ogg";
|
||||
|
||||
test_not_equal(driver, DRIVER_NULL);
|
||||
|
||||
driver->init(0, NULL, on_eos, on_error);
|
||||
|
||||
driver->load(file);
|
||||
test_equal(driver->cur_file, file);
|
||||
|
||||
test_equal(driver->play(), true);
|
||||
test_equal(driver->playing, true);
|
||||
test_equal(driver->is_playing(), true);
|
||||
|
||||
test_equal(driver->pause(), true);
|
||||
test_equal(driver->playing, false);
|
||||
test_equal(driver->is_playing(), false);
|
||||
|
||||
driver->seek_to(4242);
|
||||
test_equal(driver->cur_pos, (long)4242);
|
||||
test_equal(driver->position(), (long)4242);
|
||||
|
||||
driver->cur_duration = 424242;
|
||||
test_equal(driver->duration(), (long)424242);
|
||||
|
||||
driver->eos();
|
||||
test_equal(eos_count, (unsigned)1);
|
||||
|
||||
driver->error();
|
||||
test_equal(error_count, (unsigned)1);
|
||||
|
||||
driver->play();
|
||||
driver->seek_to(4242);
|
||||
driver->load(file);
|
||||
test_equal(driver->is_playing(), false);
|
||||
test_equal(driver->position(), (long)0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
run_test("Test Audio Driver", test_driver);
|
||||
return 0;
|
||||
}
|
117
tests/file
117
tests/file
|
@ -1,117 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2014 (c) Anna Schumaker
|
||||
|
||||
. $(dirname $0)/_functions
|
||||
|
||||
function test_file
|
||||
{
|
||||
test_equal "./src/file.run $1" "$2"
|
||||
}
|
||||
|
||||
function test_chmod
|
||||
{
|
||||
touch $2
|
||||
chmod $1 $2
|
||||
}
|
||||
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Test filepaths
|
||||
#
|
||||
|
||||
new_test "Filepath Test"
|
||||
|
||||
test_file -D INVALID
|
||||
test_file -L INVALID
|
||||
test_file "-D file.txt" "$DATA_DIR/file.txt"
|
||||
test_file "-L file.txt" "$LEGACY_DIR/file.txt"
|
||||
test_file "file.txt" INVALID
|
||||
test_file "-D -v file.txt" "0"
|
||||
test_file "-L -v file.txt" "0"
|
||||
|
||||
if [ -d $DATA_DIR ]; then
|
||||
echo "ERROR: $DATA_DIR should not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Test opening files
|
||||
#
|
||||
|
||||
echo
|
||||
new_test "File Open Test"
|
||||
|
||||
# Generic open testing
|
||||
test_file "-o N file.txt" "ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened"
|
||||
test_file "-D -o N file.txt" "ERROR: NOT_OPEN is not a legal OpenMode"
|
||||
test_file "-D -o W -O file.txt" "ERROR: File is already open" # This test creates a file
|
||||
test_file "-D -o R -O file.txt" "ERROR: File is already open"
|
||||
rm $DATA_DIR/*
|
||||
|
||||
# Test opening for read
|
||||
test_file "-D -o R file.txt" "ERROR: File does not exist"
|
||||
|
||||
test_chmod -r $DATA_DIR/file.txt
|
||||
test_file "-D -o R file.txt" "ERROR: File could not be opened for reading"
|
||||
rm -r $DATA_DIR
|
||||
|
||||
# Test opening for write
|
||||
test_file "-L -o W file.txt" "ERROR: Cannot write to legacy files"
|
||||
|
||||
touch $DATA_DIR
|
||||
test_file "-D -o W file.txt" "ERROR: Could not make directory"
|
||||
|
||||
rm $DATA_DIR
|
||||
mkdir -p $DATA_DIR
|
||||
test_chmod -w $DATA_DIR/file.txt
|
||||
test_file "-D -o W file.txt" "ERROR: Could not open file for writing"
|
||||
rm -rf $DATA_DIR
|
||||
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Test closing files
|
||||
#
|
||||
|
||||
echo
|
||||
new_test "File Close Test"
|
||||
test_file "-D -c file.txt" ""
|
||||
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Test FILE IO
|
||||
#
|
||||
|
||||
data="ABCDE FGHIJ KLMNO PQRST UVWXYZ"
|
||||
echo
|
||||
new_test "File IO Test"
|
||||
|
||||
# Write to file
|
||||
./src/file.run -D -w file.txt "$data"
|
||||
start_test
|
||||
assert_equal "$(cat $DATA_DIR/file.txt)" "0
|
||||
$data"
|
||||
|
||||
# Read data back from file
|
||||
test_file "-D -r file.txt" "ABCDE
|
||||
FGHIJ
|
||||
KLMNO
|
||||
PQRST
|
||||
UVWXYZ"
|
||||
|
||||
# Write different data to file
|
||||
./src/file.run -D -w file.txt " $data"
|
||||
start_test
|
||||
assert_equal "$(cat $DATA_DIR/file.txt)" "0
|
||||
$data"
|
||||
|
||||
# Read data back in a single line
|
||||
test_file "-D -r -g file.txt" "$data"
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/file.h>
|
||||
#include "test.h"
|
||||
|
||||
|
||||
static void test_filepath()
|
||||
{
|
||||
File a("", 0);
|
||||
File b("file.txt", 0);
|
||||
|
||||
test_equal(a.get_version(), (unsigned)0);
|
||||
test_equal((std::string)a.get_filepath(), (std::string)"");
|
||||
|
||||
test_equal(b.get_version(), (unsigned)0);
|
||||
test_equal((std::string)b.get_filepath(), test :: data_file("file.txt"));
|
||||
|
||||
test_equal(a.exists(), false);
|
||||
test_equal(b.exists(), false);
|
||||
test_equal(test :: data_dir_exists(), false);
|
||||
}
|
||||
|
||||
static void test_open()
|
||||
{
|
||||
File a("", 0);
|
||||
|
||||
test_equal(a.open(OPEN_READ), false);
|
||||
test_equal(a.open(OPEN_WRITE), false);
|
||||
|
||||
File b("file.txt", 0);
|
||||
test_equal(b.open(NOT_OPEN), false);
|
||||
test_equal(b.open(OPEN_READ), false);
|
||||
test_equal(b.open(OPEN_WRITE), true);
|
||||
test_equal(b.open(OPEN_WRITE), false);
|
||||
b.close();
|
||||
|
||||
test_equal(test :: data_file_exists("file.txt"), true);
|
||||
}
|
||||
|
||||
static void test_io()
|
||||
{
|
||||
File a("file.txt", 1);
|
||||
|
||||
test_equal(a.open(OPEN_WRITE), true);
|
||||
a << "ABCDE FGHIJ KLMNO PQRST UVWXYZ" << std::endl;
|
||||
a.close();
|
||||
test_equal(a.exists(), true);
|
||||
|
||||
File b("file.txt", 0);
|
||||
std::string res;
|
||||
|
||||
test_equal(b.open(OPEN_READ), true);
|
||||
test_equal(b.get_version(), (unsigned)1);
|
||||
b >> res;
|
||||
test_equal(res, (std::string)"ABCDE");
|
||||
b >> res;
|
||||
test_equal(res, (std::string)"FGHIJ");
|
||||
res = b.getline();
|
||||
test_equal(res, (std::string)"KLMNO PQRST UVWXYZ");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test :: rm_data_dir();
|
||||
|
||||
run_test("File Constructor Test", test_filepath);
|
||||
run_test("File Open Test", test_open);
|
||||
run_test("File I/O Test", test_io);
|
||||
return 0;
|
||||
}
|
71
tests/filter
71
tests/filter
|
@ -1,71 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2014 (c) Anna Schumaker
|
||||
|
||||
. $(dirname $0)/_functions
|
||||
|
||||
function test_add
|
||||
{
|
||||
test_equal "./src/filter.run -a $1" "$2"
|
||||
}
|
||||
|
||||
function test_lowercase
|
||||
{
|
||||
test_equal "./src/filter.run -l $1" "$2"
|
||||
}
|
||||
|
||||
function test_text
|
||||
{
|
||||
test_add "$1" "$2"
|
||||
test_lowercase "$1" "$2"
|
||||
}
|
||||
|
||||
function test_search
|
||||
{
|
||||
num=$(cat -b $DATA_DIR/filter.txt | tail -n 1 | awk '{print $1}')
|
||||
let num=$num-1
|
||||
test_equal "./src/filter.run -s $num $1" "$2"
|
||||
}
|
||||
|
||||
|
||||
|
||||
new_test "Filter Add and Lowercase Test"
|
||||
|
||||
test_text " " ""
|
||||
test_text " test
|
||||
text" "test text"
|
||||
test_text "test text" "test text"
|
||||
test_text "Test Text" "test text"
|
||||
test_text "Test? Text!" "test text"
|
||||
test_text "Test?123 Text!456" "test123 text456"
|
||||
test_text "Test?123 Text!456" "test123 text456"
|
||||
test_text "Test(text);123-456" "test text 123 456"
|
||||
test_text "Test((text));;123--456" "test text 123 456"
|
||||
|
||||
|
||||
|
||||
echo
|
||||
new_test "Filter Search Test"
|
||||
|
||||
file=$DATA_DIR/filter.txt
|
||||
mkdir -p $DATA_DIR
|
||||
|
||||
echo "0" > $file
|
||||
echo "It's dangerous to go alone! Take this..." >> $file
|
||||
echo "DODONGO DISLIKES SMOKE." >> $file
|
||||
echo "I am Error." >> $file
|
||||
echo "Error knows a secret." >> $file
|
||||
echo "Hey, you pay, then you can open the chests!" >> $file
|
||||
echo "And the Master Sword sleeps again... FOREVER!" >> $file
|
||||
echo "Link checked the chest. Wow! This is a nice chest!" >> $file
|
||||
echo "Hey! Listen! Hey! Listen! Watch out!" >> $file
|
||||
echo "You killed the Deku Tree? How could you?!" >> $file
|
||||
echo "You've met with a terrible fate, haven't you?" >> $file
|
||||
echo "Believe in your strengths... Believe..." >> $file
|
||||
echo "Tingle! Tingle! Kooloo-Limpah!" >> $file
|
||||
echo "Well excuse me, Princess!" >> $file
|
||||
|
||||
test_search "error" "2 3"
|
||||
test_search "the" "4 5 6 8"
|
||||
test_search "the ch" "4 6"
|
||||
test_search "the CH" "4 6"
|
||||
test_search "the ch y" "4"
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
* Test the filtering code
|
||||
*/
|
||||
|
||||
#include <core/filter.h>
|
||||
#include "test.h"
|
||||
|
||||
static void do_test_lowercase(const std::string &text, const std::string &lc)
|
||||
{
|
||||
test_equal(filter :: lowercase(text), lc);
|
||||
}
|
||||
|
||||
static void test_lowercase()
|
||||
{
|
||||
do_test_lowercase(" ", "");
|
||||
do_test_lowercase(" test \
|
||||
text", "test text");
|
||||
do_test_lowercase("test text", "test text");
|
||||
do_test_lowercase("Test Text", "test text");
|
||||
do_test_lowercase("Test? Text!", "test text");
|
||||
do_test_lowercase("Test?123 Text!456", "test123 text456");
|
||||
do_test_lowercase("Test?123 Text!456", "test123 text456");
|
||||
do_test_lowercase("Test(text);123-456", "test text 123 456");
|
||||
do_test_lowercase("Test((text));;123--456", "test text 123 456");
|
||||
}
|
||||
|
||||
static void do_test_add(const std::string &text, const std::string &lc)
|
||||
{
|
||||
static unsigned int i = 0;
|
||||
test_equal(filter :: add(text, i++), lc);
|
||||
}
|
||||
|
||||
static void test_add()
|
||||
{
|
||||
do_test_add("It's dangerous to go alone! Take this...",
|
||||
"its dangerous to go alone take this");
|
||||
do_test_add("DODONGO DISLIKES SMOKE.",
|
||||
"dodongo dislikes smoke");
|
||||
do_test_add("I am Error.",
|
||||
"i am error");
|
||||
do_test_add("Error knows a secret.",
|
||||
"error knows a secret");
|
||||
do_test_add("Hey, you pay, then you can open the chests!",
|
||||
"hey you pay then you can open the chests");
|
||||
do_test_add("And the Master Sword sleeps again... FOREVER!",
|
||||
"and the master sword sleeps again forever");
|
||||
do_test_add("Link checked the chest. Wow! This is a nice chest!",
|
||||
"link checked the chest wow this is a nice chest");
|
||||
do_test_add("Hey! Listen! Hey! Listen! Watch out!",
|
||||
"hey listen hey listen watch out");
|
||||
do_test_add("You killed the Deku Tree? How could you?!",
|
||||
"you killed the deku tree how could you");
|
||||
do_test_add("You've met with a terrible fate, haven't you?",
|
||||
"youve met with a terrible fate havent you");
|
||||
do_test_add("Believe in your strengths... Believe...",
|
||||
"believe in your strengths believe");
|
||||
do_test_add("Tingle! Tingle! Kooloo-Limpah!",
|
||||
"tingle tingle kooloo limpah");
|
||||
do_test_add("Well excuse me, Princess!",
|
||||
"well excuse me princess");
|
||||
}
|
||||
|
||||
static void do_test_search(const std::string &text, unsigned int len,
|
||||
unsigned int *ids)
|
||||
{
|
||||
std::set<unsigned int> res;
|
||||
std::set<unsigned int>::iterator it;
|
||||
|
||||
filter :: search(text, res);
|
||||
test_equal(res.size(), (size_t)len);
|
||||
|
||||
test :: begin();
|
||||
it = res.begin();
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
check_equal(*it, ids[i]);
|
||||
it++;
|
||||
}
|
||||
test :: success();
|
||||
}
|
||||
|
||||
static void test_search()
|
||||
{
|
||||
unsigned int res1[] = {2, 3};
|
||||
do_test_search("error", 2, res1);
|
||||
|
||||
unsigned int res2[] = {4, 5, 6, 8};
|
||||
do_test_search("the", 4, res2);
|
||||
|
||||
unsigned int res3[] = {4, 6};
|
||||
do_test_search("the ch", 2, res3);
|
||||
do_test_search("the CH", 2, res3);
|
||||
|
||||
unsigned int res4[] = {4};
|
||||
do_test_search("the ch y", 1, res4);
|
||||
|
||||
unsigned int res5[] = {};
|
||||
do_test_search("unknown terms", 0, res5);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
run_test("Filter Lowercase Test", test_lowercase);
|
||||
run_test("Filter Add Test", test_add);
|
||||
run_test("Filter Search Test", test_search);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2013 (c) Anna Schumaker.
|
||||
#
|
||||
# Generate a test library in /tmp
|
||||
#
|
||||
|
||||
# $1 - File, $2 - Directory number
|
||||
function tag_file()
|
||||
{
|
||||
artist="Artist $2"
|
||||
album="Album $2"
|
||||
let date=2008+$2
|
||||
vorbiscomment -w $1 -t "ARTIST=$artist" -t "ALBUM=$album" -t "DATE=$date"
|
||||
}
|
||||
|
||||
|
||||
mkdir -p /tmp/ocarina/dir{1..5}
|
||||
|
||||
for i in $(seq 5); do
|
||||
cp tests/Music/* /tmp/ocarina/dir$i/
|
||||
|
||||
for f in /tmp/ocarina/dir$i/*; do
|
||||
tag_file $f $i
|
||||
done
|
||||
done
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue