]> git.tdb.fi Git - libs/al.git/commitdiff
Externalize playlist management from Jukebox
authorMikko Rasa <tdb@tdb.fi>
Sun, 13 Oct 2013 08:00:22 +0000 (11:00 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 13 Oct 2013 08:00:22 +0000 (11:00 +0300)
This enables playlists to be stored and loaded from files.  There's also
support for streaming from data packs.

source/jukebox.cpp
source/jukebox.h
source/playlist.cpp [new file with mode: 0644]
source/playlist.h [new file with mode: 0644]

index 52f673e796c49bf875de112c9a0ecb937d7e630e..b753e6b9eb58bf391a13d2e5b0ef42735b8c9045 100644 (file)
@@ -2,6 +2,7 @@
 #include <cstdlib>
 #include <stdexcept>
 #include "jukebox.h"
+#include "playlist.h"
 #include "sounddecoder.h"
 
 using namespace std;
@@ -11,9 +12,10 @@ namespace AL {
 
 Jukebox::Jukebox():
        streamer(source),
+       in(0),
        decoder(0),
-       current_track(tracks.end()),
-       shuffle(false)
+       playlist(0),
+       current(0)
 { }
 
 Jukebox::~Jukebox()
@@ -22,100 +24,44 @@ Jukebox::~Jukebox()
        delete decoder;
 }
 
-void Jukebox::add_track(const string &trk)
-{
-       bool was_empty = tracks.empty();
-       tracks.push_back(trk);
-       if(was_empty)
-       {
-               current_track = tracks.begin();
-               signal_track_changed.emit(*current_track);
-       }
-}
-
-void Jukebox::remove_track(const string &trk)
-{
-       list<string>::iterator i = find(tracks.begin(), tracks.end(), trk);
-       if(i!=tracks.end())
-       {
-               if(i==current_track)
-                       next();
-               tracks.erase(i);
-               if(tracks.empty())
-                       current_track = tracks.end();
-       }
-}
-
-void Jukebox::clear_tracks()
+void Jukebox::set_playlist(const Playlist *p)
 {
+       bool was_playing = decoder;
        stop();
-       tracks.clear();
-       current_track = tracks.end();
-}
-
-const string &Jukebox::get_current_track() const
-{
-       if(tracks.empty())
-               throw logic_error("No current track");
-       return *current_track;
-}
-
-void Jukebox::set_shuffle(bool s)
-{
-       shuffle = s;
+       playlist = p;
+       if(playlist && was_playing)
+               play();
 }
 
 void Jukebox::play()
 {
-       if(tracks.empty() || decoder)
+       if(!playlist || playlist->empty() || decoder)
                return;
 
-       decoder = SoundDecoder::open_file(*current_track);
+       in = playlist->open(current);
+       decoder = SoundDecoder::open_io(*in);
        streamer.play(*decoder);
 }
 
 void Jukebox::next()
 {
+       if(!playlist || playlist->empty())
+               return;
+
        stop();
-       if(tracks.size()>1)
-       {
-               if(shuffle)
-               {
-                       while(1)
-                       {
-                               list<string>::iterator i = tracks.begin();
-                               advance(i, rand()%tracks.size());
-                               if(i!=current_track)
-                               {
-                                       current_track = i;
-                                       break;
-                               }
-                       }
-               }
-               else
-               {
-                       ++current_track;
-                       if(current_track==tracks.end())
-                               current_track = tracks.begin();
-               }
-               signal_track_changed.emit(*current_track);
-       }
+       current = playlist->advance(current, 1);
+       signal_track_changed.emit(current);
        play();
 }
 
 void Jukebox::previous()
 {
-       if(shuffle)
-               return next();
+       if(!playlist || playlist->empty())
+               return;
 
        stop();
-       if(tracks.size()>1)
-       {
-               if(current_track==tracks.begin())
-                       current_track = tracks.end();
-               --current_track;
-               signal_track_changed.emit(*current_track);
-       }
+       current = playlist->advance(current, -1);
+       signal_track_changed.emit(current);
        play();
 }
 
@@ -124,6 +70,8 @@ void Jukebox::stop()
        streamer.stop();
        delete decoder;
        decoder = 0;
+       delete in;
+       in = 0;
 }
 
 void Jukebox::tick()
index fe17f2e4a58f456338eff200046f577e80f61498..ea19723451649aa8ad0374b4ad19515318fba065 100644 (file)
@@ -4,37 +4,37 @@
 #include <list>
 #include <string>
 #include <sigc++/signal.h>
+#include <msp/io/seekable.h>
 #include "source.h"
 #include "streamer.h"
 
 namespace Msp {
 namespace AL {
 
+class Playlist;
 class SoundDecoder;
 
 class Jukebox
 {
 public:
-       sigc::signal<void, const std::string &> signal_track_changed;
+       sigc::signal<void, unsigned> signal_track_changed;
 
 private:
        Source source;
        Streamer streamer;
+       IO::Seekable *in;
        SoundDecoder *decoder;
-       std::list<std::string> tracks;
-       std::list<std::string>::iterator current_track;
-       bool shuffle;
+       const Playlist *playlist;
+       unsigned current;
 
 public:
        Jukebox();
        ~Jukebox();
 
        Source &get_source() { return source; }
-       void add_track(const std::string &);
-       void remove_track(const std::string &);
-       void clear_tracks();
-       const std::string &get_current_track() const;
-       void set_shuffle(bool);
+       void set_playlist(const Playlist *);
+       const Playlist *get_playlist() const { return playlist; }
+       unsigned get_current_track() const { return current; }
 
        void play();
        void next();
diff --git a/source/playlist.cpp b/source/playlist.cpp
new file mode 100644 (file)
index 0000000..10640ee
--- /dev/null
@@ -0,0 +1,120 @@
+#include <cstdlib>
+#include <msp/datafile/collection.h>
+#include "playlist.h"
+
+using namespace std;
+
+namespace Msp {
+namespace AL {
+
+Playlist::Playlist():
+       shuffle(false)
+{ }
+
+void Playlist::add_track(const string &trk)
+{
+       tracks.push_back(trk);
+}
+
+void Playlist::remove_track(const string &trk)
+{
+       for(vector<Track>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+               if(i->filename==trk)
+               {
+                       tracks.erase(i);
+                       break;
+               }
+}
+
+void Playlist::clear_tracks()
+{
+       tracks.clear();
+}
+
+void Playlist::set_shuffle(bool s)
+{
+       shuffle = s;
+}
+
+const string &Playlist::get_track(unsigned i) const
+{
+       if(i>=tracks.size())
+               throw out_of_range("Playlist::get_track");
+       return tracks[i].filename;
+}
+
+unsigned Playlist::advance(unsigned i, int d) const
+{
+       unsigned s = tracks.size();
+       if(i>=s)
+               throw out_of_range("Playlist::next_track");
+
+       if(shuffle)
+       {
+               if(s>1)
+               {
+                       unsigned j = i;
+                       while(j==i)
+                               j = rand()%s;
+                       i = j;
+               }
+       }
+       else if(d>0)
+               i = (i+d)%s;
+       else
+               i = s-1-(s-1-i-d)%s;
+
+       return i;
+}
+
+IO::Seekable *Playlist::open(unsigned i) const
+{
+       if(i>=tracks.size())
+               throw out_of_range("Playlist::open");
+
+       const Track &track = tracks[i];
+       if(track.collection)
+               return track.collection->open_raw(track.filename);
+
+       return new IO::BufferedFile(track.filename);
+}
+
+
+Playlist::Track::Track():
+       collection(0)
+{ }
+
+Playlist::Track::Track(const string &fn):
+       collection(0),
+       filename(fn)
+{ }
+
+
+Playlist::Loader::Loader(Playlist &p):
+       DataFile::CollectionObjectLoader<Playlist>(p, 0)
+{
+       init();
+}
+
+Playlist::Loader::Loader(Playlist &p, Collection &c):
+       DataFile::CollectionObjectLoader<Playlist>(p, &c)
+{
+       init();
+}
+
+void Playlist::Loader::init()
+{
+       add("track",   &Loader::track);
+       add("shuffle", &Playlist::shuffle);
+}
+
+void Playlist::Loader::track(const string &fn)
+{
+       Track trk;
+       trk.collection = coll;
+       trk.filename = fn;
+       obj.tracks.push_back(trk);
+}
+
+} // namespace AL
+} // namespace Msp
diff --git a/source/playlist.h b/source/playlist.h
new file mode 100644 (file)
index 0000000..72226ad
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef MSP_AL_PLAYLIST_H_
+#define MSP_AL_PLAYLIST_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+
+namespace Msp {
+namespace AL {
+
+class Playlist
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<Playlist>
+       {
+       public:
+               Loader(Playlist &);
+               Loader(Playlist &, Collection &);
+       private:
+               void init();
+
+               void track(const std::string &);
+       };
+
+private:
+       struct Track
+       {
+               const DataFile::Collection *collection;
+               std::string filename;
+
+               Track();
+               Track(const std::string &);
+       };
+
+       bool shuffle;
+       std::vector<Track> tracks;
+
+public:
+       Playlist();
+
+       void add_track(const std::string &);
+       void remove_track(const std::string &);
+       void clear_tracks();
+       void set_shuffle(bool);
+       const std::string &get_track(unsigned) const;
+       unsigned size() const { return tracks.size(); }
+       bool empty() const { return tracks.empty(); }
+
+       unsigned advance(unsigned, int = 1) const;
+       IO::Seekable *open(unsigned) const;
+};
+
+} // namespace AL
+} // namespace Msp
+
+#endif