- 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";
PlayerPtr Player::Create(std::string &url, OutputPtr *output) {
return PlayerPtr(new Player(url,output));
PlayerPtr Player::Create(std::string &url, OutputPtr output) {
return PlayerPtr(new Player(url, output));
}
Player::Player(std::string &url, OutputPtr *output)
Player::Player(std::string &url, OutputPtr output)
: volume(1.0)
, state(Player::Precache)
, url(url)
, prebufferSizeBytes(0)
, maxPrebufferSizeBytes(2000000)
, currentPosition(0)
, setPosition(-1)
{
, setPosition(-1) {
musik::debug::info(TAG, "new instance created");
if (*output) {
this->output = *output;
if (output) {
this->output = output;
}
else {
/* 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 {
class Player;
class Transport;
class Player;
typedef std::shared_ptr<Player> PlayerPtr;
class Player : public IBufferProvider {
public:
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:
Player(std::string &url,OutputPtr *output);
Player(std::string &url, OutputPtr output);
public:
~Player();
@ -75,6 +74,8 @@ namespace musik { namespace core { namespace audio {
double Volume();
void SetVolume(double volume);
std::string GetUrl() const { return this->url; }
bool Exited();
public:
@ -84,18 +85,12 @@ namespace musik { namespace core { namespace audio {
PlayerEvent PlaybackEnded;
PlayerEvent PlaybackError;
OutputPtr output;
private:
void ThreadLoop();
bool PreBuffer();
int State();
void ReleaseAllBuffers();
protected:
friend class Transport;
std::string url;
private:
typedef boost::scoped_ptr<boost::thread> ThreadPtr;
typedef std::list<BufferPtr> BufferList;
@ -107,10 +102,13 @@ namespace musik { namespace core { namespace audio {
Quit = 2
} States;
OutputPtr output;
StreamPtr stream;
ThreadPtr thread;
BufferList lockedBuffers;
std::string url;
BufferList prebufferQueue;
long prebufferSizeBytes;
long maxPrebufferSizeBytes;

View File

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

View File

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

View File

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

View File

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

View File

@ -64,7 +64,7 @@ namespace musik { namespace core {
const std::string& libraryPath,
const std::string& dbFilename);
~Indexer();
virtual ~Indexer();
virtual void AddPath(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";
Transport::Transport()
:volume(1.0)
,gapless(true)
{
: volume(1.0) {
}
Transport::~Transport(){
Transport::~Transport() {
this->nextPlayer.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);
this->nextPlayer->Play();
}else{
this->nextPlayer = Player::Create(trackUrl);
{
boost::mutex::scoped_lock lock(this->stateMutex);
this->currentPlayer = player;
}
}
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);
// Check if this is already Prepared
PlayerPtr player = this->nextPlayer;
this->nextPlayer.reset();
PlayerPtr player;
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
if(!player || player->url != url){
Player::OutputPtr output;
PlayerPtr newPlayer = this->nextPlayer;
this->nextPlayer.reset();
player = Player::Create(url, &output);
player->SetVolume(this->volume);
musik::debug::info(TAG, "creating a Player...");
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->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);
this->RaisePlaybackEvent(Transport::EventScheduled, player);
}
void Transport::Stop(){
void Transport::Stop() {
musik::debug::info(TAG, "stop");
this->players.clear();
this->currentPlayer.reset();
this->nextPlayer.reset();
this->PlaybackEnded();
PlayerPtr player = NULL;
{
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");
// pause all players
for(PlayerList::iterator player=this->players.begin();player!=this->players.end();++player){
(*player)->Pause();
PlayerPtr player;
{
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");
// Resume all players
for(PlayerList::iterator player=this->players.begin();player!=this->players.end();++player){
(*player)->Resume();
PlayerPtr player;
{
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 0;
}
void Transport::SetPosition(double seconds){
if(this->currentPlayer){
void Transport::SetPosition(double seconds) {
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer) {
return this->currentPlayer->SetPosition(seconds);
}
}
double Transport::Volume(){
double Transport::Volume() {
return this->volume;
}
void Transport::SetVolume(double volume){
void Transport::SetVolume(double volume) {
double oldVolume = this->volume;
volume = max(0, min(1.0, volume));
@ -160,48 +188,90 @@ void Transport::SetVolume(double volume){
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){
(*player)->SetVolume(volume);
{
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer) {
this->currentPlayer->SetVolume(volume);
}
}
}
void Transport::OnPlaybackStarted(Player *player){
if(this->currentPlayer.get()==player){
this->PlaybackStarted();
void Transport::OnPlaybackStarted(Player *player) {
PlayerPtr playerForEvent;
{
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){
if(this->currentPlayer.get()==player){
this->PlaybackAlmostDone();
void Transport::OnPlaybackAlmostEnded(Player *player) {
PlayerPtr playerForEvent;
// Reuse the output
if(this->nextPlayer && this->gapless){
// TODO
// this->nex
{
boost::mutex::scoped_lock lock(this->stateMutex);
if (this->currentPlayer.get() == player) {
playerForEvent = this->currentPlayer;
}
}
this->RaisePlaybackEvent(Transport::EventAlmostDone, playerForEvent);
}
void Transport::OnPlaybackEnded(Player *player){
if(this->currentPlayer.get()==player){
this->PlaybackEnded();
void Transport::OnPlaybackEnded(Player *player) {
PlayerPtr playerForEvent;
PlayerPtr nextPlayer;
// If the is a nextPlayer, then we should start playing right away
if(this->nextPlayer){
this->Start(this->nextPlayer->url.c_str());
{
boost::mutex::scoped_lock lock(this->stateMutex);
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) {
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/scoped_ptr.hpp>
#include <sigslot/sigslot.h>
#include <boost/thread/mutex.hpp>
namespace musik { namespace core { namespace audio {
@ -59,26 +60,21 @@ namespace musik { namespace core { namespace audio {
public:
typedef enum {
Started = 1,
Ended = 2,
Error = 3
} PlaybackStatus;
EventScheduled = 0,
EventStarted = 1,
EventPaused = 2,
EventResumed = 3,
EventAlmostDone = 4,
EventFinished = 5,
EventError = -1
} PlaybackEventType;
sigslot::signal1<std::string> TrackStarted;
sigslot::signal2<int, std::string> PlaybackEvent;
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:
void RaisePlaybackEvent(int type, PlayerPtr player);
void OnPlaybackStarted(Player *player);
void OnPlaybackAlmostEnded(Player *player);
void OnPlaybackEnded(Player *player);
@ -86,10 +82,8 @@ namespace musik { namespace core { namespace audio {
private:
double volume;
bool gapless;
typedef std::list<PlayerPtr> PlayerList;
PlayerList players;
boost::mutex stateMutex;
PlayerPtr currentPlayer;
PlayerPtr nextPlayer;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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