From 2a1f695ea8129a6610c782b57e557ab20b7ffd19 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 27 Feb 2014 10:02:22 -0500 Subject: [PATCH] design: Remove design directory Keeping these documents updated with the top-level design.txt was annoying. Moving forward, I would like to keep a single document up to date without extra hassle. Signed-off-by: Anna Schumaker --- design/Sconscript | 109 ----------------------- design/audio.txt | 116 ------------------------ design/callback.txt | 28 ------ design/database.txt | 169 ----------------------------------- design/deck.txt | 73 --------------- design/error.txt | 19 ---- design/file.txt | 118 ------------------------- design/filter.txt | 57 ------------ design/footer.txt | 121 ------------------------- design/gui.txt | 21 ----- design/header.txt | 17 ---- design/idle.txt | 73 --------------- design/install.txt | 12 --- design/library.txt | 205 ------------------------------------------- design/playlist.txt | 53 ----------- design/playqueue.txt | 140 ----------------------------- design/print.txt | 17 ---- design/version.txt | 14 --- 18 files changed, 1362 deletions(-) delete mode 100644 design/Sconscript delete mode 100644 design/audio.txt delete mode 100644 design/callback.txt delete mode 100644 design/database.txt delete mode 100644 design/deck.txt delete mode 100644 design/error.txt delete mode 100644 design/file.txt delete mode 100644 design/filter.txt delete mode 100644 design/footer.txt delete mode 100644 design/gui.txt delete mode 100644 design/header.txt delete mode 100644 design/idle.txt delete mode 100644 design/install.txt delete mode 100644 design/library.txt delete mode 100644 design/playlist.txt delete mode 100644 design/playqueue.txt delete mode 100644 design/print.txt delete mode 100644 design/version.txt diff --git a/design/Sconscript b/design/Sconscript deleted file mode 100644 index 0554d417..00000000 --- a/design/Sconscript +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python -import os - -created_files = dict() -node_names = dict() - -class DesignFile: - def read_files(self, file): - lastkey = "" - for line in file: - if line == "\n": - return - if line[0] == " " and line[1] == " ": - created_files[lastkey] += [line.strip()] - created_files[lastkey].sort() - else: - lastkey = line.strip() - created_files.setdefault(lastkey, []) - - def read_depends(self, file): - for line in file: - if line == "\n": - return; - else: - self.depends += line.strip().split() - - def __init__(self, file): - self.name = "" - self.lines = [] - self.depends = [] - self.depend_nodes = set() - - if file == None: - return - self.name = file.rsplit(".", 1)[0] - f = open("design/%s" % file) - for line in f: - if line == "== Files ==\n": - self.read_files(f) - elif line == "== Depends ==\n": - self.read_depends(f) - else: - self.lines += [line] - node_names[self.name] = self - - -def pump_depends(nodes): - options = [] - res = None - for n in nodes: - if len(n.depend_nodes) == 0: - options += [ n.name ] - options.sort() - res = node_names[ options[0] ] - nodes.remove(res) - for n in nodes: - if res in n.depend_nodes: - n.depend_nodes.remove(res) - return res - - -def resolve_dependencies(): - tmp = set() - res = [] - - for key, node in node_names.items(): - tmp.add(node) - for depend in node.depends: - if depend == "*": - node.depend_nodes = set(node_names.values()) - else: - node.depend_nodes.add(node_names[depend]) - node.depend_nodes.discard(node_names[node.name]) - - while len(tmp) > 0: - res += [ pump_depends(tmp) ] - return res - - -def gen_files_list(parsed): - design = DesignFile(None) - design.lines += [ "Files:\n"] - keys = created_files.keys() - keys.sort() - for key in keys: - design.lines += [ " %s\n" % key ] - for val in created_files[key]: - design.lines += [ " %s\n" % val ] - parsed.insert(1, design) - - -def merge_design(target, source, env): - files = os.listdir("design/") - files.sort() - for file in files: - if file.endswith(".txt"): - DesignFile(file) - node_list = resolve_dependencies() - gen_files_list(node_list) - - f = open("design.txt", 'w') - for index, design in enumerate(node_list): - if index != 0: - f.write("\n\n\n") - for line in design.lines: - f.write(line) - -design = Command("design.txt", None, merge_design) -Alias("design", design) diff --git a/design/audio.txt b/design/audio.txt deleted file mode 100644 index 81d004c8..00000000 --- a/design/audio.txt +++ /dev/null @@ -1,116 +0,0 @@ -== Files == - ocarina/include/ - audio.h - ocarina/lib/ - audio.cpp - -== Depends == -deck library - -Audio: (lib/audio.cpp) - This file will introduce an "audio" namespace containing all of the - functions interacting with gstreamer. This will create a wrapper - namespace that will be easier to work with than using raw gstreamer - functions. - - The audio layer is meant to be an interface used by the front end to - control most features of the backend library. This means implementing - next track, previous track, and so on here. - - Gstreamer options passed to audio :: init() can be found by running - `gst-inspect-1.0 --help-gst` on the command line. - -- Internal: - Set up a message bus to look for end-of-stream and error messages so - the next song can be played. This function should call the play - function after loading a track and after checking the "pause after N" - count. - - The audio layer will also create an internal playqueue for tracking - recently played songs. - -- State: - The audio layer will store the current trackid to disk, and then save - the playqueue deck. - - File << current_track << endl - deck.write(File); - -- API: - void audio :: init(argc, argv); - Initialize the gstreamer layer and reload the track that was - last loaded before shutdown. Gstreamer is supposed to remove - options from the argv array as they are processed, so pass - pointers to argc and argv to this function. - - Read in the state file. - - void audio :: quit(); - Clean up memory allocated by gstreamer. - Write out the state file. - - void audio :: play(); - void audio :: pause(); - Change the gstreamer state to either GST_STATE_PLAYING or - GST_STATE_PAUSED. Do nothing if there is not a track loaded - in the pipeline. Throw -EAUDIO if there is an error changing - state. - - void audio :: seek_to(long); - Seek to a position X nanoseconds into the track. Throw -EAUDIO - if there is an error seeking to the requested position. Do - nothing if there is not a track loaded in the pipeline. - - Seconds can be converted to nanoseconds by multiplying with - GST_SECOND. - - void audio :: stop(); - pause() - seek_to(0) - - void audio :: next(); - Call the deck :: next() function to get the next trackid, - and load that file into the gstreamer pipeline. Do not change - the state of the pipeline (if nothing is playing yet, don't - call play()). Throw -EEXIST if there is no track to load - into the pipeline. - - When a track is loaded: - Is it already in the recently played playqueue? - If yes, remove it from the playqueue. - Add to the front of the recently played playqueue. - Reset the current pointer in the playqueue to 0. - - Write out the state file. - - void audio :: previous(); - Call the playlist :: previous() function to iterate backwards - through the recently played playqueue. Load the returned trackid - without changing the pipeline state. - - trackid audio :: current_trackid(); - Return the trackid of the currently playing song. If no track - is loaded throw -EEXIST; - - unsigned int audio :: position(); - Return the number of seconds that the song has played. - - unsigned int audio :: duration(); - Return the duration of the current song in seconds. - - void audio :: pause_after(bool enabled, unsigned int N); - Pause after N tracks. The first parameter is a bool - representing if this feature is enabled or not (true == enabled, - false == disabled). - - The count will only be decremented when an end-of-stream message - is received by the gstreamer pipeline, and not when calling - next(). - - bool audio :: pause_enabled(); - Return true if pausing is enabled, and false if pausing is - disabled. - - unsigned int audio :: pause_count(); - Return the number of tracks that will be played before - playback pauses. diff --git a/design/callback.txt b/design/callback.txt deleted file mode 100644 index a5f4cc89..00000000 --- a/design/callback.txt +++ /dev/null @@ -1,28 +0,0 @@ -== Files == - ocarina/include/ - callback.h - ocarina/lib/ - callback.cpp - -== Depends == -version print - -Callbacks: (lib/callback.cpp) - 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. - - -- Callback functions: - struct Callbacks { - void (*on_library_add)(unsigned int, library :: Library *); - void (*on_library_update)(unsigned int, library :: Library *); - void (*on_library_track_add)(); - }; - - static struct Callbacks callbacks; - -- API: - struct Callbacks *get_callbacks(); - Return the Callbacks structure; diff --git a/design/database.txt b/design/database.txt deleted file mode 100644 index f336f420..00000000 --- a/design/database.txt +++ /dev/null @@ -1,169 +0,0 @@ -== Files == - ocarina/include/ - database.h - database.hpp - ocarina/lib/ - database.cpp - -== Depends == -file - -Database: (lib/database.cpp) - Ocarina 5.x created a different save file format for each type of - data that needed to be stored (preferences, library paths, playlists). - I intend to unify everything into a generic file format that can be - accessed through a generic database interface. The database code will - be in charge of printing the "valid" bit for each DatabaseEntry so that - child classes do not need to call into the parent class. If valid == - true, the DatabaseEntry will be streamed out followed by a newline. If - valid == false the database will print the next entry in the vector. - - Modules should inherit from the DatabasEntry class and implement their - own read() and write() functions. The "valid" field will be stored - before these functions are called, and the entry will be skipped if - valid is set to false. - - The Database class is a templated class, so code could potentially - get messy. Normal class declarations can still exist in the file - include/database.h and member functions can be written in the file - include/database.hpp, which will be included by database.h. Any - function not relying on a template can be written in lib/database.cpp. - -- DatabaseEntry: - class DatabaseEntry { /* let database modify valid flag */ - public: - const std::string primary_key; - bool valid; - - DatabaseEntry(const std::string &); - virtual void write(File &) = 0; - virtual void read(File &) = 0; - virtual void print() = 0; - }; - - File << - -- IndexEntry: - class IndexEntry : public DatabaseEntry { - public: - set values; - - void write(File &); - void read(File &); - void print(); - void insert(unsigned int); - void remove(unsigned int); - }; - - File << key << endl; - File << values.size() << values[0] << .. << values[N] << endl; - -- Database: - template - class Database { - private: - vector db; - map keys; - unsigned int _size; /* Number of valid rows */ - File file; - - public: - Database::Database(filename, flags); - void load(); - void save(); - void clear(); - void print(); - void print_keys(); - - unsigned int insert(T); - void remove(unsigned int); - unsigned int size(); - unsigned int num_rows(); - - unsigned int first(); - unsigned int last(); - unsigned int next(unsigned int &); - bool has_key(const std :: string &); - unsigned int find_index(const std::string &); - T &find(const std::string &); - T &operator[](unsigned int); - }; - - File << db.size() << endl - File << INDEX_0 << db[INDEX_0].valid << db[INDEX_0] << endl; - File << INDEX_1 << db[INDEX_1].valid << db[INDEX_1] << endl; - ... - -- API: - Database :: Database(filename, flags); - Initializes database to use ~/.ocarina{-debug}/filename. - Set up flags. - - void Database :: load(); - Reads a saved database from disk. - - void Database :: save(); - Saves the database to disk. - - void Database :: clear(); - This function exists only if CONFIG_TEST is enabled. - Clear the database contents in-memory, but do NOT write - to disk. - - void Database :: print(); - This function exists only If CONFIG_TEST is enabled. - Following a similar format for writing to disk, print the - database to the console in a human-readable format. - - void Database :: print_keys(); - This function exists only if CONFIG_TEST is enabled. - Print out the collected primary keys in the database. - - template - unsigned int Database :: insert(T &); - Adds a new item to the db and marks it as valid. A reverse - mapping is also created into the keys map to map the primary - key back to the id of the newly added item. - - void Database :: remove(unsigned int index); - Mark db[index] as invalid (quick deletion). - - unsigned int Database :: size(); - Returns number of valid rows in the database. - - unsigned int Database :: num_rows(); - Return db.size(). - - unsigned int Database :: first(); - Return the id to the first valid row. Return db.size() if - there are no valid rows. - - unsigned int Database :: last(); - Return the id of the last valid row. Return db.size() if - there are no valid rows. - - unsigned int Database :: next(unsigned int &id); - Return the id of the next valid row or return db.size() if - there are no valid rows. - - bool Database :: has_key(const std::string &key); - Return true if an item with primary key "key" exists in the - database, and false otherwise. - - unsigned int Database :: find_index(const std::string key); - If the key exists in the database, return the index of the - database item with that key. Throw -EEXIST if the key is not - in the database. - - template - T &Database :: find(const std::string &key); - Search for primary key "key" in the database. The reverse - mapping should be used to make this operation faster. - Throw -EEXIST if the key is not found in the mapping. - Throw -EINVAL if the key points to an invalid entry. - - template - T &Database :: operator[](unsigned int index); - Return a reference to db[index]. - Throw -EEXIST if index is out of range. - Throw -EINVAL if index is invalid. diff --git a/design/deck.txt b/design/deck.txt deleted file mode 100644 index 53ad216d..00000000 --- a/design/deck.txt +++ /dev/null @@ -1,73 +0,0 @@ -== Files == - ocarina/include/ - deck.h - ocarina/lib/ - deck.cpp - $HOME/.ocarina{-debug}/ - playqueue.lst - -== Depends == -playqueue - -Deck: (lib/deck.cpp) - The playqueue deck is used to hold the temporary playqueues created by - the user. - - This module also controls the library playqueue, which should be updated - using the on_library_track_add() and on_library_track_del() callback - functions. The library playqueue will always have PQ_ENABLED and - PQ_REPEAT set. This playlist will default to PQ_RANDOM unset. - -- Deck: - list deck; - Playqueue library_pq; - - File << library_pq.random << deck.size() << endl; - File << deck[0] << endl; - File << deck[N] << endl; - -- API - void deck :: init(); - Set up callbacks used by the library. - - void deck :: read(File &); - void deck :: write(File &); - Read or write the playqueue file. This will be called - from the audio layer to store state. - - Playqueue *deck :: create(); - Adds a new playqueue to the end of the deck and returns a - pointer to it. - - void deck :: remove(N); - Remove playqueue N from the deck. - - Playqueue *deck :: get(N); - Return playqueue N from the deck. - - void deck :: move(M, N); - Move playqueue at index M to index N. - - unsigned int deck :: next(); - Iterate through the deck until you find a playqueue with the - flag PQ_ENABLED set. Call next() on this playqueue and return - the result. - - If the playqueue is empty after calling next(), remove it from - the deck. - - If there are no playqueues on the deck, play a song from the - library playqueue. - - If there are no playable IDs, throw -EEXIST. - - void deck :: reset(); - This function only exists if CONFIG_TEST is enabled. Erase - all the playqueue information and reset the deck list. - - void deck :: print_info(); - This function only exists if CONFIG_TEST is enabled. Print - out helpful stats about the current state of the playqueue deck. - - Playqueue *deck :: get_library_pq(); - Return the library playqueue. diff --git a/design/error.txt b/design/error.txt deleted file mode 100644 index 72353ece..00000000 --- a/design/error.txt +++ /dev/null @@ -1,19 +0,0 @@ -== Files == - ocarina/include/ - error.h - -== Depends == -install - -Errors: (include/error.h - This file contains an enum defining error codes used throughout the - codebase. - -Error Codes: - enum error_t { - EAUDIO = 1, /* Audio error */ - EEXIST, /* Requested object does not exist */ - EINVAL, /* Invalid operation requested */ - EIO, /* I/O error */ - ENOTRACK, - }; diff --git a/design/file.txt b/design/file.txt deleted file mode 100644 index f85f2063..00000000 --- a/design/file.txt +++ /dev/null @@ -1,118 +0,0 @@ -== Files == - ocarina/include/ - file.h - ocarina/lib/ - file.cpp - -== Depends == -version print - -On-disk files: (lib/file.cpp) - Data will be stored in the user's home directory according to the - XDG / freedesktop.org specification. This means storing data in - $XDG_DATA_HOME/ocarina{-debug|-test}/ and storing configuration in - $XDG_CONFIG_HOME/ocarina{-debug|-test}/. In addition, I will support - importing data from Ocarina 5.10 for conversion to the new format. - - In theory my file format will not change often, so it should be - possible to use multiple Ocarina versions with the same data. However, - should the format need to change I will only support forward - compatibility. This means that downgrades will not be possible after - a file format change. To keep the code even cleaner, I will only - support updating from the previous file format version. This means - that legacy support will be removed after the first file format change. - - Items should be written to a file with either a space or new line - separating multiple values. - -- Notation: - "File << aaaaa << bbbbb << endl" is translated into "aaaaa bbbbb\n" - -- File version: - #define FILE_VERSION 0 - -- Hint where the file is located: - enum FileLocHint { - FILE_TYPE_CONFIG, - FILE_TYPE_DATA, - FILE_TYPE_LEGACY, - FILE_TYPE_INVALID, - } - -- Open mode: - enum OpenMode { - OPEN_READ, - OPEN_WRITE, - NOT_OPEN, - } - -- File: - class File : std::fstream { - private: - unsigned int version; - OpenMode mode; - FileLocHint hint; - string filepath; - - public: - File(string, FileLocHint); - ~File(); - const char *get_filepath(); - const unsigned int get_version(); - bool exists(); - void open(OpenMode); - void close(); - string getline(); - } - -- File format: - File << FILE_VERSION << endl; - File << ; - -- API: - File :: File(string filepath, FileLocHint hint); - Resolve filepath to one of: - XDG_{CONFIG|DATA}_HOME/ocarina/filepath - XDG_{CONFIG|DATA}_HOME/ocarina-debug/filepath - XDG_{CONFIG|DATA}_HOME/ocarina-test/filepath - $HOME/.ocarina/ - $HOME/.ocarina-debug/ - $HOME/.ocarina-test/ - - If filepath is an empty string, set the file hint to - FILE_TYPE_INVALID and do not set the filepath field. - - File :: ~File(); - Close the file stream if it is open. - - const char *File :: get_filepath(); - Return the full filepath to the file. - - const unsigned int File :: get_version(); - Return the file version number. - - bool File :: exists(); - Return true if the file exists in the filesystem. - Return false otherwise. - - void File :: open(OpenMode mode); - When opening a file for reading (mode == OPEN_READ), - - Throw -EEXIST if the file does not exist - - Open the file - - Read in version from the start of the file - - When opening a file for writing (mode == OPEN_WRITE), - - Throw -ELEGACY if the file has FILE_TYPE_LEGACY set - - Create missing directories as needed - - Write version information to the start of the file - - Throw -EINVAL if hint == FILE_TYPE_INVALID. - Throw -EOPEN if the file is already open. - - void File :: close(); - Close a file after IO. - - string File :: getline(); - Read an entire line from the file and return it to the caller. - In theory a return value optimization will occur so returning - a string by value won't be a problem. diff --git a/design/filter.txt b/design/filter.txt deleted file mode 100644 index 45243582..00000000 --- a/design/filter.txt +++ /dev/null @@ -1,57 +0,0 @@ -== Files == - ocarina/include/ - filter.h - ocarina/lib/ - filter.cpp - -== Depends == -database - -Filter: (lib/filter.cpp) - Filtering is used to generate a subset of songs displayed by the UI to - that users can choose from. The inverted index is generated at startup - so there is no need for a remove() function, since it will be wiped - the next time the application starts. - -- Index: - Database filter_index; - map lowercase_cache; - unsigned int lowercase_cache_hits; - -- Parsing: - 1) Convert the provided string into a list of words, using whitespace - and the following characters as delimiters: \/,;()_-~+" - - For each word: - 2) Check the lowercase_cache to see if we have seen the word before, - a) If we have, return the stored string - b) Convert the string to lowercase and strip out remaining - special characters. Add the result to the lowercase_cache; - -- API: - void filter :: add(string, track_id); - Parse the string into substrings following the "Parsing" - section (above). Add each (substring, track_id) pair to the - filter_index. - - To generate substrings, iterate over the word starting from - the front. For example: "goron" would contain the substrings - {g, go, gor, goro, goron}. - - void filter :: search(string, set &); - Parse the string into substrings following the "Parsing" - section (above). We want to find track_ids that match ALL - substrings, so take the intersection of all sets returned by - the filter_index for a given substring. - - std::string filter :: to_lowercase(const std::string &string); - Split the string into words following step 1 of "Parsing" - (above). Assemble and return a result string using the lower - case cache to convert each term to lowercase. - - void filter :: print_cache_stats(); - Print cache hit and size information. - - void filter :: get_index(); - This function only exists if CONFIG_TEST is enabled. - Return the index storing all the filter data. diff --git a/design/footer.txt b/design/footer.txt deleted file mode 100644 index 51ca2e75..00000000 --- a/design/footer.txt +++ /dev/null @@ -1,121 +0,0 @@ -== Depends == -* - -Future work: - I want to set reasonable expectations for Ocarina 6 so that I don't - have to spend a large amount of time coding before releasing something - to the wild. This section will be a list of features that I want, but - should be deferred to a future release so basic support can be coded. - - Hint: If feature B depends on A, implement A in 6.x and B in 6.x+1 - - - New default groups: - Unplayed tracks - - - Categories: - Use these to make "groups of groups" for better organization. - Different categories can include Album, Artist and Genere - dynamic groups in addition to user created groups (see below) - - The Artist, Album and Genre "tables" can be used to populate - these categories. - - - User created song groups: - Basic add and remove features can be implemented using the - Library and Banned Songs groups. This will give me a chance - to test saving groups on a small scale before letting users - create anything they want. - - - Save a user's playlist as a group: - - - Library defragment: - Ocarina 6.0 will leave holes in the library when tracks are - deleted, potentially leading to fragmentation and larger-than- - needed file sizes. A "defragment" utility can be created to - clean up unused slots. - - To help with fixing groups, a mapping of (old values) -> - (new values) should be kept. - - - Fix track durations: - Some tracks in my library are tagged with the wrong duration, - so fix them as they are played. - - - Track tag editor: - Make a pop-up window for editing the tags of a track. Be sure - to update the library information and the on-disk file. - - - Album art: - (easy) Start with album art fetching script - (hard) Build in to Ocarina - - - Playlist custom sorting: - Click column headers to choos sort order - Keep a list of fields that the user has selected and place new - fields in the front of this list. Use a recursive stable sort - to do the sorting. - - - Better design file format: - Todo list in each document instead of all at once in the footer. - Leave the Todo list out of the official "design document" and - keep it in each individual section instead. - XML? - Code formatting? - Checkbox for when features are done? (reference commit) - - - Copy a song group to a different directory: - This can be useful for keeping an external device (like an - Android phone) updated with the music you want to listen to. - Complications: I have an mp3 mirror of all my music, and I - want the mp3s to be synced. Perhaps track mirrors in Ocarina? - - - Mirror directory: - I rip music to .flac, but keep an mp3 mirror directory to sync - to other computers and phones. An Ocarina tool to manage a - COMPLETE library mirror might be a good idea so I no longer - need to manage it externally. This can still be done with a - script, a cron job, and maybe a "mirror this track" option in - 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 - - - Preferences: - - Set default sort - - Save window size - - Set album art size - - - Rip out Ocarina 5.x importing and legacy file support - - - AirPlay / remote audio support - - - Media keys - - - Replaygain support - External script to calculate values? - 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. - - - Scons improvements: - Why does everything compile with the same CONFIG_* parameters? - Cleanups to tests/Sconscript - Global dependency resolution (rather than having multiple ways - in lib/ design/ and tests/) - - - 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 diff --git a/design/gui.txt b/design/gui.txt deleted file mode 100644 index bbf99939..00000000 --- a/design/gui.txt +++ /dev/null @@ -1,21 +0,0 @@ -== Files == - ocarina/gui/ - ocarina6.glade - * - -== Depends == -idle file audio library - -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 diff --git a/design/header.txt b/design/header.txt deleted file mode 100644 index 8940e680..00000000 --- a/design/header.txt +++ /dev/null @@ -1,17 +0,0 @@ -== Files == - ocarina/ - design.txt - -=============================================================================== -= = -= Ocarina 6.0 = -= = -=============================================================================== - -Ocarina 6.0 is the 6th (re)writing of the Ocarina music player - a lightweight, -GTK+ based music player. Improvements over the 5.x series will include the -existence of both a design document and unit tests, two items that will help -the main developer stay focused and make maintenance easier. - -Ocarina 6.0 will use Gstreamer 1.0 for audio playback and GTK-MM 3 for user -interface development. diff --git a/design/idle.txt b/design/idle.txt deleted file mode 100644 index 1ac8eb61..00000000 --- a/design/idle.txt +++ /dev/null @@ -1,73 +0,0 @@ -== Files == - ocarina/include/ - idle.h - idle.hpp - ocarina/lib/ - idle.cpp - -== Depends == -version print - -Idle queue: (lib/idle.cpp) - The idle queue is used to schedule tasks to run at a later time. Idle - tasks must inherit from the IdleBase class so that multiple templated - IdleTask pointers can be placed on the same idle queue. - - Creating an idle queue in idle.hpp will create a new queue for every - file that idle.h is included in. I want to have a single, shared - idle queue used by the entire application so to get around this the - IdleBase class is used and implemented in lib/idle.cpp. - -- IdleBase: - class IdleBase { - private: - schedule(); - - public: - IdleBase(); - ~IdleBase(); - virtual void run() = 0; - }; - -- IdleTask: - template - class IdleTask : IdleBase { - private: - void (*func)(T &); - T &data; - - public: - IdleTask(void (*)(T &), T); - void run(); - }; - -- Queue: - queue idle_queue; - float queued = 0.0 - float serviced = 0.0 - -- API: - void IdleBase :: schedule(); - Add the idle task to the idle queue. This should be called - by the IdleTask constructor. - queued++; - - template - static inline void idle :: schedule(void (*)(T &), T); - Create a new IdleTask to run the function later. - - bool idle :: run_task(); - If there are tasks on the queue: - run the next task - scheduled++ - If there are still tasks on the queue: - return true - else: - queued = 0 - scheduled = 0 - return false - - float idle :: get_progress(); - Return (serviced / queued) to the caller. If there are no - tasks, return 1.0 to indicate that the queue is finished (and - to avoid a divide-by-zero error). diff --git a/design/install.txt b/design/install.txt deleted file mode 100644 index 1ef49111..00000000 --- a/design/install.txt +++ /dev/null @@ -1,12 +0,0 @@ -== Files == - /usr/bin/ - ocarina - /usr/lib/ocarina/ - -== Depends == -header - -Install: - Ocarina will be compiled into a single executable placed under - /usr/bin/. Any extra files needed to run will be placed under - /usr/lib/ocarina/. diff --git a/design/library.txt b/design/library.txt deleted file mode 100644 index 65baa019..00000000 --- a/design/library.txt +++ /dev/null @@ -1,205 +0,0 @@ -== Files == - ocarina/include/ - library.h - ocarina/lib/ - library.cpp - $HOME/.ocarina{-debug}/ - album.db - artist.db - genre.db - library.db - track.db - -== Depends == -idle database playlist filter - -Library: (lib/library.cpp) - The library manages databases containing track information added by the - user. Ocarina 6 splits the library into multiple database tables for - storing content. The library will exist in a library namespace to - to make functions and classes more unique. - -- Databases: - enum DB_Type { - DB_ALBUM, - DB_ARTIST, - DB_GENRE, - DB_LIBRARY, - DB_TRACK, - }; - -- Album: - class library :: Album : public DatabaseEntry { - public: - /* primary_key = "$name.$year.$artist_id" */ - string name; - string name_lower; - unsigned int year; - unsigned int artist_id; - }; - - File << artist_id << year << name - -- Artist and Genre: - class library :: AGInfo : public DatabaseEntry { - public: - string name; /* primary key */ - string name_lower; - }; - - File << name - -- Library: - class library :: Library : public DatabaseEntry { - public: - string root_path; /* primary_key */ - unsigned int count; - bool enabled; - }; - - File << enabled << root_path - -- Track: - The primary key for a track is the full filepath (library.root_path + - track.filepath) - - class library :: Track : public DatabaseEntry { - public: - /* primary_key = library.root_path + "/" + filepath */ - 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; - - string length_str; - string title; - string title_lower; - string filepath; - }; - - File << library_id << artist_id << album_id << genre_id; - File << track << last_year << last_month << last_day << play_count; - File << length << length_str << endl - File << title << endl; - File << filepath << endl; - -- Song: - struct Song { - library :: Album *album; - library :: AGInfo *artist; - library :: AGInfo *genre; - library :: Library *library; - library :: Track *track; - }; - -- Databases: - Database album_db(album.db); - Database artist_db(artist.db); - Database genre_db(genre.db); - Database library_db(library.db); - Database track_db(track.db); - -- Updating algorithm: - 1) Use a single IdleTask to loop over each track in the library, check - if the track still exists in the filesystem and remove it from - library_db if not. - 2) For each directory in the scan directory, create an IdleTask to - scan the next level of directories. - 3) For each file in the scan directory, check if the file already - exists in the track_db and add it to the database if not. Save - each database after adding files. - - The taglib library should be used for finding artist, album, etc. tags - for each track. - -- Importing - Ocarina 5.11 stores library files in ~/.ocarina/library/. Importing - involves reading each file and adding them to the database. If the file - describes a path already in the database then DO NOT overwrite the - current path and instead move on to the next file. If version != 2 then - move on to the next file. - - File format: - File << version << endl; /* version == 2 */ - File << path << endl; - File << id << enabled << next_track_id << size << endl; - File << - - Track format: - File << filepath << endl; - File << title << endl; - File << artist << endl; - File << album << endl; - File << comment << endl; - File << genre << endl; - File << lenstr << endl; - File << id << year << track << count; - File << last_day << last_month << last_year; - File << length << bitrate << sample << channels << banned << endl; - -- 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. - -- API - void library :: init(); - Initialize databases and read files from disk. While reading - the library: - - Update the count of tracks in each library path - - Find the lowercase text of artist, album, genre, track - - void library :: add_path(string dir); - If dir is not a directory: - throw -EINVAL - - Trigger the on_library_add() callback on success. - - void library :: del_path(unsigned int lib_id); - Invalidate a library_db row and all tracks owned by that path - if lib_id is not valid, throw -EEXIST. - - void library :: update_path(lib_id); - Update the given library_db row. - If lib_id is not valid, throw -EEXIST. - - Trigger the on_library_update() callback. - - void library :: update_all(); - Update all library paths. - Trigger the on_library_update() callback for each path. - - void library :: set_enabled(unsigned int id, bool enabled); - Either enable or disable a library path. Trigger - on_library_track_del() for each track disabled this way. - - struct Song library :: lookup(track_id); - Fill out a Song structure for the provided track_id. - Throw -EEXIST if there is no track mapping to track_id. - - struct library :: Library *library :: lookup_path(unsigned int id); - Return the library path with index id. - Throw -EEXIST if there is no such path. - - void library :: import(); - Call this function to import an Ocarina 5.11 style library, - following the "Importing" section above. - -#ifdef CONFIG_TEST - void library :: print_db(DB_Type); - Print the database corresponding to DB_Type - - void library :: reset(); - Clear all databases, returning the library to an empty state. -endif /* CONFIG_TEST */ - diff --git a/design/playlist.txt b/design/playlist.txt deleted file mode 100644 index 748a35e2..00000000 --- a/design/playlist.txt +++ /dev/null @@ -1,53 +0,0 @@ -== Files == - ocarina/include/ - playlist.h - ocarina/lib/ - playlist.cpp - $HOME/.ocarina{-debug}/ - playlist.db - -== Depends == -database - -Playlists: (lib/playlist.cpp) - Playlists are a new feature in Ocarina 6 and are modeled after Gmail - labels. Ocarina 6.0 will support two different playlists that the - user can add tracks to: banned and favorites. - - Future releases will add support for more playlists. - -- Database: - Database playlist_db - -- 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. - -- API - void playlist :: init(): - Load the playlist database. - - void playlist :: add(name, track_id); - Add the track_id to the playlist named "name". Save the - database to disk. - Throw -EEXIST if "name" does not exist. - - void playlist :: del(name, track_id); - Remove the track_id from the playlist named "name". Save the - database to disk. Attempting to remove a track_id that does - not exist is not an error. - Throw -EEXIST if "name" does not exist. - - const std::set &playlist :: get_tracks(name); - Return the set of tracks representing the requested group. - Throw -EEXIST if "name" does not exist. - - void playlist :: clear(); - This function only exists if CONFIG_TEST is enabled. Clear - (reset) the playlist database. diff --git a/design/playqueue.txt b/design/playqueue.txt deleted file mode 100644 index 8772d3ee..00000000 --- a/design/playqueue.txt +++ /dev/null @@ -1,140 +0,0 @@ -== Files == - ocarina/include/ - playqueue.h - ocarina/lib/ - playqueue.cpp - -== Depends == -file - -Playqueue: (lib/playqueue.cpp) - Playqueues are a list of songs that the user has requested to play. - -- Flags: - enum playqueue_flags { - PQ_ENABLED (1 << 0), - PQ_RANDOM (1 << 1), - PQ_REPEAT (1 << 2), - }; - -- Sort order: - enum sort_t { - SORT_ARTIST = 1, - SORT_ALBUM = 2, - SORT_COUNT = 3, - SORT_GENRE = 4, - SORT_LENGTH = 5, - SORT_PLAYED = 6, - SORT_TITLE = 7, - SORT_TRACK = 8, - SORT_YEAR = 9, - }; - -- Playqueue: - class Playqueue { - private: - vector tracks; - list sort_order; /* default = { SORT_ARTIST, - SORT_YEAR, - SORT_TRACK }; - unsigned int cur; - unsigned int flags; - unsigned int length; - public: - Playqueue(flags); - void write(File &); - void read(File &); - - void set_flag(playqueue_flags); - void unset_flag(playqueue_flags); - const unsigned int get_flags(); - string get_length(); - - unsigned int add(track_id); - unsigned int add_front(track_id); - void del(playqueue_id); - unsigned int size(); - - void add_sort(sort_t); - void reset_sort(sort_t); - - unsigned int next(); - void reset_cur(); - } - - File << flags << sort_order.size() << sort_order[0] << ... << sort_order[1]; - File << tracks.size() << tracks[0] << tracks[1] << ... << tracks[N]; - -- API - Playqueue :: Playlist(unsigned int flags); - Create a new playqueue with the appropriate flags set. - sort_order = { (SORT_ARTIST, true), (SORT_YEAR, true), - (SORT_TRACK, true) }; - - unsigned int Playqueue :: add(unsigned int track_id); - unsigned int Playqueue :: add_front(unsigned int track_id); - Add a new track to the tracks vector and return the index. If - add_front is called, the track will be added to the front of - the playqueue (index = 0). - length += track.length. - - void Playqueue :: del(unsigned int playqueue_id); - Erase tracks[playqueue_id] from the tracks vector. - length -= track.length. - - void Playqueue :: del_track(unsigned int track_id); - Erase all tracks with track id track_id. - - void Playqueue :: set_flag(playqueue_flags flag); - void Playqueue :: unset_flag(playqueue_flags flag); - Set or unset the given flag. - - const unsigned int Playqueue :: get_flags(); - Return the currently enabled flags. - - song Playqueue :: get_length(); - Convert the length variable into a string and return the result - to the caller. - - unsigned int Playqueue :: size(); - Return tracks.size(); - - void Playqueue :: write(File &); - void Playqueue :: read(File &); - Read or write the playqueue to the file. - - void Playqueue :: reset_sort(); - Reset the sort_order list to empty. - - void Playqueue :: add_sort(sort_t type, bool ascending); - Add a new term to the sort order. - - void Playqueue :: sort(); - Perform a stable sort on the entire playqueue. Compare tracks - based on the sort_order list. - - unsigned int Playqueue :: next(); - Return the next track_id to play. - - if (tracks.size() == 0) - throw -EEXIST; - - 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)): - length -= track.length; - tracks.erase(cur); - return track; - - void Playqueue :: reset_cur(); - This function is intended to be used by the audio layer when - managing the recently played playqueue. - - cur = 0; diff --git a/design/print.txt b/design/print.txt deleted file mode 100644 index 08b30837..00000000 --- a/design/print.txt +++ /dev/null @@ -1,17 +0,0 @@ -== Files == - ocarina/include/ - print.h - -== Depends == -error - -Printing: (include/print.h> - Sometimes text needs to be printed to the screen so users (or debuggers) - can trace what is going on. - -API: - void print(string fmt, ...); - void dprint(string fmt, ...); - Print text to the screen. The dprint() option will only only - be implemented when when CONFIG_DEBUG or CONFIG_TEST is enabled, - and will be an empty function otherwise. diff --git a/design/version.txt b/design/version.txt deleted file mode 100644 index 3a76e29d..00000000 --- a/design/version.txt +++ /dev/null @@ -1,14 +0,0 @@ -== Files == - ocarina/include/ - version.h - -== Depends == -error - -Versioning: (include/version.h) - This file contains a simple function for returning a string stating - the current version. - -API: - const char *get_version(); - Returns a string describing the current version.