#include <cstdlib>
#include <stdexcept>
#include "jukebox.h"
+#include "playlist.h"
#include "sounddecoder.h"
using namespace std;
Jukebox::Jukebox():
streamer(source),
+ in(0),
decoder(0),
- current_track(tracks.end()),
- shuffle(false)
+ playlist(0),
+ current(0)
{ }
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();
}
streamer.stop();
delete decoder;
decoder = 0;
+ delete in;
+ in = 0;
}
void Jukebox::tick()
#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();
--- /dev/null
+#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
--- /dev/null
+#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