mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-11 00:40:00 +00:00
Important work towards eventual support of gapless playback:
- Transport: cleaned up memory management model -- shared_ptr<Player> was causing lots of unnecessary complication. The players are encapsulated, so switched to just Player* - Player's destructor wasn't virtual! Caused some strange problems. - Integrated next track loading in PlayerService!
This commit is contained in:
parent
65a4d33823
commit
b544ea6f1f
@ -41,16 +41,16 @@ using namespace musik::core::audio;
|
||||
|
||||
static std::string TAG = "Player";
|
||||
|
||||
PlayerPtr Player::Create(std::string &url, OutputPtr output) {
|
||||
PlayerPtr Player::Create(const std::string &url, OutputPtr output) {
|
||||
return PlayerPtr(new Player(url, output));
|
||||
}
|
||||
|
||||
Player::Player(std::string &url, OutputPtr output)
|
||||
Player::Player(const std::string &url, OutputPtr output)
|
||||
: volume(1.0)
|
||||
, state(Player::Precache)
|
||||
, url(url)
|
||||
, prebufferSizeBytes(0)
|
||||
, maxPrebufferSizeBytes(2000000)
|
||||
, maxPrebufferSizeBytes(100000)
|
||||
, currentPosition(0)
|
||||
, setPosition(-1) {
|
||||
musik::debug::info(TAG, "new instance created");
|
||||
|
@ -53,12 +53,9 @@ namespace musik { namespace core { namespace audio {
|
||||
public:
|
||||
typedef std::shared_ptr<IOutput> OutputPtr;
|
||||
|
||||
static PlayerPtr Create(std::string &url, OutputPtr output = OutputPtr());
|
||||
|
||||
private:
|
||||
Player(std::string &url, OutputPtr output);
|
||||
static PlayerPtr Create(const std::string &url, OutputPtr output = OutputPtr());
|
||||
|
||||
public:
|
||||
Player(const std::string &url, OutputPtr output = OutputPtr());
|
||||
~Player();
|
||||
|
||||
virtual void OnBufferProcessed(IBuffer *buffer);
|
||||
|
@ -75,7 +75,7 @@ namespace musik { namespace core { namespace audio {
|
||||
BufferPtr GetEmptyBuffer();
|
||||
void LoadDecoderPlugins();
|
||||
|
||||
private:
|
||||
private:
|
||||
typedef std::list<BufferPtr> BufferList;
|
||||
typedef std::shared_ptr<IDecoderFactory> DecoderFactoryPtr;
|
||||
typedef std::vector<DecoderFactoryPtr> DecoderFactoryList;
|
||||
|
@ -64,7 +64,7 @@ inline std::string u16to8(const std::wstring& u16) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static int u8len(const std::string& str) {
|
||||
inline static size_t u8len(const std::string& str) {
|
||||
try {
|
||||
return utf8::distance(str.begin(), str.end());
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ void LibraryTrack::SetThumbnail(const char *data, long size) {
|
||||
}
|
||||
|
||||
std::string LibraryTrack::URI() {
|
||||
return this->GetValue("path");
|
||||
return this->GetValue("filename");
|
||||
}
|
||||
|
||||
Track::MetadataIteratorRange LibraryTrack::GetValues(const char* metakey) {
|
||||
|
@ -35,111 +35,124 @@
|
||||
|
||||
#include <core/debug.h>
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
|
||||
#include <boost/thread/future.hpp>
|
||||
|
||||
using namespace musik::core::audio;
|
||||
|
||||
static std::string TAG = "Transport";
|
||||
|
||||
#define RESET_NEXT_PLAYER() \
|
||||
delete this->nextPlayer; \
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
static void pausePlayer(Player* p) {
|
||||
p->Pause();
|
||||
}
|
||||
|
||||
static void resumePlayer(Player* p) {
|
||||
p->Resume();
|
||||
}
|
||||
|
||||
static void deletePlayer(Player* p) {
|
||||
delete p;
|
||||
}
|
||||
|
||||
Transport::Transport()
|
||||
: volume(1.0)
|
||||
, state(StateStopped) {
|
||||
, state(PlaybackStopped)
|
||||
, nextPlayer(NULL) {
|
||||
}
|
||||
|
||||
Transport::~Transport() {
|
||||
this->nextPlayer.reset();
|
||||
this->currentPlayer.reset();
|
||||
}
|
||||
|
||||
Transport::PlaybackState Transport::GetPlaybackState() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Transport::PrepareNextTrack(std::string trackUrl) {
|
||||
PlayerPtr player = Player::Create(trackUrl);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
this->currentPlayer = player;
|
||||
}
|
||||
void Transport::PrepareNextTrack(const std::string& trackUrl) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->nextPlayer = new Player(trackUrl);
|
||||
}
|
||||
|
||||
void Transport::Start(std::string url) {
|
||||
void Transport::Start(const std::string& url) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
|
||||
/* TODO FIXME: hack; the player is reference counted, we don't want the count
|
||||
to reach zero within the critical section, because its background thread may
|
||||
raise an event and cause a deadlock. do we really need shared_ptrs for these
|
||||
Player instances? */
|
||||
PlayerPtr current;
|
||||
Player* newPlayer = new Player(url);
|
||||
newPlayer->SetVolume(this->volume);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
|
||||
PlayerPtr player;
|
||||
this->StartWithPlayer(newPlayer);
|
||||
}
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
void Transport::StartWithPlayer(Player* newPlayer) {
|
||||
if (newPlayer) {
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
PlayerPtr newPlayer = this->nextPlayer;
|
||||
this->nextPlayer.reset();
|
||||
if (newPlayer != nextPlayer) {
|
||||
delete nextPlayer;
|
||||
}
|
||||
|
||||
musik::debug::info(TAG, "creating a Player...");
|
||||
this->nextPlayer = NULL;
|
||||
|
||||
if (!newPlayer || newPlayer->GetUrl() != url) {
|
||||
newPlayer = Player::Create(url); /* non-blocking */
|
||||
newPlayer->SetVolume(this->volume);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
newPlayer->PlaybackStarted.connect(this, &Transport::OnPlaybackStarted);
|
||||
newPlayer->PlaybackAlmostEnded.connect(this, &Transport::OnPlaybackAlmostEnded);
|
||||
newPlayer->PlaybackEnded.connect(this, &Transport::OnPlaybackEnded);
|
||||
newPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
|
||||
|
||||
musik::debug::info(TAG, "play()");
|
||||
|
||||
this->active.push_front(newPlayer);
|
||||
newPlayer->Play();
|
||||
}
|
||||
|
||||
current = this->currentPlayer; /* see hack note above. */
|
||||
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(); /* non-blocking */
|
||||
|
||||
player = this->currentPlayer;
|
||||
this->RaiseStreamEvent(Transport::StreamScheduled, newPlayer);
|
||||
}
|
||||
|
||||
this->RaisePlaybackEvent(Transport::EventScheduled, player);
|
||||
}
|
||||
|
||||
|
||||
void Transport::Stop() {
|
||||
musik::debug::info(TAG, "stop");
|
||||
|
||||
PlayerPtr player = NULL;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
std::list<Player*> toDelete;
|
||||
|
||||
player = this->currentPlayer;
|
||||
this->currentPlayer.reset();
|
||||
this->nextPlayer.reset();
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
RESET_NEXT_PLAYER();
|
||||
std::swap(toDelete, this->active);
|
||||
}
|
||||
|
||||
/* do the actual delete outside of the critical section! the players run
|
||||
in a background thread that will emit a signal on completion, but the
|
||||
destructor joins(). */
|
||||
std::for_each(toDelete.begin(), toDelete.end(), deletePlayer);
|
||||
this->active.clear();
|
||||
|
||||
if (player) {
|
||||
this->RaisePlaybackEvent(Transport::EventStopped, player);
|
||||
this->SetPlaybackState(PlaybackStopped);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::Pause() {
|
||||
musik::debug::info(TAG, "pause");
|
||||
|
||||
PlayerPtr player;
|
||||
size_t count = 0;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
this->currentPlayer->Pause();
|
||||
player = this->currentPlayer;
|
||||
}
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
std::for_each(this->active.begin(), this->active.end(), pausePlayer);
|
||||
count = this->active.size();
|
||||
}
|
||||
|
||||
if (player) {
|
||||
this->RaisePlaybackEvent(Transport::EventPaused, player);
|
||||
if (count) {
|
||||
this->SetPlaybackState(PlaybackPaused);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -149,19 +162,16 @@ bool Transport::Pause() {
|
||||
bool Transport::Resume() {
|
||||
musik::debug::info(TAG, "resume");
|
||||
|
||||
PlayerPtr player;
|
||||
size_t count = 0;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
this->currentPlayer->Resume();
|
||||
player = this->currentPlayer;
|
||||
}
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
std::for_each(this->active.begin(), this->active.end(), resumePlayer);
|
||||
count = this->active.size();
|
||||
}
|
||||
|
||||
if (player) {
|
||||
this->RaisePlaybackEvent(Transport::EventResumed, player);
|
||||
if (count) {
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -169,29 +179,30 @@ bool Transport::Resume() {
|
||||
}
|
||||
|
||||
double Transport::Duration() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
return this->currentPlayer->Position();
|
||||
if (!this->active.empty()) {
|
||||
return this->active.front()->Position();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double Transport::Position() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
return this->currentPlayer->Position();
|
||||
if (!this->active.empty()) {
|
||||
return this->active.front()->Position();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Transport::SetPosition(double seconds) {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
return this->currentPlayer->SetPosition(seconds);
|
||||
if (!this->active.empty()) {
|
||||
return this->active.front()->SetPosition(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,104 +225,67 @@ void Transport::SetVolume(double volume) {
|
||||
boost::format("set volume %d%%") % round(volume * 100)));
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer) {
|
||||
this->currentPlayer->SetVolume(volume);
|
||||
if (!this->active.empty()) {
|
||||
this->active.front()->SetVolume(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackStarted(Player *player) {
|
||||
PlayerPtr playerForEvent;
|
||||
void Transport::OnPlaybackStarted(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamPlaying, player);
|
||||
this->SetPlaybackState(Transport::PlaybackPlaying);
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackAlmostEnded(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamAlmostDone, player);
|
||||
}
|
||||
|
||||
void Transport::RemoveActive(Player* player) {
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
std::list<Player*>::iterator it =
|
||||
std::find(this->active.begin(), this->active.end(), player);
|
||||
|
||||
if (it != this->active.end()) {
|
||||
delete (*it);
|
||||
this->active.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackEnded(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamFinished, player);
|
||||
|
||||
if (this->nextPlayer) {
|
||||
this->StartWithPlayer(this->nextPlayer);
|
||||
}
|
||||
else {
|
||||
this->SetPlaybackState(Transport::PlaybackStopped);
|
||||
}
|
||||
|
||||
boost::async(boost::bind(&Transport::RemoveActive, this, player));
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackError(Player* player) {
|
||||
this->RaiseStreamEvent(Transport::StreamError, player);
|
||||
this->SetPlaybackState(Transport::PlaybackStopped);
|
||||
boost::async(boost::bind(&Transport::RemoveActive, this, player));
|
||||
}
|
||||
|
||||
void Transport::SetPlaybackState(int state) {
|
||||
bool changed = false;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
|
||||
if (this->currentPlayer.get() == player) {
|
||||
playerForEvent = this->currentPlayer;
|
||||
}
|
||||
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
|
||||
this->state = (PlaybackState) state;
|
||||
}
|
||||
|
||||
if (playerForEvent) {
|
||||
this->RaisePlaybackEvent(Transport::EventPlaying, playerForEvent);
|
||||
if (changed) {
|
||||
this->PlaybackEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackAlmostEnded(Player *player) {
|
||||
PlayerPtr playerForEvent;
|
||||
|
||||
{
|
||||
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) {
|
||||
PlayerPtr playerForEvent;
|
||||
PlayerPtr nextPlayer;
|
||||
|
||||
{
|
||||
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::EventStopped, playerForEvent);
|
||||
}
|
||||
|
||||
if (nextPlayer) {
|
||||
this->Start(nextPlayer->GetUrl().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnPlaybackError(Player *player) {
|
||||
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) {
|
||||
/* TODO FIXME: should be either decoupled or merged with the playback
|
||||
event enum. */
|
||||
switch (type) {
|
||||
case EventPlaying:
|
||||
case EventResumed:
|
||||
this->state = StatePlaying;
|
||||
break;
|
||||
|
||||
case EventStopped:
|
||||
case EventError:
|
||||
this->state = StateStopped;
|
||||
break;
|
||||
|
||||
case EventPaused:
|
||||
this->state = StatePaused;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string uri = player ? player->GetUrl() : "";
|
||||
this->PlaybackEvent(type, uri);
|
||||
void Transport::RaiseStreamEvent(int type, Player* player) {
|
||||
this->StreamEvent(type, player->GetUrl());
|
||||
}
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include <core/config.h>
|
||||
#include <core/audio/Player.h>
|
||||
#include <core/sdk/IOutput.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <sigslot/sigslot.h>
|
||||
@ -43,30 +44,29 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
class Transport : public sigslot::has_slots<> {
|
||||
public:
|
||||
sigslot::signal2<int, std::string> PlaybackEvent;
|
||||
sigslot::signal2<int, std::string> StreamEvent;
|
||||
sigslot::signal1<int> PlaybackEvent;
|
||||
sigslot::signal0<> VolumeChanged;
|
||||
|
||||
typedef enum {
|
||||
StateStopped,
|
||||
StatePaused,
|
||||
StatePlaying
|
||||
PlaybackStopped,
|
||||
PlaybackPaused,
|
||||
PlaybackPlaying
|
||||
} PlaybackState;
|
||||
|
||||
typedef enum {
|
||||
EventScheduled = 0,
|
||||
EventPlaying = 1,
|
||||
EventPaused = 2,
|
||||
EventResumed = 3,
|
||||
EventAlmostDone = 4,
|
||||
EventStopped = 5,
|
||||
EventError = -1
|
||||
} PlaybackEventType;
|
||||
StreamScheduled = 0,
|
||||
StreamPlaying = 1,
|
||||
StreamAlmostDone = 4,
|
||||
StreamFinished = 5,
|
||||
StreamError = -1
|
||||
} StreamEventType;
|
||||
|
||||
Transport();
|
||||
~Transport();
|
||||
|
||||
void PrepareNextTrack(std::string trackUrl);
|
||||
void Start(std::string trackUrl);
|
||||
void PrepareNextTrack(const std::string& trackUrl);
|
||||
void Start(const std::string& trackUrl);
|
||||
void Stop();
|
||||
bool Pause();
|
||||
bool Resume();
|
||||
@ -81,20 +81,24 @@ namespace musik { namespace core { namespace audio {
|
||||
PlaybackState GetPlaybackState();
|
||||
|
||||
private:
|
||||
void RaisePlaybackEvent(int type, PlayerPtr player);
|
||||
void StartWithPlayer(Player* player);
|
||||
void RemoveActive(Player* player);
|
||||
|
||||
void OnPlaybackStarted(Player *player);
|
||||
void OnPlaybackAlmostEnded(Player *player);
|
||||
void OnPlaybackEnded(Player *player);
|
||||
void OnPlaybackError(Player *player);
|
||||
void RaiseStreamEvent(int type, Player* player);
|
||||
void SetPlaybackState(int state);
|
||||
|
||||
void OnPlaybackStarted(Player* player);
|
||||
void OnPlaybackAlmostEnded(Player* player);
|
||||
void OnPlaybackEnded(Player* player);
|
||||
void OnPlaybackError(Player* player);
|
||||
|
||||
private:
|
||||
double volume;
|
||||
PlaybackState state;
|
||||
|
||||
boost::mutex stateMutex;
|
||||
PlayerPtr currentPlayer;
|
||||
PlayerPtr nextPlayer;
|
||||
boost::recursive_mutex stateMutex;
|
||||
Player* nextPlayer;
|
||||
std::list<Player*> active;
|
||||
|
||||
};
|
||||
|
||||
|
@ -49,9 +49,9 @@
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace musik{ namespace core{
|
||||
namespace musik { namespace core {
|
||||
|
||||
class PluginFactory{
|
||||
class PluginFactory {
|
||||
public:
|
||||
|
||||
static PluginFactory& Instance() {
|
||||
|
@ -44,6 +44,8 @@ namespace musik { namespace core { namespace audio {
|
||||
//////////////////////////////////////////
|
||||
class IBufferProvider {
|
||||
public:
|
||||
virtual ~IBufferProvider() = 0 { }
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Release used by the output to notify the player a buffer has finished
|
||||
|
@ -42,43 +42,11 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
class IOutput {
|
||||
public:
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Destroy the object
|
||||
///
|
||||
///The Destroy method is used so that it's guaranteed that the object is
|
||||
///destroyed inside the right DLL/exe
|
||||
//////////////////////////////////////////
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Pause the current output
|
||||
//////////////////////////////////////////
|
||||
virtual void Pause() = 0;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///resume a paused output
|
||||
//////////////////////////////////////////
|
||||
virtual void Resume() = 0;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Set the volume on this output
|
||||
//////////////////////////////////////////
|
||||
virtual void SetVolume(double volume) = 0;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Clear internal buffers. Used when setting new position in a stream
|
||||
//////////////////////////////////////////
|
||||
virtual void Stop() = 0;
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Play this buffer
|
||||
//////////////////////////////////////////
|
||||
virtual bool Play(IBuffer *buffer, IBufferProvider *provider) = 0;
|
||||
};
|
||||
|
||||
|
@ -39,29 +39,12 @@
|
||||
#include <core/config.h>
|
||||
#include <utf8/utf8.h>
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get the directory where plugin-dlls are located.
|
||||
///
|
||||
///\returns
|
||||
///String with the directory
|
||||
///
|
||||
///\see
|
||||
///<GetApplicationDirectory>
|
||||
//////////////////////////////////////////
|
||||
std::string musik::core::GetPluginDirectory(){
|
||||
std::string sDirectory(GetApplicationDirectory());
|
||||
sDirectory.append("plugins/");
|
||||
return sDirectory;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Get path to where the application is located.
|
||||
///
|
||||
///\returns
|
||||
///String with the path
|
||||
//////////////////////////////////////////
|
||||
std::string musik::core::GetApplicationDirectory() {
|
||||
std::string sDirectory;
|
||||
|
||||
@ -98,17 +81,6 @@ std::string musik::core::GetDataDirectory(){
|
||||
return directory;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
///\brief
|
||||
///Find out the full path to a file.
|
||||
///
|
||||
///\param sFile
|
||||
///File to get the full path to.
|
||||
///
|
||||
///\returns
|
||||
///String with path.
|
||||
//////////////////////////////////////////
|
||||
std::string musik::core::GetPath(const std::string &sFile){
|
||||
|
||||
std::string sPath;
|
||||
|
@ -39,28 +39,12 @@
|
||||
#include <string>
|
||||
#include <core/config.h>
|
||||
|
||||
namespace musik{ namespace core{
|
||||
namespace musik { namespace core {
|
||||
|
||||
/*****************************
|
||||
Path to where the executable is located.
|
||||
*****************************/
|
||||
std::string GetApplicationDirectory();
|
||||
|
||||
/*****************************
|
||||
Path to where the executable is located.
|
||||
*****************************/
|
||||
std::string GetDataDirectory();
|
||||
|
||||
/*****************************
|
||||
Get the full path of the sFile
|
||||
*****************************/
|
||||
std::string GetPath(const std::string &sFile);
|
||||
|
||||
/*****************************
|
||||
Path to where plugins are located.
|
||||
*****************************/
|
||||
std::string GetPluginDirectory();
|
||||
|
||||
UINT64 Checksum(char *data,unsigned int bytes);
|
||||
|
||||
} }
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <app/layout/LibraryLayout.h>
|
||||
#include <app/window/OutputWindow.h>
|
||||
#include <app/util/GlobalHotkeys.h>
|
||||
#include <app/service/PlaybackService.h>
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
@ -171,12 +172,14 @@ int main(int argc, char* argv[])
|
||||
Transport tp;
|
||||
tp.SetVolume(0.75);
|
||||
|
||||
PlaybackService playback(tp);
|
||||
|
||||
using musik::core::LibraryFactory;
|
||||
LibraryPtr library = LibraryFactory::Libraries().at(0);
|
||||
|
||||
GlobalHotkeys globalHotkeys(tp, library);
|
||||
|
||||
ILayoutPtr libraryLayout(new LibraryLayout(tp, library));
|
||||
ILayoutPtr libraryLayout(new LibraryLayout(playback, library));
|
||||
ILayoutPtr consoleLayout(new MainLayout(tp, library));
|
||||
|
||||
int64 ch;
|
||||
|
@ -13,9 +13,10 @@ using namespace musik::core::library::constants;
|
||||
#define TRANSPORT_HEIGHT 3
|
||||
#define DEFAULT_CATEGORY Track::ALBUM_ID
|
||||
|
||||
LibraryLayout::LibraryLayout(Transport& transport, LibraryPtr library)
|
||||
LibraryLayout::LibraryLayout(PlaybackService& playback, LibraryPtr library)
|
||||
: LayoutBase()
|
||||
, transport(transport) {
|
||||
, playback(playback)
|
||||
, transport(playback.GetTransport()) {
|
||||
this->library = library;
|
||||
this->InitializeWindows();
|
||||
}
|
||||
@ -43,7 +44,7 @@ void LibraryLayout::Layout() {
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
this->categoryList.reset(new CategoryListView(this->library, DEFAULT_CATEGORY));
|
||||
this->trackList.reset(new TrackListView(this->transport, this->library));
|
||||
this->trackList.reset(new TrackListView(this->playback, this->library));
|
||||
this->transportView.reset(new TransportWindow(this->library, this->transport));
|
||||
|
||||
this->AddWindow(this->categoryList);
|
||||
@ -106,10 +107,10 @@ bool LibraryLayout::KeyPress(int64 ch) {
|
||||
/* copied from GlobalHotkeys. should probably be generalized
|
||||
at some point. */
|
||||
int state = this->transport.GetPlaybackState();
|
||||
if (state == Transport::StatePaused) {
|
||||
if (state == Transport::PlaybackPaused) {
|
||||
this->transport.Resume();
|
||||
}
|
||||
else if (state == Transport::StatePlaying) {
|
||||
else if (state == Transport::PlaybackPlaying) {
|
||||
this->transport.Pause();
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <app/window/CategoryListView.h>
|
||||
#include <app/window/TrackListView.h>
|
||||
#include <app/window/TransportWindow.h>
|
||||
#include <app/service/PlaybackService.h>
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
@ -16,7 +17,7 @@ using musik::core::audio::Transport;
|
||||
|
||||
class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
|
||||
public:
|
||||
LibraryLayout(Transport& transport, LibraryPtr library);
|
||||
LibraryLayout(PlaybackService& playback, LibraryPtr library);
|
||||
virtual ~LibraryLayout();
|
||||
|
||||
virtual void Layout();
|
||||
@ -34,6 +35,7 @@ class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
|
||||
void OnCategoryViewInvalidated(
|
||||
ListWindow *view, size_t selectedIndex);
|
||||
|
||||
PlaybackService& playback;
|
||||
Transport& transport;
|
||||
LibraryPtr library;
|
||||
std::shared_ptr<CategoryListView> categoryList;
|
||||
|
@ -1,41 +1,65 @@
|
||||
#include <stdafx.h>
|
||||
#include "PlaybackService.h"
|
||||
|
||||
#include <cursespp/MessageQueue.h>
|
||||
#include <cursespp/Message.h>
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
using musik::core::audio::Transport;
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define URI_AT_INDEX(x) this->playlist.at(index + 1)->URI()
|
||||
#define URI_AT_INDEX(x) this->playlist.at(x)->URI()
|
||||
#define MESSAGE_START_NEXT_TRACK 2000
|
||||
|
||||
class StreamEvent : public IMessage {
|
||||
|
||||
};
|
||||
|
||||
PlaybackService::PlaybackService(Transport& transport)
|
||||
: transport(transport) {
|
||||
transport.PlaybackEvent.connect(this, &PlaybackService::OnTransportEvent);
|
||||
this->index = this->next = (size_t) -1;
|
||||
transport.StreamEvent.connect(this, &PlaybackService::OnStreamEvent);
|
||||
this->index = (size_t) -1;
|
||||
}
|
||||
|
||||
|
||||
void PlaybackService::ProcessMessage(IMessage &message) {
|
||||
|
||||
if (message.MessageType() == MESSAGE_START_NEXT_TRACK) {
|
||||
if (this->playlist.size() > index + 1) {
|
||||
++index;
|
||||
std::string uri = URI_AT_INDEX(index);
|
||||
transport.Start(uri);
|
||||
}
|
||||
else {
|
||||
transport.Stop();
|
||||
index = (size_t)-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackService::Start(std::vector<TrackPtr>& tracks, size_t index) {
|
||||
this->playlist.clear();
|
||||
std::copy(tracks.begin(), tracks.end(), this->playlist.begin());
|
||||
std::copy(tracks.begin(), tracks.end(), std::back_inserter(this->playlist));
|
||||
this->Start(index);
|
||||
}
|
||||
|
||||
void PlaybackService::Start(size_t index) {
|
||||
transport.Start(URI_AT_INDEX(index));
|
||||
transport.Stop();
|
||||
std::string uri = URI_AT_INDEX(index);
|
||||
transport.Start(uri);
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
void PlaybackService::OnTransportEvent(int eventType, std::string uri) {
|
||||
if (eventType == Transport::EventAlmostDone) {
|
||||
if (this->playlist.size() > index + 1) {
|
||||
std::string uri = URI_AT_INDEX(index + 1);
|
||||
transport.PrepareNextTrack(uri);
|
||||
void PlaybackService::OnStreamEvent(int eventType, std::string uri) {
|
||||
//if (eventType == Transport::StreamFinished) {
|
||||
// MessageQueue::Instance().Post(
|
||||
// Message::Create(this, MESSAGE_START_NEXT_TRACK, 0, 0));
|
||||
//}
|
||||
if (eventType == Transport::StreamAlmostDone) {
|
||||
if (this->playlist.size() > this->index + 1) {
|
||||
this->transport.PrepareNextTrack(URI_AT_INDEX(index + 1));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ class PlaybackService : public IMessageTarget, public sigslot::has_slots<> {
|
||||
size_t Count() { return this->playlist.size(); }
|
||||
|
||||
private:
|
||||
void OnTransportEvent(int eventType, std::string uri);
|
||||
void OnStreamEvent(int eventType, std::string uri);
|
||||
|
||||
Transport& transport;
|
||||
std::vector<TrackPtr> playlist;
|
||||
size_t index, next;
|
||||
size_t index;
|
||||
};
|
@ -17,10 +17,10 @@ bool GlobalHotkeys::Handle(int64 ch) {
|
||||
|
||||
if (kn == "^P") {
|
||||
int state = this->transport.GetPlaybackState();
|
||||
if (state == Transport::StatePaused) {
|
||||
if (state == Transport::PlaybackPaused) {
|
||||
this->transport.Resume();
|
||||
}
|
||||
else if (state == Transport::StatePlaying) {
|
||||
else if (state == Transport::PlaybackPlaying) {
|
||||
this->transport.Pause();
|
||||
}
|
||||
}
|
||||
|
@ -30,10 +30,10 @@ using std::setw;
|
||||
using std::setfill;
|
||||
using std::setiosflags;
|
||||
|
||||
TrackListView::TrackListView(Transport& transport, LibraryPtr library, IWindow *parent)
|
||||
: ListWindow(parent) {
|
||||
TrackListView::TrackListView(PlaybackService& playback, LibraryPtr library, IWindow *parent)
|
||||
: ListWindow(parent)
|
||||
, playback(playback) {
|
||||
this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK);
|
||||
this->transport = &transport;
|
||||
this->library = library;
|
||||
this->library->QueryCompleted.connect(this, &TrackListView::OnQueryCompleted);
|
||||
this->adapter = new Adapter(*this);
|
||||
@ -58,10 +58,7 @@ bool TrackListView::KeyPress(int64 ch) {
|
||||
if (ch == '\n') { /* return */
|
||||
size_t selected = this->GetSelectedIndex();
|
||||
if (this->metadata && this->metadata->size() > selected) {
|
||||
TrackPtr track = this->metadata->at(selected);
|
||||
std::string fn = track->GetValue(Track::FILENAME);
|
||||
this->transport->Stop();
|
||||
this->transport->Start(fn);
|
||||
playback.Start(*this->metadata, selected);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
#include <cursespp/curses_config.h>
|
||||
#include <cursespp/ScrollAdapterBase.h>
|
||||
#include <cursespp/IKeyHandler.h>
|
||||
|
||||
#include <cursespp/ListWindow.h>
|
||||
|
||||
#include <app/query/TrackListViewQuery.h>
|
||||
#include <app/service/PlaybackService.h>
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
@ -17,7 +17,7 @@ using musik::core::audio::Transport;
|
||||
|
||||
class TrackListView : public ListWindow, public sigslot::has_slots<> {
|
||||
public:
|
||||
TrackListView(Transport& transport, LibraryPtr library, IWindow *parent = NULL);
|
||||
TrackListView(PlaybackService& playback, LibraryPtr library, IWindow *parent = NULL);
|
||||
~TrackListView();
|
||||
|
||||
virtual void ProcessMessage(IMessage &message);
|
||||
@ -45,6 +45,6 @@ class TrackListView : public ListWindow, public sigslot::has_slots<> {
|
||||
std::shared_ptr<TrackListViewQuery> query;
|
||||
std::shared_ptr<std::vector<TrackPtr>> metadata;
|
||||
Adapter* adapter;
|
||||
Transport* transport;
|
||||
PlaybackService& playback;
|
||||
LibraryPtr library;
|
||||
};
|
@ -43,7 +43,7 @@ TransportWindow::TransportWindow(LibraryPtr library, Transport& transport)
|
||||
this->library = library;
|
||||
this->library->QueryCompleted.connect(this, &TransportWindow::OnQueryCompleted);
|
||||
this->transport = &transport;
|
||||
this->transport->PlaybackEvent.connect(this, &TransportWindow::OnTransportPlaybackEvent);
|
||||
this->transport->StreamEvent.connect(this, &TransportWindow::OnTransportStreamEvent);
|
||||
this->transport->VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged);
|
||||
this->paused = false;
|
||||
}
|
||||
@ -65,8 +65,8 @@ void TransportWindow::ProcessMessage(IMessage &message) {
|
||||
}
|
||||
}
|
||||
|
||||
void TransportWindow::OnTransportPlaybackEvent(int eventType, std::string url) {
|
||||
if (eventType == Transport::EventPlaying) {
|
||||
void TransportWindow::OnTransportStreamEvent(int eventType, std::string url) {
|
||||
if (eventType == Transport::StreamPlaying) {
|
||||
this->trackQuery.reset(new SingleTrackQuery(url));
|
||||
this->library->Enqueue(this->trackQuery);
|
||||
SCHEDULE_REFRESH(0)
|
||||
@ -88,7 +88,7 @@ void TransportWindow::Update() {
|
||||
this->Clear();
|
||||
WINDOW *c = this->GetContent();
|
||||
|
||||
bool paused = (transport->GetPlaybackState() == Transport::StatePaused);
|
||||
bool paused = (transport->GetPlaybackState() == Transport::PlaybackPaused);
|
||||
int64 gb = COLOR_PAIR(BOX_COLOR_GREEN_ON_BLACK);
|
||||
|
||||
/* playing SONG TITLE from ALBUM NAME */
|
||||
|
@ -23,7 +23,7 @@ class TransportWindow : public Window, public sigslot::has_slots<> {
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void OnTransportPlaybackEvent(int eventType, std::string url);
|
||||
void OnTransportStreamEvent(int eventType, std::string url);
|
||||
void OnTransportVolumeChanged();
|
||||
void OnQueryCompleted(QueryPtr query);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user