- Cleaned up and generalized Transport event handling

- Added some virtual destructors to interfaces
This commit is contained in:
casey 2016-05-17 22:45:32 -07:00
parent 483760effd
commit 2d65ab480a
22 changed files with 228 additions and 139 deletions

View File

@ -41,23 +41,22 @@ using namespace musik::core::audio;
static std::string TAG = "Player"; static std::string TAG = "Player";
PlayerPtr Player::Create(std::string &url, OutputPtr *output) { PlayerPtr Player::Create(std::string &url, OutputPtr output) {
return PlayerPtr(new Player(url,output)); return PlayerPtr(new Player(url, output));
} }
Player::Player(std::string &url, OutputPtr *output) Player::Player(std::string &url, OutputPtr output)
: volume(1.0) : volume(1.0)
, state(Player::Precache) , state(Player::Precache)
, url(url) , url(url)
, prebufferSizeBytes(0) , prebufferSizeBytes(0)
, maxPrebufferSizeBytes(2000000) , maxPrebufferSizeBytes(2000000)
, currentPosition(0) , currentPosition(0)
, setPosition(-1) , setPosition(-1) {
{
musik::debug::info(TAG, "new instance created"); musik::debug::info(TAG, "new instance created");
if (*output) { if (output) {
this->output = *output; this->output = output;
} }
else { else {
/* if no output is specified, find all output plugins, and select the first one. */ /* if no output is specified, find all output plugins, and select the first one. */

View File

@ -46,18 +46,17 @@
namespace musik { namespace core { namespace audio { namespace musik { namespace core { namespace audio {
class Player; class Player;
class Transport;
typedef std::shared_ptr<Player> PlayerPtr; typedef std::shared_ptr<Player> PlayerPtr;
class Player : public IBufferProvider { class Player : public IBufferProvider {
public: public:
typedef std::shared_ptr<IOutput> OutputPtr; typedef std::shared_ptr<IOutput> OutputPtr;
static PlayerPtr Create(std::string &url,OutputPtr *output = NULL); static PlayerPtr Create(std::string &url, OutputPtr output = OutputPtr());
private: private:
Player(std::string &url,OutputPtr *output); Player(std::string &url, OutputPtr output);
public: public:
~Player(); ~Player();
@ -75,6 +74,8 @@ namespace musik { namespace core { namespace audio {
double Volume(); double Volume();
void SetVolume(double volume); void SetVolume(double volume);
std::string GetUrl() const { return this->url; }
bool Exited(); bool Exited();
public: public:
@ -83,19 +84,13 @@ namespace musik { namespace core { namespace audio {
PlayerEvent PlaybackAlmostEnded; PlayerEvent PlaybackAlmostEnded;
PlayerEvent PlaybackEnded; PlayerEvent PlaybackEnded;
PlayerEvent PlaybackError; PlayerEvent PlaybackError;
OutputPtr output;
private: private:
void ThreadLoop(); void ThreadLoop();
bool PreBuffer(); bool PreBuffer();
int State(); int State();
void ReleaseAllBuffers(); void ReleaseAllBuffers();
protected:
friend class Transport;
std::string url;
private: private:
typedef boost::scoped_ptr<boost::thread> ThreadPtr; typedef boost::scoped_ptr<boost::thread> ThreadPtr;
typedef std::list<BufferPtr> BufferList; typedef std::list<BufferPtr> BufferList;
@ -107,10 +102,13 @@ namespace musik { namespace core { namespace audio {
Quit = 2 Quit = 2
} States; } States;
OutputPtr output;
StreamPtr stream; StreamPtr stream;
ThreadPtr thread; ThreadPtr thread;
BufferList lockedBuffers; BufferList lockedBuffers;
std::string url;
BufferList prebufferQueue; BufferList prebufferQueue;
long prebufferSizeBytes; long prebufferSizeBytes;
long maxPrebufferSizeBytes; long maxPrebufferSizeBytes;

View File

@ -51,7 +51,7 @@ namespace musik {
} }
if (!active_) { if (!active_) {
throw stopped_exception(); return NULL;
} }
log_entry* top = queue_.front(); log_entry* top = queue_.front();
@ -101,8 +101,10 @@ static void thread_proc() {
try { try {
while (!cancel_) { while (!cancel_) {
log_queue::log_entry* entry = queue_->pop_top(); log_queue::log_entry* entry = queue_->pop_top();
debug::string_logged(entry->level_, entry->tag_, entry->message_); if (entry) {
delete entry; debug::string_logged(entry->level_, entry->tag_, entry->message_);
delete entry;
}
} }
} }
catch (log_queue::stopped_exception&) { catch (log_queue::stopped_exception&) {

View File

@ -12,6 +12,8 @@ namespace musik { namespace core {
sigslot::signal0<> PathsUpdated; sigslot::signal0<> PathsUpdated;
sigslot::signal0<> TrackRefreshed; sigslot::signal0<> TrackRefreshed;
virtual ~IIndexer() = 0 { }
virtual void AddPath(const std::string& path) = 0; virtual void AddPath(const std::string& path) = 0;
virtual void RemovePath(const std::string& path) = 0; virtual void RemovePath(const std::string& path) = 0;
virtual void GetPaths(std::vector<std::string>& paths) = 0; virtual void GetPaths(std::vector<std::string>& paths) = 0;

View File

@ -16,6 +16,8 @@ namespace musik { namespace core {
public: public:
sigslot::signal1<QueryPtr> QueryCompleted; sigslot::signal1<QueryPtr> QueryCompleted;
virtual ~ILibrary() = 0 { }
virtual int Enqueue(QueryPtr query, unsigned int options = 0) = 0; virtual int Enqueue(QueryPtr query, unsigned int options = 0) = 0;
virtual IIndexer *Indexer() = 0; virtual IIndexer *Indexer() = 0;
virtual int Id() = 0; virtual int Id() = 0;

View File

@ -24,6 +24,8 @@ namespace musik { namespace core {
Finished = 4, Finished = 4,
} Status; } Status;
virtual ~IQuery() = 0 { }
virtual bool Run(db::Connection &db) = 0; virtual bool Run(db::Connection &db) = 0;
virtual int GetStatus() = 0; virtual int GetStatus() = 0;
virtual int GetId() = 0; virtual int GetId() = 0;

View File

@ -64,7 +64,7 @@ namespace musik { namespace core {
const std::string& libraryPath, const std::string& libraryPath,
const std::string& dbFilename); const std::string& dbFilename);
~Indexer(); virtual ~Indexer();
virtual void AddPath(const std::string& paths); virtual void AddPath(const std::string& paths);
virtual void RemovePath(const std::string& paths); virtual void RemovePath(const std::string& paths);

View File

@ -41,115 +41,143 @@ using namespace musik::core::audio;
static std::string TAG = "Transport"; static std::string TAG = "Transport";
Transport::Transport() Transport::Transport()
:volume(1.0) : volume(1.0) {
,gapless(true)
{
} }
Transport::~Transport(){ Transport::~Transport() {
this->nextPlayer.reset(); this->nextPlayer.reset();
this->currentPlayer.reset(); this->currentPlayer.reset();
this->players.clear();
} }
void Transport::PrepareNextTrack(std::string trackUrl){ void Transport::PrepareNextTrack(std::string trackUrl) {
PlayerPtr player = Player::Create(trackUrl);
if(this->gapless && this->currentPlayer){ {
this->nextPlayer = Player::Create(trackUrl,&this->currentPlayer->output); boost::mutex::scoped_lock lock(this->stateMutex);
this->nextPlayer->Play(); this->currentPlayer = player;
}else{
this->nextPlayer = Player::Create(trackUrl);
} }
} }
void Transport::Start(std::string url){ void Transport::Start(std::string url) {
musik::debug::info(TAG, "we were asked to start the track at " + url); musik::debug::info(TAG, "we were asked to start the track at " + url);
// Check if this is already Prepared PlayerPtr player;
PlayerPtr player = this->nextPlayer;
this->nextPlayer.reset();
musik::debug::info(TAG, "creating a Player..."); {
boost::mutex::scoped_lock lock(this->stateMutex);
// If the nextPlayer wasn't the same as the one started, lets create a new one PlayerPtr newPlayer = this->nextPlayer;
if(!player || player->url != url){ this->nextPlayer.reset();
Player::OutputPtr output;
player = Player::Create(url, &output); musik::debug::info(TAG, "creating a Player...");
player->SetVolume(this->volume);
musik::debug::info(TAG, "Player created successfully"); if (!newPlayer || newPlayer->GetUrl() != url) {
newPlayer = Player::Create(url); /* non-blocking */
newPlayer->SetVolume(this->volume);
musik::debug::info(TAG, "Player created successfully");
}
this->currentPlayer = newPlayer;
this->currentPlayer->PlaybackStarted.connect(this, &Transport::OnPlaybackStarted);
this->currentPlayer->PlaybackAlmostEnded.connect(this, &Transport::OnPlaybackAlmostEnded);
this->currentPlayer->PlaybackEnded.connect(this, &Transport::OnPlaybackEnded);
this->currentPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
musik::debug::info(TAG, "play()");
this->currentPlayer->Play();
player = this->currentPlayer;
} }
// Add to the players this->RaisePlaybackEvent(Transport::EventScheduled, player);
this->players.push_front(player);
this->currentPlayer = player;
musik::debug::info(TAG, "player added to player list");
// Lets connect to the signals of the currentPlayer /* FIXME event binding is reversed here */
this->currentPlayer->PlaybackStarted.connect(this,&Transport::OnPlaybackStarted);
this->currentPlayer->PlaybackAlmostEnded.connect(this,&Transport::OnPlaybackAlmostEnded);
this->currentPlayer->PlaybackEnded.connect(this,&Transport::OnPlaybackEnded);
this->currentPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
musik::debug::info(TAG, "play()");
// Start playing
player->Play();
this->TrackStarted(url);
} }
void Transport::Stop(){ void Transport::Stop() {
musik::debug::info(TAG, "stop"); musik::debug::info(TAG, "stop");
this->players.clear();
this->currentPlayer.reset(); PlayerPtr player = NULL;
this->nextPlayer.reset();
this->PlaybackEnded(); {
boost::mutex::scoped_lock lock(this->stateMutex);
player = this->currentPlayer;
this->currentPlayer.reset();
this->nextPlayer.reset();
}
if (player) {
this->RaisePlaybackEvent(Transport::EventFinished, player);
}
} }
bool Transport::Pause(){ bool Transport::Pause() {
musik::debug::info(TAG, "pause"); musik::debug::info(TAG, "pause");
// pause all players PlayerPtr player;
for(PlayerList::iterator player=this->players.begin();player!=this->players.end();++player){
(*player)->Pause(); {
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer) {
this->currentPlayer->Pause();
player = this->currentPlayer;
}
} }
this->PlaybackPause();
return true; if (player) {
this->RaisePlaybackEvent(Transport::EventPaused, player);
return true;
}
return false;
} }
bool Transport::Resume(){
bool Transport::Resume() {
musik::debug::info(TAG, "resume"); musik::debug::info(TAG, "resume");
// Resume all players PlayerPtr player;
for(PlayerList::iterator player=this->players.begin();player!=this->players.end();++player){
(*player)->Resume(); {
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer) {
this->currentPlayer->Resume();
player = this->currentPlayer;
}
} }
this->PlaybackResume();
return true; if (player) {
this->RaisePlaybackEvent(Transport::EventResumed, player);
return true;
}
return false;
} }
double Transport::Position() {
boost::mutex::scoped_lock lock(this->stateMutex);
double Transport::Position(){ if (this->currentPlayer) {
if(this->currentPlayer){
return this->currentPlayer->Position(); return this->currentPlayer->Position();
} }
return 0; return 0;
} }
void Transport::SetPosition(double seconds){ void Transport::SetPosition(double seconds) {
if(this->currentPlayer){ boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer) {
return this->currentPlayer->SetPosition(seconds); return this->currentPlayer->SetPosition(seconds);
} }
} }
double Transport::Volume() {
double Transport::Volume(){
return this->volume; return this->volume;
} }
void Transport::SetVolume(double volume){ void Transport::SetVolume(double volume) {
double oldVolume = this->volume; double oldVolume = this->volume;
volume = max(0, min(1.0, volume)); volume = max(0, min(1.0, volume));
@ -160,48 +188,90 @@ void Transport::SetVolume(double volume){
this->VolumeChanged(); this->VolumeChanged();
} }
musik::debug::info(TAG, boost::str(boost::format("set volume %d%%") % round(volume * 100))); musik::debug::info(TAG, boost::str(
boost::format("set volume %d%%") % round(volume * 100)));
if(this->currentPlayer){ {
for(PlayerList::iterator player=this->players.begin();player!=this->players.end();++player){ boost::mutex::scoped_lock lock(this->stateMutex);
(*player)->SetVolume(volume);
if (this->currentPlayer) {
this->currentPlayer->SetVolume(volume);
} }
} }
} }
void Transport::OnPlaybackStarted(Player *player){ void Transport::OnPlaybackStarted(Player *player) {
if(this->currentPlayer.get()==player){ PlayerPtr playerForEvent;
this->PlaybackStarted();
{
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer.get() == player) {
playerForEvent = this->currentPlayer;
}
}
if (playerForEvent) {
this->RaisePlaybackEvent(Transport::EventStarted, playerForEvent);
} }
} }
void Transport::OnPlaybackAlmostEnded(Player *player){ void Transport::OnPlaybackAlmostEnded(Player *player) {
if(this->currentPlayer.get()==player){ PlayerPtr playerForEvent;
this->PlaybackAlmostDone();
// Reuse the output {
if(this->nextPlayer && this->gapless){ boost::mutex::scoped_lock lock(this->stateMutex);
// TODO
// this->nex if (this->currentPlayer.get() == player) {
playerForEvent = this->currentPlayer;
} }
} }
this->RaisePlaybackEvent(Transport::EventAlmostDone, playerForEvent);
} }
void Transport::OnPlaybackEnded(Player *player){ void Transport::OnPlaybackEnded(Player *player) {
if(this->currentPlayer.get()==player){ PlayerPtr playerForEvent;
this->PlaybackEnded(); PlayerPtr nextPlayer;
// If the is a nextPlayer, then we should start playing right away {
if(this->nextPlayer){ boost::mutex::scoped_lock lock(this->stateMutex);
this->Start(this->nextPlayer->url.c_str());
if (this->currentPlayer.get() == player) {
playerForEvent = this->currentPlayer;
} }
if (this->nextPlayer) {
nextPlayer = this->nextPlayer;
}
}
if (playerForEvent) {
this->RaisePlaybackEvent(Transport::EventFinished, playerForEvent);
}
if (nextPlayer) {
this->Start(nextPlayer->GetUrl().c_str());
} }
} }
void Transport::OnPlaybackError(Player *player) { void Transport::OnPlaybackError(Player *player) {
this->PlaybackError(); PlayerPtr playerForEvent;
}
{
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer.get() == player) {
playerForEvent = this->currentPlayer;
}
}
if (playerForEvent) {
this->RaisePlaybackEvent(Transport::EventError, playerForEvent);
}
}
void Transport::RaisePlaybackEvent(int type, PlayerPtr player) {
std::string uri = player ? player->GetUrl() : "";
this->PlaybackEvent(type, uri);
}

View File

@ -37,6 +37,7 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <sigslot/sigslot.h> #include <sigslot/sigslot.h>
#include <boost/thread/mutex.hpp>
namespace musik { namespace core { namespace audio { namespace musik { namespace core { namespace audio {
@ -59,26 +60,21 @@ namespace musik { namespace core { namespace audio {
public: public:
typedef enum { typedef enum {
Started = 1, EventScheduled = 0,
Ended = 2, EventStarted = 1,
Error = 3 EventPaused = 2,
} PlaybackStatus; EventResumed = 3,
EventAlmostDone = 4,
EventFinished = 5,
EventError = -1
} PlaybackEventType;
sigslot::signal1<std::string> TrackStarted; sigslot::signal2<int, std::string> PlaybackEvent;
sigslot::signal0<> VolumeChanged; sigslot::signal0<> VolumeChanged;
typedef sigslot::signal1<int> PlaybackStatusEvent;
PlaybackStatusEvent PlaybackStatusChange;
typedef sigslot::signal0<> PlaybackEvent;
PlaybackEvent PlaybackAlmostDone;
PlaybackEvent PlaybackStarted;
PlaybackEvent PlaybackEnded;
PlaybackEvent PlaybackPause;
PlaybackEvent PlaybackResume;
PlaybackEvent PlaybackError;
private: private:
void RaisePlaybackEvent(int type, PlayerPtr player);
void OnPlaybackStarted(Player *player); void OnPlaybackStarted(Player *player);
void OnPlaybackAlmostEnded(Player *player); void OnPlaybackAlmostEnded(Player *player);
void OnPlaybackEnded(Player *player); void OnPlaybackEnded(Player *player);
@ -86,10 +82,8 @@ namespace musik { namespace core { namespace audio {
private: private:
double volume; double volume;
bool gapless;
typedef std::list<PlayerPtr> PlayerList; boost::mutex stateMutex;
PlayerList players;
PlayerPtr currentPlayer; PlayerPtr currentPlayer;
PlayerPtr nextPlayer; PlayerPtr nextPlayer;
}; };

View File

@ -191,6 +191,9 @@ int main(int argc, char* argv[])
if (ch == '\t') { /* tab */ if (ch == '\t') { /* tab */
focusNextInLayout(state); focusNextInLayout(state);
} }
else if (kn == "^D") {
quit = true;
}
else if (kn == "ALT_K") { else if (kn == "ALT_K") {
tp.SetVolume(tp.Volume() + 0.05); /* 5% */ tp.SetVolume(tp.Volume() + 0.05); /* 5% */
} }

View File

@ -29,7 +29,7 @@ TransportWindow::TransportWindow(LibraryPtr library, Transport& transport)
this->library = library; this->library = library;
this->library->QueryCompleted.connect(this, &TransportWindow::OnQueryCompleted); this->library->QueryCompleted.connect(this, &TransportWindow::OnQueryCompleted);
this->transport = &transport; this->transport = &transport;
this->transport->TrackStarted.connect(this, &TransportWindow::OnTransportStarted); this->transport->PlaybackEvent.connect(this, &TransportWindow::OnTransportPlaybackEvent);
this->transport->VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged); this->transport->VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged);
this->paused = false; this->paused = false;
} }
@ -48,9 +48,11 @@ void TransportWindow::ProcessMessage(IWindowMessage &message) {
} }
} }
void TransportWindow::OnTransportStarted(std::string url) { void TransportWindow::OnTransportPlaybackEvent(int eventType, std::string url) {
this->trackQuery.reset(new SingleTrackQuery(url)); if (eventType == Transport::EventStarted) {
this->library->Enqueue(this->trackQuery); this->trackQuery.reset(new SingleTrackQuery(url));
this->library->Enqueue(this->trackQuery);
}
} }
void TransportWindow::OnTransportVolumeChanged() { void TransportWindow::OnTransportVolumeChanged() {

View File

@ -23,7 +23,7 @@ class TransportWindow : public Window, public sigslot::has_slots<> {
void Update(); void Update();
private: private:
void OnTransportStarted(std::string url); void OnTransportPlaybackEvent(int eventType, std::string url);
void OnTransportVolumeChanged(); void OnTransportVolumeChanged();
void OnQueryCompleted(QueryPtr query); void OnQueryCompleted(QueryPtr query);

View File

@ -2,6 +2,8 @@
class IDisplayable { class IDisplayable {
public: public:
virtual ~IDisplayable() = 0 { }
virtual void Show() = 0; virtual void Show() = 0;
virtual void Hide() = 0; virtual void Hide() = 0;
}; };

View File

@ -4,5 +4,7 @@
class IInput { class IInput {
public: public:
virtual ~IInput() = 0 { }
virtual void WriteChar(int64 ch) = 0; virtual void WriteChar(int64 ch) = 0;
}; };

View File

@ -4,5 +4,6 @@
class IKeyHandler { class IKeyHandler {
public: public:
virtual ~IKeyHandler() = 0 { }
virtual void KeyPress(int64 ch) = 0; virtual void KeyPress(int64 ch) = 0;
}; };

View File

@ -5,6 +5,7 @@
class ILayout : public IWindowGroup, public IDisplayable { class ILayout : public IWindowGroup, public IDisplayable {
public: public:
virtual ~ILayout() = 0 { }
virtual IWindow* FocusNext() = 0; virtual IWindow* FocusNext() = 0;
virtual IWindow* FocusPrev() = 0; virtual IWindow* FocusPrev() = 0;
virtual IWindow* GetFocus() = 0; virtual IWindow* GetFocus() = 0;

View File

@ -4,6 +4,8 @@
class IScrollAdapter { class IScrollAdapter {
public: public:
virtual ~IScrollAdapter() = 0 { }
struct ScrollPosition { struct ScrollPosition {
ScrollPosition() { ScrollPosition() {
firstVisibleEntryIndex = 0; firstVisibleEntryIndex = 0;
@ -22,6 +24,7 @@ class IScrollAdapter {
class IEntry { class IEntry {
public: public:
virtual ~IEntry() = 0 { }
virtual size_t GetLineCount() = 0; virtual size_t GetLineCount() = 0;
virtual std::string GetLine(size_t line) = 0; virtual std::string GetLine(size_t line) = 0;
virtual std::string GetValue() = 0; virtual std::string GetValue() = 0;

View File

@ -2,6 +2,7 @@
class IScrollable { class IScrollable {
public: public:
virtual ~IScrollable() = 0 { }
virtual void ScrollToTop() = 0; virtual void ScrollToTop() = 0;
virtual void ScrollToBottom() = 0; virtual void ScrollToBottom() = 0;
virtual void ScrollUp(int delta = 1) = 0; virtual void ScrollUp(int delta = 1) = 0;

View File

@ -7,6 +7,7 @@ class IWindowMessage;
class IWindow : public IDisplayable { class IWindow : public IDisplayable {
public: public:
virtual ~IWindow() = 0 { }
virtual void Repaint() = 0; virtual void Repaint() = 0;
virtual void SetParent(IWindow* parent) = 0; virtual void SetParent(IWindow* parent) = 0;
virtual void Show() = 0; virtual void Show() = 0;

View File

@ -4,6 +4,7 @@
class IWindowGroup { class IWindowGroup {
public: public:
virtual ~IWindowGroup() = 0 { }
virtual bool AddWindow(IWindowPtr window) = 0; virtual bool AddWindow(IWindowPtr window) = 0;
virtual bool RemoveWindow(IWindowPtr window) = 0; virtual bool RemoveWindow(IWindowPtr window) = 0;
virtual size_t GetWindowCount() = 0; virtual size_t GetWindowCount() = 0;

View File

@ -6,6 +6,7 @@
class IWindowMessage { class IWindowMessage {
public: public:
virtual ~IWindowMessage() = 0 { }
virtual IWindow* Target() = 0; virtual IWindow* Target() = 0;
virtual int MessageType() = 0; virtual int MessageType() = 0;
virtual int64 UserData1() = 0; virtual int64 UserData1() = 0;

View File

@ -45,7 +45,7 @@ void Window::ProcessMessage(IWindowMessage &message) {
} }
bool Window::IsAcceptingMessages() { bool Window::IsAcceptingMessages() {
return this->IsVisible(); return true; //this->IsVisible();
} }
bool Window::IsVisible() { bool Window::IsVisible() {
@ -260,14 +260,16 @@ void Window::Clear() {
} }
void Window::Repaint() { void Window::Repaint() {
if (this->frame && this->content) { if (this->isVisible) {
wnoutrefresh(this->frame); if (this->frame && this->content) {
wnoutrefresh(this->frame);
if (this->frame != this->content) { if (this->frame != this->content) {
wnoutrefresh(this->content); wnoutrefresh(this->content);
}
drawPending = true;
} }
drawPending = true;
} }
} }