2013-05-13 23:13:54 -04:00
|
|
|
===============================================================================
|
|
|
|
= =
|
2014-10-31 08:16:38 -04:00
|
|
|
= Ocarina 6.3 =
|
2013-05-13 23:13:54 -04:00
|
|
|
= =
|
|
|
|
===============================================================================
|
|
|
|
|
2014-06-14 19:28:45 -04:00
|
|
|
Ocarina 6.2 is the 6th implementation of the Ocarina music player - a
|
2014-03-02 10:42:22 -05:00
|
|
|
lightweight, GTK+ based music player with all the features that I want.
|
|
|
|
Improvements over the 5.x series will include the existence of both a design
|
|
|
|
document (this file) and unit tests. This should make maintenance easier and
|
|
|
|
help me stay focused.
|
2013-05-13 23:13:54 -04:00
|
|
|
|
2014-06-14 19:28:45 -04:00
|
|
|
Ocarina 6.2 will use Gstreamer 1.0 for audio playback, GTK-MM 3 for user
|
2014-03-02 10:42:22 -05:00
|
|
|
interface development, and Taglib for extracting tags.
|
2013-05-26 11:08:41 -04:00
|
|
|
|
|
|
|
|
2013-07-04 18:03:15 -04:00
|
|
|
|
|
|
|
Install:
|
|
|
|
Ocarina will be compiled into a single executable placed under
|
|
|
|
/usr/bin/. Any extra files needed to run will be placed under
|
2014-03-02 10:42:22 -05:00
|
|
|
/usr/share/ocarina/.
|
2013-07-04 18:03:15 -04:00
|
|
|
|
|
|
|
|
2014-11-01 21:53:50 -04:00
|
|
|
Core Layers:
|
|
|
|
Ocarina was written with an "every file is a layer" strategy. This
|
|
|
|
means code can only call functions that reside in a lower layer.
|
|
|
|
From top to bottom, core layers are:
|
|
|
|
|
|
|
|
* Audio (include/core/audio.h)
|
|
|
|
* Audio driver (include/core/driver.h)
|
|
|
|
* Deck (include/core/deck.h)
|
|
|
|
* Playlist (include/core/playlist.h)
|
|
|
|
* Library (include/core/library.h)
|
|
|
|
* Queue (include/core/queue.h)
|
|
|
|
* RNG (include/core/random.h)
|
2014-12-02 08:24:36 -05:00
|
|
|
* Tags (include/core/tags/*.h)
|
2014-11-01 21:53:50 -04:00
|
|
|
* Idle queue (include/core/idle.h)
|
|
|
|
* Filter (include/core/filter.h)
|
|
|
|
* Index (include/core/index.h)
|
|
|
|
* Databases (include/core/database.h)
|
|
|
|
* File (include/core/file.h)
|
|
|
|
* Callbacks (include/core/callback.h)
|
|
|
|
* Version (include/core/version.h)
|
|
|
|
|
|
|
|
|
2013-07-04 18:03:15 -04:00
|
|
|
|
2014-03-07 20:49:29 -05:00
|
|
|
Callbacks:
|
2014-01-15 20:40:24 -05:00
|
|
|
Callbacks are used to notify a unit test or the gui that something in
|
|
|
|
the backend has changed. The callbacks structure should be initialized
|
|
|
|
with no-op default values and filled in by the user through the
|
|
|
|
get_callbacks() function.
|
|
|
|
|
2014-01-18 11:00:08 -05:00
|
|
|
|
2014-01-15 20:40:24 -05:00
|
|
|
- Callback functions:
|
|
|
|
struct Callbacks {
|
|
|
|
void (*on_library_add)(unsigned int, library :: Library *);
|
|
|
|
void (*on_library_update)(unsigned int, library :: Library *);
|
2014-01-18 11:00:08 -05:00
|
|
|
void (*on_library_track_add)();
|
2014-01-15 20:40:24 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct Callbacks callbacks;
|
|
|
|
|
|
|
|
- API:
|
|
|
|
struct Callbacks *get_callbacks();
|
|
|
|
Return the Callbacks structure;
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-16 21:12:18 -04:00
|
|
|
Track Tag:
|
|
|
|
The track tag is used to store information about a single track in the
|
|
|
|
user's music collection.
|
|
|
|
|
2014-03-22 20:06:36 -04:00
|
|
|
- Sorting:
|
|
|
|
enum sort_t {
|
|
|
|
SORT_ARTIST,
|
|
|
|
SORT_ALBUM,
|
|
|
|
SORT_COUNT,
|
|
|
|
SORT_GENRE,
|
|
|
|
SORT_LENGTH,
|
|
|
|
SORT_PLAYED,
|
|
|
|
SORT_TITLE,
|
|
|
|
SORT_TRACK,
|
|
|
|
SORT_YEAR,
|
|
|
|
};
|
|
|
|
|
2014-03-16 21:12:18 -04:00
|
|
|
- Track:
|
|
|
|
class Track : public DatabaseEntry {
|
2013-09-01 11:20:47 -04:00
|
|
|
public:
|
2014-03-16 21:12:18 -04:00
|
|
|
Library *library;
|
|
|
|
Artist *artist;
|
|
|
|
Album *album;
|
|
|
|
Genre *genre;
|
2013-06-27 16:20:38 -04:00
|
|
|
|
2013-11-23 13:21:17 -05:00
|
|
|
unsigned int track;
|
2014-03-16 21:12:18 -04:00
|
|
|
unsigned int length;
|
|
|
|
unsigned int play_count;
|
2013-11-23 13:21:17 -05:00
|
|
|
unsigned int last_year;
|
|
|
|
unsigned int last_month;
|
|
|
|
unsigned int last_day;
|
|
|
|
|
2014-03-16 21:12:18 -04:00
|
|
|
std :: string title;
|
|
|
|
std :: string title_lower;
|
|
|
|
std :: string filepath;
|
|
|
|
std :: string length_str;
|
|
|
|
|
|
|
|
Track();
|
2014-03-22 23:02:04 -04:00
|
|
|
Track(const std::string &, Library *);
|
2014-03-25 17:51:37 -04:00
|
|
|
const std::string primary_key() const;
|
2014-03-16 21:12:18 -04:00
|
|
|
void read(File &);
|
|
|
|
void write(File &);
|
2014-03-22 20:06:36 -04:00
|
|
|
|
2014-06-22 12:08:26 -04:00
|
|
|
bool tag();
|
2014-03-22 20:06:36 -04:00
|
|
|
const std::string path();
|
2014-05-23 21:52:09 -04:00
|
|
|
void played();
|
2014-03-22 23:02:04 -04:00
|
|
|
bool less_than(Track *, sort_t);
|
2013-06-27 16:20:38 -04:00
|
|
|
};
|
|
|
|
|
2014-03-16 21:12:18 -04:00
|
|
|
- File Format:
|
2014-03-22 10:52:28 -04:00
|
|
|
File << library->id << artist->id << album->id << genre->id << track;
|
|
|
|
File << last_year << last_month << last_day << play_count << length;
|
2013-06-27 22:41:03 -04:00
|
|
|
File << title << endl;
|
|
|
|
File << filepath << endl;
|
|
|
|
|
2014-03-16 21:12:18 -04:00
|
|
|
- API:
|
|
|
|
Track();
|
|
|
|
Initialize an invalid Track instance.
|
|
|
|
|
2014-03-22 10:52:28 -04:00
|
|
|
Track(const std::string &full_path, Library *lib);
|
|
|
|
This function will only set up the primary key, and the tag()
|
|
|
|
function must be called to find audio tags.
|
|
|
|
|
|
|
|
- Strip library path from the beginning of full_path and use
|
|
|
|
the result to set filepath.
|
|
|
|
- Set library = lib.
|
2014-03-16 21:12:18 -04:00
|
|
|
|
2014-03-25 17:51:37 -04:00
|
|
|
const std::string Track :: primary_key() const;
|
2014-03-16 21:12:18 -04:00
|
|
|
return path();
|
|
|
|
|
2014-03-22 20:06:36 -04:00
|
|
|
void read(File &f);
|
|
|
|
Read track information from file. Look up the corresponding
|
|
|
|
Library, Artist, Album and Genre pointers. Add title to the
|
|
|
|
filter and find the string version of length.
|
|
|
|
|
|
|
|
void write(File &f);
|
|
|
|
Write track information to file.
|
2014-03-16 21:12:18 -04:00
|
|
|
|
2014-06-22 12:08:26 -04:00
|
|
|
bool Track :: tag();
|
2014-03-22 10:52:28 -04:00
|
|
|
Use TagLib to find tags and audio properties for this file.
|
|
|
|
|
|
|
|
- Insert Artist, Album, and Genre into their databases and
|
|
|
|
set the corresponding pointers.
|
|
|
|
- Find title, track number, and length.
|
|
|
|
- Set play_count, last_year, last_month and last_day = 0.
|
|
|
|
- Set lowercase title and find the string form of length.
|
|
|
|
|
2014-06-22 12:08:26 -04:00
|
|
|
Return true if the track could be tagged and false otherwise.
|
|
|
|
|
2014-03-22 20:06:36 -04:00
|
|
|
const std::string Track :: path();
|
|
|
|
Combine library->path and filepath to find the full path to
|
|
|
|
the audio file.
|
2014-03-16 21:12:18 -04:00
|
|
|
|
2014-05-23 21:52:09 -04:00
|
|
|
void Track :: played();
|
|
|
|
Called when a track has been played. Increment the play count
|
|
|
|
and set last_day, last_month, and last_year to today's date.
|
|
|
|
|
|
|
|
Call tagdb :: commit() to save the track database.
|
|
|
|
|
2014-03-26 20:14:00 -04:00
|
|
|
int Track :: less_than(Track *rhs, sort_t field);
|
2014-03-22 20:06:36 -04:00
|
|
|
Compare the requested field for this Track instance to the same
|
2014-03-26 20:14:00 -04:00
|
|
|
field in the provided track.
|
|
|
|
|
|
|
|
Return -1 if this < rhs.
|
|
|
|
Return 0 if this == rhs.
|
|
|
|
Return 1 if this > rhs.
|
2014-03-16 21:12:18 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-04-01 20:11:57 -04:00
|
|
|
Queue:
|
|
|
|
Queues are lists of songs that the user has requested to play. They
|
|
|
|
are the main interface for all music played by Ocarina.
|
|
|
|
|
|
|
|
- Flags:
|
|
|
|
enum queue_flag {
|
|
|
|
Q_ENABLED (1 << 0),
|
|
|
|
Q_RANDOM (1 << 1),
|
|
|
|
Q_REPEAT (1 << 2),
|
|
|
|
Q_NO_SORT (1 << 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
- Sort info:
|
|
|
|
struct sort_info {
|
|
|
|
sort_t field;
|
|
|
|
bool ascending;
|
|
|
|
};
|
|
|
|
|
|
|
|
- Sorting:
|
|
|
|
Sorting is done using std::stable_sort() to make sure that orders won't
|
2014-04-13 12:26:32 -04:00
|
|
|
change unexpectedly.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
|
|
|
- Queue:
|
|
|
|
class Queue {
|
2014-04-13 12:26:32 -04:00
|
|
|
protected:
|
2014-04-01 20:11:57 -04:00
|
|
|
vector<Track *> _tracks;
|
|
|
|
list<sort_t> _sort_order;
|
|
|
|
unsigned int _cur;
|
|
|
|
unsigned int _flags;
|
|
|
|
unsigned int _length;
|
|
|
|
|
|
|
|
public:
|
2014-04-13 12:26:32 -04:00
|
|
|
Queue();
|
2014-04-01 20:11:57 -04:00
|
|
|
Queue(unsigned int);
|
|
|
|
void read(File &);
|
|
|
|
void write(File &);
|
|
|
|
|
|
|
|
void set_flag(queue_flag);
|
|
|
|
void unset_flag(queue_flag);
|
|
|
|
bool has_flag(queue_flag);
|
|
|
|
|
|
|
|
unsigned int add(Track *);
|
|
|
|
void del(Track *);
|
|
|
|
void del(unsigned int);
|
|
|
|
void updated(Track *);
|
2014-05-18 14:45:47 -04:00
|
|
|
Track *next();
|
2014-04-01 20:11:57 -04:00
|
|
|
|
|
|
|
unsigned int size();
|
|
|
|
const std::string size_str();
|
|
|
|
const std::string length_str();
|
|
|
|
|
2014-05-18 14:45:47 -04:00
|
|
|
void sort(sort_t, bool);
|
|
|
|
Track *operator[](unsigned int);
|
2014-04-01 20:11:57 -04:00
|
|
|
void track_selected(unsigned int);
|
|
|
|
};
|
|
|
|
|
|
|
|
File Format:
|
|
|
|
File << flags << tracks.size() << tracks[0] << tracks[1] << ... << tracks[N];
|
|
|
|
|
|
|
|
- API
|
2014-04-13 12:26:32 -04:00
|
|
|
Queue :: Queue();
|
|
|
|
Initialize _flags = 0, _cur = -1, _length = 0, and empty sort
|
|
|
|
order.
|
|
|
|
|
2014-04-01 20:11:57 -04:00
|
|
|
Queue :: Queue(unsigned int flags);
|
2014-04-13 12:26:32 -04:00
|
|
|
Initialize _flags = flags, _cur = -1, _length = 0, and empty
|
|
|
|
sort order.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
|
|
|
void Queue :: read(File &f);
|
|
|
|
Read queue from file.
|
|
|
|
|
|
|
|
void Queue :: write(File &f);
|
|
|
|
Write queue to file.
|
|
|
|
|
|
|
|
void Queue :: set_flag(queue_flag f);
|
|
|
|
Set the appropriate flag.
|
|
|
|
|
|
|
|
void Queue :: unset_flag(queue_flag f);
|
|
|
|
Unset the appropriate flag.
|
|
|
|
|
|
|
|
bool Queue :: has_flag(queue_flag f);
|
|
|
|
Return true if the queue has the flag enabled and false
|
|
|
|
otherwise.
|
|
|
|
|
|
|
|
unsigned int Queue :: add(Track *track);
|
|
|
|
Add a new track to the tracks vector and return the index.
|
|
|
|
Increase length by the length of the track.
|
|
|
|
|
|
|
|
void Queue :: del(Track *track);
|
|
|
|
Remove all instances of the requested track from the queue.
|
|
|
|
|
|
|
|
void Queue :: del(unsigned int queue_id);
|
|
|
|
Remove the track at the given index from the queue.
|
|
|
|
|
|
|
|
void Queue :: updated(Track *track);
|
|
|
|
Find all indexes of the updated track and notify the UI that
|
|
|
|
it has changed.
|
|
|
|
|
|
|
|
Track *Queue :: next();
|
|
|
|
Return the next track to play.
|
|
|
|
|
|
|
|
if (tracks.size() == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (flags & PL_RANDOM):
|
|
|
|
_cur += rand() % tracks.size();
|
|
|
|
else:
|
|
|
|
_cur += 1;
|
|
|
|
|
|
|
|
if (_cur >= tracks.size())
|
|
|
|
_cur -= tracks.size();
|
|
|
|
track = tracks[_cur];
|
|
|
|
|
|
|
|
if (!(flags & PL_REPEAT)):
|
|
|
|
del(_cur);
|
|
|
|
return track;
|
|
|
|
|
2014-05-18 14:45:47 -04:00
|
|
|
unsigned int Queue :: size();
|
|
|
|
Return the number of tracks currently on the queue.
|
|
|
|
|
|
|
|
const std::string Queue :: size_str();
|
|
|
|
Return the number of tracks currently on the queue, in string
|
|
|
|
form.
|
|
|
|
|
|
|
|
const std::string Queue :: length_str();
|
|
|
|
Return the remaining length of the queue in a human-readable
|
|
|
|
format.
|
|
|
|
|
|
|
|
void Queue :: sort(sort_t field, bool reset);
|
|
|
|
If the field is already in the sort order, toggle its
|
|
|
|
ascending value. Otherwise, add a new sort field to the end
|
|
|
|
of the sort order with ascending set to true. If reset is set
|
|
|
|
to true, clear the sorting list before appending.
|
|
|
|
|
|
|
|
Track *Queue :: operator[](unsigned int i);
|
|
|
|
Return the track and index i.
|
|
|
|
|
2014-04-01 20:11:57 -04:00
|
|
|
void Queue :: track_selected(unsigned int queue_id);
|
|
|
|
Set _cur to queue_id. If PQ_REPEAT is not set, remove the
|
|
|
|
track from the queue.
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-23 21:56:33 -04:00
|
|
|
Library:
|
|
|
|
The library is in charge of scanning and updating library paths added
|
|
|
|
to the tag database. In addition, the library layer is also in charge
|
|
|
|
of managing a library queue used by the UI. This queue has a special
|
|
|
|
file format, and will be saved to the file "library.q".
|
|
|
|
|
|
|
|
- Queue:
|
|
|
|
class LibraryQueue : public Queue {
|
|
|
|
public:
|
|
|
|
LibraryQueue();
|
|
|
|
save();
|
|
|
|
load();
|
|
|
|
set_flag(queue_flag);
|
|
|
|
unset_flag(queue_flag);
|
|
|
|
sort(sort_t, bool);
|
|
|
|
};
|
|
|
|
|
|
|
|
File << flags << _sort_order.size()
|
|
|
|
File << _sort_order[N].field << _sort_order[N].ascending << ...
|
|
|
|
|
|
|
|
- Validation:
|
|
|
|
Use a single idle function to loop over each track in the track
|
|
|
|
database. Check if the track still exists in the filesystem and remove
|
|
|
|
it from the tagdb if not.
|
|
|
|
|
|
|
|
- Updating:
|
|
|
|
Scan over all files in the current directory directory.
|
|
|
|
For each encountered directory:
|
|
|
|
Use the idle queue to call the update function with the new
|
|
|
|
directory as the "current" directory.
|
|
|
|
For each encountered file:
|
|
|
|
Attempt to add the file to the track_db.
|
|
|
|
Commit the database if at least one new file has been added.
|
|
|
|
|
|
|
|
- Testing:
|
|
|
|
The script tests/library/gen_library.sh will create a sample library
|
|
|
|
in the /tmp/ directory for testing purposes. All the track files are
|
|
|
|
complete silence, but the script will fake up tags for each file.
|
|
|
|
|
|
|
|
To test importing, create several mock library files and copy them to
|
|
|
|
~/.ocarina-test/library/ and attempt to read them in.
|
|
|
|
|
|
|
|
- LibraryQueue API:
|
|
|
|
LibraryQueue :: LibraryQueue();
|
|
|
|
Initialize a Queue with the flags Q_ENABLED and Q_REPEAT. The
|
|
|
|
default sorting order should be artist, year, track.
|
|
|
|
|
|
|
|
LibraryQueue :: save();
|
|
|
|
Write a library queue to disk.
|
|
|
|
|
|
|
|
LibraryQueue :: load();
|
|
|
|
Read a library queue from disk.
|
|
|
|
|
|
|
|
LibraryQueue :: set_flag(queue_flag f);
|
|
|
|
LibraryQueue :: unset_flag(queue_flag f);
|
|
|
|
LibraryQueue :: sort(sort_t field, bool reset);
|
|
|
|
These functions are wrappers around the default Queue
|
|
|
|
implementation. First call the original function, then use
|
|
|
|
save() to store the changes.
|
|
|
|
|
|
|
|
- API
|
|
|
|
void library :: init();
|
|
|
|
Scan the tagdb track list, and add each track to the library
|
|
|
|
queue.
|
|
|
|
|
2014-05-24 12:47:33 -04:00
|
|
|
Library *library :: add(string dir);
|
2014-05-23 21:56:33 -04:00
|
|
|
If dir is not a directory:
|
|
|
|
return NULL
|
|
|
|
|
|
|
|
Add a new path to the tag database, trigger an update, and
|
|
|
|
then return the corresponding Library tag to the caller.
|
|
|
|
|
2014-05-24 12:47:33 -04:00
|
|
|
void library :: remove(Library *library);
|
2014-05-23 21:56:33 -04:00
|
|
|
Invalidate a library_db row and all tracks owned by that path.
|
2014-05-24 12:47:33 -04:00
|
|
|
Do not use the library pointer after calling this function.
|
2014-05-23 21:56:33 -04:00
|
|
|
|
2014-05-24 12:47:33 -04:00
|
|
|
void library :: update(Library *library);
|
2014-05-23 21:56:33 -04:00
|
|
|
First, validate all tracks in the given library.
|
|
|
|
Next, trigger an update on the given library.
|
|
|
|
|
|
|
|
void library :: update_all();
|
|
|
|
Update all valid library paths.
|
|
|
|
|
2014-05-24 12:47:33 -04:00
|
|
|
void library :: set_enabled(Library *library, bool enabled);
|
2014-05-23 21:56:33 -04:00
|
|
|
Toggle if a library path is enabled or not. A disabled
|
|
|
|
library path will have its tracks removed from the
|
|
|
|
LibraryQueue.
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Queue *library :: get_queue();
|
2014-05-23 21:56:33 -04:00
|
|
|
Return the LibraryQueue to the caller.
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-18 21:09:46 -04:00
|
|
|
Playlist:
|
|
|
|
Playlists are a new feature in Ocarina 6 and are modeled after Gmail
|
2014-06-14 19:28:45 -04:00
|
|
|
labels. Ocarina 6.2 will support two different playlists that the
|
2014-05-18 21:09:46 -04:00
|
|
|
user can add tracks to: banned and favorites.
|
|
|
|
|
|
|
|
The playlist layer will maintain a queue that is used by the UI to
|
|
|
|
display tracks in a given playlist. This queue is inherited from
|
|
|
|
the base Queue class to provide extra features.
|
|
|
|
|
|
|
|
Future releases will add support for more playlists.
|
|
|
|
|
|
|
|
- Index:
|
|
|
|
Index playlist_db("playlist.db", true);
|
|
|
|
|
|
|
|
- Queue:
|
|
|
|
class PlaylistQueue : public Queue {
|
|
|
|
public:
|
|
|
|
PlaylistQueue();
|
|
|
|
fill(IndexEntry *);
|
|
|
|
};
|
|
|
|
|
|
|
|
- Default playlists:
|
|
|
|
Favorites:
|
|
|
|
The user will add music they really like to this playlist.
|
|
|
|
|
|
|
|
Banned:
|
|
|
|
The user should add music they do not like to this playlist.
|
|
|
|
Tracks should be removed from the Library playqueue when they
|
|
|
|
are banned and added back to the playqueue when they are
|
|
|
|
un-banned.
|
|
|
|
|
|
|
|
- PlaylistQueue API:
|
|
|
|
PlaylistQueue :: PlaylistQueue();
|
|
|
|
Initialize a Queue with the flags Q_ENABLED, Q_REPEAT, and
|
|
|
|
Q_NO_SORT set. Default sorting order should be artist, year,
|
|
|
|
track.
|
|
|
|
|
|
|
|
PlaylistQueue :: fill(IndexEntry *ent);
|
|
|
|
Remove all tracks in the queue and repopulate using ent.
|
|
|
|
|
|
|
|
- API
|
|
|
|
void playlist :: init():
|
2014-05-24 13:31:45 -04:00
|
|
|
Load the playlist index from file. Remove every banned song
|
|
|
|
from the LibraryQueue in the library layer.
|
2014-05-18 21:09:46 -04:00
|
|
|
|
|
|
|
void playlist :: add(Track *track, const std::string &name);
|
|
|
|
Add track->id to the playlist named "name" and return true.
|
|
|
|
Return false if the playlist does not exist.
|
|
|
|
|
|
|
|
If "name" is the currently selected playlist, add the track
|
|
|
|
to the PlaylistQueue.
|
|
|
|
|
2014-05-24 13:31:45 -04:00
|
|
|
If "name" is "Banned", remove the track from the LibraryQueue
|
|
|
|
in the library layer.
|
|
|
|
|
2014-05-18 21:09:46 -04:00
|
|
|
void playlist :: del(Track *track, const std::string &name);
|
|
|
|
Remove track->id from the playlist named "name" and return true.
|
|
|
|
Return false if the playlist does not exist or if the track
|
|
|
|
is not in the playlist.
|
|
|
|
|
|
|
|
If "name" is the currently selected playlist, remove the track
|
|
|
|
from the PlaylistQueue.
|
|
|
|
|
2014-05-24 13:31:45 -04:00
|
|
|
If "name" is "Banned", add the track to the LibraryQueue in the
|
|
|
|
library layer.
|
|
|
|
|
2014-05-18 21:09:46 -04:00
|
|
|
bool playlist :: has(Track *track, const std::string &name);
|
|
|
|
Return true if the chosen playlist has the given track.
|
|
|
|
Return false otherwise.
|
|
|
|
|
|
|
|
void playlist :: select(const std::string &name);
|
|
|
|
Change the currently displayed playlist to "name".
|
|
|
|
|
|
|
|
const IndexEntry *playlist :: get_tracks(const std::string &name);
|
|
|
|
Return the IndexEntry represeting the requested playlist.
|
|
|
|
Return NULL if the requested playlist does not exist.
|
|
|
|
|
|
|
|
Queue *playlist :: get_queue();
|
|
|
|
Return the PlaylistQueue to the caller.
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Deck:
|
|
|
|
The deck is used to hold temporary queues created by the user. This
|
|
|
|
layer is also in charge of maintaining a "recently played" queue of
|
2014-05-31 09:09:44 -04:00
|
|
|
tracks.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
The deck will be saved to the file "deck". When upgrading from file
|
|
|
|
version V0 to V1, use the saved random flag and sort order to set up
|
|
|
|
the library_q.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
2014-05-31 09:09:44 -04:00
|
|
|
- TempQueue:
|
|
|
|
class TempQueue : public Queue {
|
|
|
|
public:
|
|
|
|
TempQueue(bool);
|
|
|
|
|
|
|
|
void set_flag(queue_flag);
|
|
|
|
void unset_flag(queue_flag);
|
|
|
|
|
|
|
|
unsigned int add(Track *);
|
|
|
|
void del(Track *);
|
|
|
|
void del(unsigned int);
|
|
|
|
|
|
|
|
void sort(sort_t, bool);
|
|
|
|
};
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
- Deck:
|
2014-05-31 09:09:44 -04:00
|
|
|
list<TempQueue> deck;
|
2014-05-26 20:27:26 -04:00
|
|
|
|
|
|
|
V0:
|
|
|
|
File << library_q.random << library_q.sort_order().size();
|
|
|
|
File << lib12order()[N].field << lib12order()[N].ascending;
|
|
|
|
File << deck.size() << endl;
|
|
|
|
File << deck[0] << endl;
|
|
|
|
File << deck[N] << endl;
|
|
|
|
|
|
|
|
V1:
|
|
|
|
File << deck.size() << endl;
|
|
|
|
File << deck[0] << endl;
|
|
|
|
File << deck[1] << endl;
|
|
|
|
|
|
|
|
- RecentQueue:
|
|
|
|
class RecentQueue : public Queue {
|
|
|
|
public:
|
|
|
|
RecentQueue();
|
|
|
|
unsigned int add(Track *);
|
|
|
|
};
|
2014-04-01 20:11:57 -04:00
|
|
|
|
2014-05-31 09:09:44 -04:00
|
|
|
- TempQueue API:
|
|
|
|
TempQueue :: TempQueue(bool random);
|
|
|
|
Initialize a new TempQueue with the flag Q_ENABLED set.
|
|
|
|
If random is True then also set the Q_RANDOM flag.
|
|
|
|
|
|
|
|
void TempQueue :: set_flag(queue_flag flag);
|
|
|
|
void TempQueue :: unset_flag(queue_flag flag);
|
|
|
|
unsigned int TempQueue :: add(Track *track);
|
|
|
|
void TempQueue :: del(Track *track);
|
|
|
|
void TempQueue :: del(unsigned int index);
|
|
|
|
void TempQueue :: sort(sort_t field, bool ascending);
|
|
|
|
These functions are all wrappers around the basic Queue
|
|
|
|
functions of the same name. First, call the corresponding
|
|
|
|
Queue :: <whatever>() function to make the correct Queue
|
|
|
|
modification. Then, call deck :: write() to save changes to
|
|
|
|
disk.
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
- RecentQueue API:
|
|
|
|
RecentQueue :: RecentQueue();
|
|
|
|
Initialize a Queue with the flags Q_ENABLED, Q_REPEAT, and
|
|
|
|
Q_NO_SORT set.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
unsigned int RecentQueue :: add(Track *track);
|
|
|
|
The RecentQueue is designed to be a uniqueue queue that displays
|
|
|
|
the most recent tracks first.
|
2014-04-01 20:11:57 -04:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
del(track);
|
|
|
|
_cur = 0;
|
|
|
|
return _add_at(track, 0);
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2013-12-22 17:22:40 -05:00
|
|
|
- API
|
2014-01-18 11:00:08 -05:00
|
|
|
void deck :: init();
|
2014-05-26 20:27:26 -04:00
|
|
|
Read the deck file from disk and restore the queues.
|
2014-01-18 11:00:08 -05:00
|
|
|
|
2013-12-27 17:25:45 -05:00
|
|
|
void deck :: write(File &);
|
2013-12-28 14:01:38 -05:00
|
|
|
Read or write the playqueue file. This will be called
|
2013-12-27 17:25:45 -05:00
|
|
|
from the audio layer to store state.
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Queue *deck :: create(bool random);
|
|
|
|
Adds a new queue to the end of the deck and return a pointer
|
|
|
|
to it. Save the deck to disk.
|
|
|
|
|
|
|
|
void deck :: destroy(Queue *queue);
|
|
|
|
Remove the requested queue from the deck and trigger the
|
|
|
|
on_pq_removed() callback. Save the deck to disk.
|
|
|
|
|
|
|
|
void deck :: move(Queue *queue, unsigned int pos);
|
|
|
|
Move the queue to the new location in the deck. Save the deck
|
|
|
|
to disk.
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2014-05-26 22:20:07 -04:00
|
|
|
unsigned int deck :: index(Queue *queue);
|
|
|
|
Return the index of the queue in the deck or deck.size() if
|
|
|
|
the queue is not currently in the deck.
|
|
|
|
|
2014-06-05 11:04:55 -04:00
|
|
|
Queue *deck :: get(unsigned int index);
|
|
|
|
Return the queue at the requested index, or NULL if no queue
|
|
|
|
is found.
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Track *deck :: next();
|
|
|
|
Find the first enabled queue on the deck and return the track
|
|
|
|
given by queue->next().
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
If the queue is empty after calling next(), call destroy() to
|
|
|
|
remove it from the list.
|
2013-12-27 17:25:45 -05:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
If there are no enabled queues, return a track from the library
|
|
|
|
queue. If the library queue is empty, return NULL.
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2014-05-26 22:20:07 -04:00
|
|
|
If the result is non-NULL, add the found track to the recent
|
|
|
|
queue.
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Save the deck before returning.
|
2013-11-23 13:21:17 -05:00
|
|
|
|
2014-05-26 22:20:07 -04:00
|
|
|
Track *deck :: prev();
|
|
|
|
Return the track given by recent_queue->next(). If the recent
|
|
|
|
queue is empty, return NULL.
|
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
list<Queue> &deck :: get_queues();
|
|
|
|
Return the list of queues to the caller.
|
2014-01-18 11:00:08 -05:00
|
|
|
|
2014-05-26 20:27:26 -04:00
|
|
|
Queue *deck :: get_queue();
|
|
|
|
Return the RecentQueue to the caller.
|
2013-12-27 17:25:45 -05:00
|
|
|
|
|
|
|
|
2014-05-31 10:25:25 -04:00
|
|
|
Audio Driver:
|
|
|
|
The audio driver creates a way to fake audio playback for testing. This
|
|
|
|
will allow for more accurate tests, since I will know in advance what
|
|
|
|
values are returned to the Audio layer. This layer will derive from
|
|
|
|
the Driver class to implement either the GSTDriver or the TestDriver.
|
|
|
|
|
2014-06-01 17:52:49 -04:00
|
|
|
- Seconds -> Nanoseconds conversion:
|
|
|
|
static const unsigned long O_SECOND = 1000000000;
|
|
|
|
|
2014-05-31 10:25:25 -04:00
|
|
|
- Driver:
|
|
|
|
class Driver {
|
|
|
|
protected:
|
|
|
|
void (*on_eos)();
|
|
|
|
|
|
|
|
public:
|
|
|
|
Driver();
|
|
|
|
~Driver();
|
2014-05-31 20:52:40 -04:00
|
|
|
virtual void init(int *, char ***, void (*)(), void (*)()) = 0;
|
2014-05-31 10:25:25 -04:00
|
|
|
|
|
|
|
virtual void load(const std::string &) = 0;
|
|
|
|
virtual void play() = 0;
|
|
|
|
virtual void pause() = 0;
|
2014-05-31 20:52:40 -04:00
|
|
|
virtual void is_playing() = 0;
|
2014-05-31 10:25:25 -04:00
|
|
|
|
|
|
|
virtual void seek_to(long) = 0;
|
|
|
|
virtual long position() = 0;
|
|
|
|
virtual long duration() = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
- Driver API:
|
|
|
|
void Driver :: Driver();
|
|
|
|
Initialize the audio driver. This involves setting up a GST
|
|
|
|
Bus in the GSTDriver case.
|
|
|
|
|
|
|
|
void Driver :: ~Driver();
|
|
|
|
In the GSTDriver case, call gst_deinit() to avoid memory leak
|
|
|
|
false positives.
|
|
|
|
|
2014-05-31 20:52:40 -04:00
|
|
|
void Driver :: init(int argc, char **argv, void (*eos_cb)(),
|
|
|
|
void (*error_cb)());
|
2014-05-31 10:25:25 -04:00
|
|
|
The GSTDriver will use this function to set up the playbin2.
|
|
|
|
When an end-of-stream message is received, call eos_cb().
|
2014-05-31 20:52:40 -04:00
|
|
|
If there is an error, call error_cb();
|
2014-05-31 10:25:25 -04:00
|
|
|
|
|
|
|
void Driver :: load(const std::string &file);
|
|
|
|
Load file for playback, but do not begin playback yet.
|
|
|
|
|
|
|
|
void Driver :: play();
|
2014-05-31 20:52:40 -04:00
|
|
|
Start playback. Return true if the state change operation
|
|
|
|
succeeds, false otherwise.
|
2014-05-31 10:25:25 -04:00
|
|
|
|
|
|
|
void Driver :: pause();
|
2014-05-31 20:52:40 -04:00
|
|
|
Pause playback. Return true if the state change operation
|
|
|
|
succeeds, false otherwise.
|
|
|
|
|
|
|
|
bool Driver :: is_playing();
|
|
|
|
Return true if the player is currently playing, false otherwise.
|
2014-05-31 10:25:25 -04:00
|
|
|
|
|
|
|
void Driver :: seek_to(long pos);
|
|
|
|
Change playback position in the current track in nanoseconds.
|
|
|
|
|
|
|
|
long Driver :: position();
|
|
|
|
Return the current position in the track in nanoseconds.
|
|
|
|
|
|
|
|
long Driver :: duration();
|
|
|
|
Return the duration of the track in nanoseconds.
|
|
|
|
|
|
|
|
- API:
|
|
|
|
Driver *driver :: get_driver();
|
|
|
|
Return the current driver to be used for audio playback. This
|
|
|
|
could be either the GSTDriver or the TestDriver depending on
|
|
|
|
if CONFIG_TEST is set when compiling.
|
|
|
|
|
2013-12-27 17:25:45 -05:00
|
|
|
|
2014-01-18 11:00:08 -05:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
Audio:
|
|
|
|
The audio layer uses the configured driver to control audio playback.
|
2013-12-27 17:25:45 -05:00
|
|
|
|
|
|
|
Gstreamer options passed to audio :: init() can be found by running
|
|
|
|
`gst-inspect-1.0 --help-gst` on the command line.
|
2013-08-07 21:07:07 -04:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
- File:
|
|
|
|
File cur_track("cur_track");
|
2013-12-28 14:01:38 -05:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
File << current_track->id << endl;
|
2013-12-28 14:01:38 -05:00
|
|
|
|
2013-08-07 21:07:07 -04:00
|
|
|
- API:
|
2014-05-31 21:06:48 -04:00
|
|
|
void audio :: init(int *argc, char ***argv);
|
|
|
|
Initialize the audio driver through argc and argv. Read in
|
|
|
|
the current track file and load the track.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2013-12-28 14:01:38 -05:00
|
|
|
void audio :: play();
|
|
|
|
void audio :: pause();
|
2014-05-31 21:06:48 -04:00
|
|
|
void audio :: seek_to(long pos);
|
|
|
|
Call the corresponding function from the audio driver, but only
|
|
|
|
if a track is loaded.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2014-06-01 17:52:49 -04:00
|
|
|
void audio :: stop();
|
|
|
|
pause()
|
|
|
|
seek_to(0)
|
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
long audio :: position();
|
|
|
|
long audio :: duration();
|
|
|
|
Call the corresponding function from the audio driver. Return
|
|
|
|
0 if no track is currently loaded.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
std::string audio :: position_str();
|
|
|
|
Return the current audio position in string form.
|
|
|
|
Return an empty string if there is no current track.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2013-12-28 14:01:38 -05:00
|
|
|
void audio :: next();
|
2014-05-31 21:06:48 -04:00
|
|
|
Call the deck :: next() function to get the next track that
|
|
|
|
should be played and use the audio driver to load the track.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
Save that track's ID to the cur_track file.
|
2013-12-28 14:01:38 -05:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
void audio :: prev();
|
|
|
|
Call the deck :: previous() function to find a new track to
|
|
|
|
play and use the audio driver to load the track.
|
2013-12-28 14:01:38 -05:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
Save that track's ID to the cur_track file.
|
2013-10-06 22:16:54 -04:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
void audio :: load_track(Track *track);
|
|
|
|
Load the requested track.
|
2013-08-07 21:07:07 -04:00
|
|
|
|
2014-05-31 21:06:48 -04:00
|
|
|
Save that track's ID to the cur_track file.
|
2013-08-07 21:07:07 -04:00
|
|
|
|
2014-06-01 17:52:49 -04:00
|
|
|
Track *audio :: current_track();
|
|
|
|
Return the currently playing Track.
|
|
|
|
Return NULL if there is no current track.
|
|
|
|
|
2013-12-28 14:01:38 -05:00
|
|
|
void audio :: pause_after(bool enabled, unsigned int N);
|
2014-05-31 21:06:48 -04:00
|
|
|
If enabled == true:
|
|
|
|
Configure Ocarina to pause playback after N tracks
|
|
|
|
have been played.
|
|
|
|
If enabled == false:
|
|
|
|
Do not automatically pause.
|
2013-12-27 17:25:45 -05:00
|
|
|
|
2014-06-01 17:52:49 -04:00
|
|
|
If N is greater than the current pause count then enabled should
|
|
|
|
be set to true.
|
|
|
|
|
2013-12-28 14:01:38 -05:00
|
|
|
bool audio :: pause_enabled();
|
|
|
|
unsigned int audio :: pause_count();
|
2014-05-31 21:06:48 -04:00
|
|
|
Use these functions to access the current "pause after N" state.
|
2013-07-27 10:25:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-25 10:56:59 -04:00
|
|
|
Gui: (ocarina/*)
|
|
|
|
The GUI will be written in C++ using gtkmm3 for (hopefully) cleaner code.
|
|
|
|
|
|
|
|
- Design requirements:
|
|
|
|
- gtkmm3
|
|
|
|
- Front-end for the library to add, remove and modify paths
|
|
|
|
- This can be hidden since it's not a common task
|
|
|
|
- Double-click to play songs
|
|
|
|
- Front-end for groups to add and remove tracks
|
|
|
|
- This is a common task and should not be hidden
|
|
|
|
- Don't save / restore window size
|
|
|
|
- Some window managers already do this automatically
|
|
|
|
- Set reasonable defaults
|