mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +00:00
Propagate buffering status from Player -> Transport -> PlaybackService
-> UI and render it in TransportWindow.
This commit is contained in:
parent
b21b21dab6
commit
c644862959
@ -50,7 +50,7 @@ static std::string TAG = "CrossfadeTransport";
|
|||||||
|
|
||||||
CrossfadeTransport::CrossfadeTransport()
|
CrossfadeTransport::CrossfadeTransport()
|
||||||
: volume(1.0)
|
: volume(1.0)
|
||||||
, state(PlaybackStopped)
|
, playbackState(PlaybackStopped)
|
||||||
, muted(false)
|
, muted(false)
|
||||||
, crossfader(*this)
|
, crossfader(*this)
|
||||||
, active(*this, crossfader)
|
, active(*this, crossfader)
|
||||||
@ -66,7 +66,12 @@ CrossfadeTransport::~CrossfadeTransport() {
|
|||||||
|
|
||||||
PlaybackState CrossfadeTransport::GetPlaybackState() {
|
PlaybackState CrossfadeTransport::GetPlaybackState() {
|
||||||
Lock lock(this->stateMutex);
|
Lock lock(this->stateMutex);
|
||||||
return this->state;
|
return this->playbackState;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamState CrossfadeTransport::GetStreamState() {
|
||||||
|
Lock lock(this->stateMutex);
|
||||||
|
return this->activePlayerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossfadeTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
void CrossfadeTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
||||||
@ -88,6 +93,12 @@ void CrossfadeTransport::Start(const std::string& uri, Gain gain, StartMode mode
|
|||||||
this->active.Reset();
|
this->active.Reset();
|
||||||
this->next.TransferTo(this->active);
|
this->next.TransferTo(this->active);
|
||||||
|
|
||||||
|
if (this->active.player) {
|
||||||
|
this->RaiseStreamEvent(
|
||||||
|
this->active.player->GetStreamState(),
|
||||||
|
this->active.player);
|
||||||
|
}
|
||||||
|
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
this->active.Start(this->volume);
|
this->active.Start(this->volume);
|
||||||
}
|
}
|
||||||
@ -98,7 +109,7 @@ void CrossfadeTransport::Start(const std::string& uri, Gain gain, StartMode mode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->RaiseStreamEvent(StreamScheduled, this->active.player);
|
this->RaiseStreamEvent(StreamBuffering, this->active.player);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CrossfadeTransport::Uri() {
|
std::string CrossfadeTransport::Uri() {
|
||||||
@ -176,7 +187,7 @@ void CrossfadeTransport::SetPosition(double seconds) {
|
|||||||
Lock lock(this->stateMutex);
|
Lock lock(this->stateMutex);
|
||||||
|
|
||||||
if (this->active.player) {
|
if (this->active.player) {
|
||||||
if (this->state != PlaybackPlaying) {
|
if (this->playbackState != PlaybackPlaying) {
|
||||||
this->SetPlaybackState(PlaybackPlaying);
|
this->SetPlaybackState(PlaybackPlaying);
|
||||||
}
|
}
|
||||||
this->active.player->SetPosition(seconds);
|
this->active.player->SetPosition(seconds);
|
||||||
@ -243,7 +254,7 @@ void CrossfadeTransport::SetVolume(double volume) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossfadeTransport::OnPlayerPrepared(Player* player) {
|
void CrossfadeTransport::OnPlayerBuffered(Player* player) {
|
||||||
{
|
{
|
||||||
Lock lock(this->stateMutex);
|
Lock lock(this->stateMutex);
|
||||||
|
|
||||||
@ -280,7 +291,7 @@ void CrossfadeTransport::OnPlayerPrepared(Player* player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (player == this->active.player) {
|
if (player == this->active.player) {
|
||||||
this->RaiseStreamEvent(StreamPrepared, player);
|
this->RaiseStreamEvent(StreamBuffered, player);
|
||||||
this->SetPlaybackState(PlaybackPrepared);
|
this->SetPlaybackState(PlaybackPrepared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,8 +375,8 @@ void CrossfadeTransport::SetPlaybackState(int state) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
Lock lock(this->stateMutex);
|
Lock lock(this->stateMutex);
|
||||||
changed = (this->state != state);
|
changed = (this->playbackState != state);
|
||||||
this->state = (PlaybackState) state;
|
this->playbackState = (PlaybackState) state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@ -374,6 +385,13 @@ void CrossfadeTransport::SetPlaybackState(int state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CrossfadeTransport::RaiseStreamEvent(int type, Player* player) {
|
void CrossfadeTransport::RaiseStreamEvent(int type, Player* player) {
|
||||||
|
{
|
||||||
|
Lock lock(this->stateMutex);
|
||||||
|
if (player == active.player) {
|
||||||
|
this->activePlayerState = (StreamState) type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->StreamEvent(type, player->GetUrl());
|
this->StreamEvent(type, player->GetUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
virtual void ReloadOutput();
|
virtual void ReloadOutput();
|
||||||
|
|
||||||
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
||||||
|
virtual musik::core::sdk::StreamState GetStreamState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Lock = std::unique_lock<std::recursive_mutex>;
|
using Lock = std::unique_lock<std::recursive_mutex>;
|
||||||
@ -129,13 +130,14 @@ namespace musik { namespace core { namespace audio {
|
|||||||
|
|
||||||
void OnCrossfaderEmptied();
|
void OnCrossfaderEmptied();
|
||||||
|
|
||||||
virtual void OnPlayerPrepared(Player* player);
|
virtual void OnPlayerBuffered(Player* player);
|
||||||
virtual void OnPlayerStarted(Player* player);
|
virtual void OnPlayerStarted(Player* player);
|
||||||
virtual void OnPlayerFinished(Player* player);
|
virtual void OnPlayerFinished(Player* player);
|
||||||
virtual void OnPlayerError(Player* player);
|
virtual void OnPlayerError(Player* player);
|
||||||
virtual void OnPlayerMixPoint(Player* player, int id, double time);
|
virtual void OnPlayerMixPoint(Player* player, int id, double time);
|
||||||
|
|
||||||
musik::core::sdk::PlaybackState state;
|
musik::core::sdk::PlaybackState playbackState;
|
||||||
|
musik::core::sdk::StreamState activePlayerState;
|
||||||
std::recursive_mutex stateMutex;
|
std::recursive_mutex stateMutex;
|
||||||
Crossfader crossfader;
|
Crossfader crossfader;
|
||||||
PlayerContext active;
|
PlayerContext active;
|
||||||
|
@ -57,11 +57,12 @@ static std::string TAG = "GaplessTransport";
|
|||||||
instance->activePlayer->Detach(instance); \
|
instance->activePlayer->Detach(instance); \
|
||||||
instance->activePlayer->Destroy(); \
|
instance->activePlayer->Destroy(); \
|
||||||
instance->activePlayer = nullptr; \
|
instance->activePlayer = nullptr; \
|
||||||
|
instance->activePlayerState = StreamError; \
|
||||||
}
|
}
|
||||||
|
|
||||||
GaplessTransport::GaplessTransport()
|
GaplessTransport::GaplessTransport()
|
||||||
: volume(1.0)
|
: volume(1.0)
|
||||||
, state(PlaybackStopped)
|
, playbackState(PlaybackStopped)
|
||||||
, activePlayer(nullptr)
|
, activePlayer(nullptr)
|
||||||
, nextPlayer(nullptr)
|
, nextPlayer(nullptr)
|
||||||
, nextCanStart(false)
|
, nextCanStart(false)
|
||||||
@ -77,7 +78,12 @@ GaplessTransport::~GaplessTransport() {
|
|||||||
|
|
||||||
PlaybackState GaplessTransport::GetPlaybackState() {
|
PlaybackState GaplessTransport::GetPlaybackState() {
|
||||||
LockT lock(this->stateMutex);
|
LockT lock(this->stateMutex);
|
||||||
return this->state;
|
return this->playbackState;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamState GaplessTransport::GetStreamState() {
|
||||||
|
LockT lock(this->stateMutex);
|
||||||
|
return this->activePlayerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GaplessTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
void GaplessTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
||||||
@ -121,6 +127,10 @@ void GaplessTransport::StartWithPlayer(Player* newPlayer, StartMode mode) {
|
|||||||
this->nextPlayer = nullptr;
|
this->nextPlayer = nullptr;
|
||||||
this->activePlayer = newPlayer;
|
this->activePlayer = newPlayer;
|
||||||
|
|
||||||
|
if (newPlayer) {
|
||||||
|
this->RaiseStreamEvent(newPlayer->GetStreamState(), newPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
/* first argument suppresses the "Stop" event from getting triggered,
|
/* first argument suppresses the "Stop" event from getting triggered,
|
||||||
the second param is used for gapless playback -- we won't stop the output
|
the second param is used for gapless playback -- we won't stop the output
|
||||||
and will allow pending buffers to finish if we're not automatically
|
and will allow pending buffers to finish if we're not automatically
|
||||||
@ -135,8 +145,6 @@ void GaplessTransport::StartWithPlayer(Player* newPlayer, StartMode mode) {
|
|||||||
newPlayer->Play();
|
newPlayer->Play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->RaiseStreamEvent(StreamScheduled, newPlayer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +245,7 @@ void GaplessTransport::SetPosition(double seconds) {
|
|||||||
LockT lock(this->stateMutex);
|
LockT lock(this->stateMutex);
|
||||||
|
|
||||||
if (this->activePlayer) {
|
if (this->activePlayer) {
|
||||||
if (this->state != PlaybackPlaying) {
|
if (this->playbackState != PlaybackPlaying) {
|
||||||
this->SetPlaybackState(PlaybackPlaying);
|
this->SetPlaybackState(PlaybackPlaying);
|
||||||
}
|
}
|
||||||
this->activePlayer->SetPosition(seconds);
|
this->activePlayer->SetPosition(seconds);
|
||||||
@ -289,9 +297,9 @@ void GaplessTransport::SetNextCanStart(bool nextCanStart) {
|
|||||||
this->nextCanStart = nextCanStart;
|
this->nextCanStart = nextCanStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GaplessTransport::OnPlayerPrepared(Player* player) {
|
void GaplessTransport::OnPlayerBuffered(Player* player) {
|
||||||
if (player == this->activePlayer) {
|
if (player == this->activePlayer) {
|
||||||
this->RaiseStreamEvent(StreamPrepared, player);
|
this->RaiseStreamEvent(StreamBuffered, player);
|
||||||
this->SetPlaybackState(PlaybackPrepared);
|
this->SetPlaybackState(PlaybackPrepared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,8 +380,8 @@ void GaplessTransport::SetPlaybackState(int state) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
LockT lock(this->stateMutex);
|
LockT lock(this->stateMutex);
|
||||||
changed = (this->state != state);
|
changed = (this->playbackState != state);
|
||||||
this->state = (PlaybackState) state;
|
this->playbackState = (PlaybackState) state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@ -382,5 +390,11 @@ void GaplessTransport::SetPlaybackState(int state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GaplessTransport::RaiseStreamEvent(int type, Player* player) {
|
void GaplessTransport::RaiseStreamEvent(int type, Player* player) {
|
||||||
|
{
|
||||||
|
LockT lock(this->stateMutex);
|
||||||
|
if (player == activePlayer) {
|
||||||
|
this->activePlayerState = (StreamState) type;
|
||||||
|
}
|
||||||
|
}
|
||||||
this->StreamEvent(type, player->GetUrl());
|
this->StreamEvent(type, player->GetUrl());
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
virtual void ReloadOutput();
|
virtual void ReloadOutput();
|
||||||
|
|
||||||
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
||||||
|
virtual musik::core::sdk::StreamState GetStreamState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using LockT = std::unique_lock<std::recursive_mutex>;
|
using LockT = std::unique_lock<std::recursive_mutex>;
|
||||||
@ -93,13 +94,14 @@ namespace musik { namespace core { namespace audio {
|
|||||||
void SetPlaybackState(int state);
|
void SetPlaybackState(int state);
|
||||||
|
|
||||||
virtual void OnPlayerStarted(Player* player);
|
virtual void OnPlayerStarted(Player* player);
|
||||||
virtual void OnPlayerPrepared(Player* player);
|
virtual void OnPlayerBuffered(Player* player);
|
||||||
virtual void OnPlayerAlmostEnded(Player* player);
|
virtual void OnPlayerAlmostEnded(Player* player);
|
||||||
virtual void OnPlayerFinished(Player* player);
|
virtual void OnPlayerFinished(Player* player);
|
||||||
virtual void OnPlayerError(Player* player);
|
virtual void OnPlayerError(Player* player);
|
||||||
virtual void OnPlayerDestroying(Player* player);
|
virtual void OnPlayerDestroying(Player* player);
|
||||||
|
|
||||||
musik::core::sdk::PlaybackState state;
|
musik::core::sdk::PlaybackState playbackState;
|
||||||
|
musik::core::sdk::StreamState activePlayerState;
|
||||||
std::recursive_mutex stateMutex;
|
std::recursive_mutex stateMutex;
|
||||||
std::shared_ptr<musik::core::sdk::IOutput> output;
|
std::shared_ptr<musik::core::sdk::IOutput> output;
|
||||||
Player* activePlayer;
|
Player* activePlayer;
|
||||||
|
@ -79,6 +79,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
virtual void ReloadOutput() = 0;
|
virtual void ReloadOutput() = 0;
|
||||||
|
|
||||||
virtual musik::core::sdk::PlaybackState GetPlaybackState() = 0;
|
virtual musik::core::sdk::PlaybackState GetPlaybackState() = 0;
|
||||||
|
virtual musik::core::sdk::StreamState GetStreamState() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} } }
|
} } }
|
||||||
|
@ -163,6 +163,10 @@ PlaybackState MasterTransport::GetPlaybackState() {
|
|||||||
return this->transport->GetPlaybackState();
|
return this->transport->GetPlaybackState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StreamState MasterTransport::GetStreamState() {
|
||||||
|
return this->transport->GetStreamState();
|
||||||
|
}
|
||||||
|
|
||||||
void MasterTransport::OnStreamEvent(int type, std::string url) {
|
void MasterTransport::OnStreamEvent(int type, std::string url) {
|
||||||
this->StreamEvent(type, url);
|
this->StreamEvent(type, url);
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
virtual void ReloadOutput();
|
virtual void ReloadOutput();
|
||||||
|
|
||||||
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
virtual musik::core::sdk::PlaybackState GetPlaybackState();
|
||||||
|
virtual musik::core::sdk::StreamState GetStreamState();
|
||||||
|
|
||||||
void SwitchTo(Type type);
|
void SwitchTo(Type type);
|
||||||
Type GetType();
|
Type GetType();
|
||||||
|
@ -305,15 +305,13 @@ void PlaybackService::ProcessMessage(IMessage &message) {
|
|||||||
}
|
}
|
||||||
else if (type == MESSAGE_STREAM_EVENT) {
|
else if (type == MESSAGE_STREAM_EVENT) {
|
||||||
StreamMessage* streamMessage = static_cast<StreamMessage*>(&message);
|
StreamMessage* streamMessage = static_cast<StreamMessage*>(&message);
|
||||||
|
StreamState eventType = (StreamState) streamMessage->GetEventType();
|
||||||
int64_t eventType = streamMessage->GetEventType();
|
|
||||||
|
|
||||||
if (eventType == StreamPlaying) {
|
if (eventType == StreamPlaying) {
|
||||||
TrackPtr track;
|
TrackPtr track;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::recursive_mutex> lock(this->playlistMutex);
|
std::unique_lock<std::recursive_mutex> lock(this->playlistMutex);
|
||||||
|
|
||||||
if (this->nextIndex != NO_POSITION) {
|
if (this->nextIndex != NO_POSITION) {
|
||||||
/* in most cases when we get here it means that the next track is
|
/* in most cases when we get here it means that the next track is
|
||||||
starting, so we want to update our internal index. however, because
|
starting, so we want to update our internal index. however, because
|
||||||
@ -343,6 +341,16 @@ void PlaybackService::ProcessMessage(IMessage &message) {
|
|||||||
|
|
||||||
this->PrepareNextTrack();
|
this->PrepareNextTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool raiseStreamEvent = false;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(this->playlistMutex);
|
||||||
|
raiseStreamEvent = this->UriAtIndex(this->index) == streamMessage->GetUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raiseStreamEvent) {
|
||||||
|
this->StreamStateChanged(eventType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (type == MESSAGE_PLAYBACK_EVENT) {
|
else if (type == MESSAGE_PLAYBACK_EVENT) {
|
||||||
int64_t eventType = message.UserData1();
|
int64_t eventType = message.UserData1();
|
||||||
@ -365,7 +373,7 @@ void PlaybackService::ProcessMessage(IMessage &message) {
|
|||||||
(*it)->OnPlaybackStateChanged((PlaybackState) eventType);
|
(*it)->OnPlaybackStateChanged((PlaybackState) eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->PlaybackEvent((PlaybackState) eventType);
|
this->PlaybackStateChanged((PlaybackState) eventType);
|
||||||
}
|
}
|
||||||
else if (type == MESSAGE_PREPARE_NEXT_TRACK) {
|
else if (type == MESSAGE_PREPARE_NEXT_TRACK) {
|
||||||
if (transport->GetPlaybackState() != PlaybackStopped) {
|
if (transport->GetPlaybackState() != PlaybackStopped) {
|
||||||
|
@ -63,7 +63,8 @@ namespace musik { namespace core { namespace audio {
|
|||||||
|
|
||||||
/* copied from Transport, but will be automatically called on the
|
/* copied from Transport, but will be automatically called on the
|
||||||
specified MessageQueue's thread! */
|
specified MessageQueue's thread! */
|
||||||
sigslot::signal1<int> PlaybackEvent;
|
sigslot::signal1<musik::core::sdk::PlaybackState> PlaybackStateChanged;
|
||||||
|
sigslot::signal1<musik::core::sdk::StreamState> StreamStateChanged;
|
||||||
sigslot::signal0<> VolumeChanged;
|
sigslot::signal0<> VolumeChanged;
|
||||||
sigslot::signal1<double> TimeChanged;
|
sigslot::signal1<double> TimeChanged;
|
||||||
|
|
||||||
|
@ -123,7 +123,8 @@ Player::Player(
|
|||||||
DestroyMode destroyMode,
|
DestroyMode destroyMode,
|
||||||
EventListener *listener,
|
EventListener *listener,
|
||||||
Gain gain)
|
Gain gain)
|
||||||
: state(Player::Idle)
|
: internalState(Player::Idle)
|
||||||
|
, streamState(StreamBuffering)
|
||||||
, stream(Stream::Create())
|
, stream(Stream::Create())
|
||||||
, url(url)
|
, url(url)
|
||||||
, currentPosition(0)
|
, currentPosition(0)
|
||||||
@ -159,8 +160,8 @@ Player::~Player() {
|
|||||||
void Player::Play() {
|
void Player::Play() {
|
||||||
std::unique_lock<std::mutex> lock(this->queueMutex);
|
std::unique_lock<std::mutex> lock(this->queueMutex);
|
||||||
|
|
||||||
if (this->state != Player::Quit) {
|
if (this->internalState != Player::Quit) {
|
||||||
this->state = Player::Playing;
|
this->internalState = Player::Playing;
|
||||||
this->writeToOutputCondition.notify_all();
|
this->writeToOutputCondition.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,11 +174,11 @@ void Player::Destroy() {
|
|||||||
|
|
||||||
std::unique_lock<std::mutex> lock(this->queueMutex);
|
std::unique_lock<std::mutex> lock(this->queueMutex);
|
||||||
|
|
||||||
if (this->state == Player::Quit && !this->thread) {
|
if (this->internalState == Player::Quit && !this->thread) {
|
||||||
return; /* already terminated (or terminating) */
|
return; /* already terminated (or terminating) */
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state = Player::Quit;
|
this->internalState = Player::Quit;
|
||||||
this->writeToOutputCondition.notify_all();
|
this->writeToOutputCondition.notify_all();
|
||||||
this->thread->detach();
|
this->thread->detach();
|
||||||
delete this->thread;
|
delete this->thread;
|
||||||
@ -258,7 +259,7 @@ void Player::AddMixPoint(int id, double time) {
|
|||||||
|
|
||||||
int Player::State() {
|
int Player::State() {
|
||||||
std::unique_lock<std::mutex> lock(this->queueMutex);
|
std::unique_lock<std::mutex> lock(this->queueMutex);
|
||||||
return this->state;
|
return this->internalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::HasCapability(Capability c) {
|
bool Player::HasCapability(Capability c) {
|
||||||
@ -297,13 +298,14 @@ void musik::core::audio::playerThreadLoop(Player* player) {
|
|||||||
|
|
||||||
if (player->stream->OpenStream(player->url)) {
|
if (player->stream->OpenStream(player->url)) {
|
||||||
for (Listener* l : player->Listeners()) {
|
for (Listener* l : player->Listeners()) {
|
||||||
l->OnPlayerPrepared(player);
|
player->streamState = StreamBuffered;
|
||||||
|
l->OnPlayerBuffered(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait until we enter the Playing or Quit state */
|
/* wait until we enter the Playing or Quit state */
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(player->queueMutex);
|
std::unique_lock<std::mutex> lock(player->queueMutex);
|
||||||
while (player->state == Player::Idle) {
|
while (player->internalState == Player::Idle) {
|
||||||
player->writeToOutputCondition.wait(lock);
|
player->writeToOutputCondition.wait(lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,6 +421,7 @@ void musik::core::audio::playerThreadLoop(Player* player) {
|
|||||||
it wasn't stopped by the user. raise the "almost ended" flag. */
|
it wasn't stopped by the user. raise the "almost ended" flag. */
|
||||||
if (!player->Exited()) {
|
if (!player->Exited()) {
|
||||||
for (Listener* l : player->Listeners()) {
|
for (Listener* l : player->Listeners()) {
|
||||||
|
player->streamState = StreamAlmostDone;
|
||||||
l->OnPlayerAlmostEnded(player);
|
l->OnPlayerAlmostEnded(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,6 +431,7 @@ void musik::core::audio::playerThreadLoop(Player* player) {
|
|||||||
else {
|
else {
|
||||||
if (!player->Exited()) {
|
if (!player->Exited()) {
|
||||||
for (Listener* l : player->Listeners()) {
|
for (Listener* l : player->Listeners()) {
|
||||||
|
player->streamState = StreamError;
|
||||||
l->OnPlayerError(player);
|
l->OnPlayerError(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,13 +458,15 @@ void musik::core::audio::playerThreadLoop(Player* player) {
|
|||||||
|
|
||||||
if (!player->Exited()) {
|
if (!player->Exited()) {
|
||||||
for (Listener* l : player->Listeners()) {
|
for (Listener* l : player->Listeners()) {
|
||||||
|
player->streamState = StreamFinished;
|
||||||
l->OnPlayerFinished(player);
|
l->OnPlayerFinished(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player->state = Player::Quit;
|
player->internalState = Player::Quit;
|
||||||
|
|
||||||
for (Listener* l : player->Listeners()) {
|
for (Listener* l : player->Listeners()) {
|
||||||
|
player->streamState = StreamStopped;
|
||||||
l->OnPlayerDestroying(player);
|
l->OnPlayerDestroying(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +477,7 @@ void musik::core::audio::playerThreadLoop(Player* player) {
|
|||||||
|
|
||||||
bool Player::Exited() {
|
bool Player::Exited() {
|
||||||
std::unique_lock<std::mutex> lock(this->queueMutex);
|
std::unique_lock<std::mutex> lock(this->queueMutex);
|
||||||
return (this->state == Player::Quit);
|
return (this->internalState == Player::Quit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void initHammingWindow() {
|
static inline void initHammingWindow() {
|
||||||
@ -587,6 +593,7 @@ void Player::OnBufferProcessed(IBuffer *buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this->notifiedStarted) {
|
if (!this->notifiedStarted) {
|
||||||
|
this->streamState = StreamPlaying;
|
||||||
this->notifiedStarted = true;
|
this->notifiedStarted = true;
|
||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EventListener {
|
struct EventListener {
|
||||||
virtual void OnPlayerPrepared(Player *player) { }
|
virtual void OnPlayerBuffered(Player *player) { }
|
||||||
virtual void OnPlayerStarted(Player *player) { }
|
virtual void OnPlayerStarted(Player *player) { }
|
||||||
virtual void OnPlayerAlmostEnded(Player *player) { }
|
virtual void OnPlayerAlmostEnded(Player *player) { }
|
||||||
virtual void OnPlayerFinished(Player *player) { }
|
virtual void OnPlayerFinished(Player *player) { }
|
||||||
@ -102,6 +102,8 @@ namespace musik { namespace core { namespace audio {
|
|||||||
|
|
||||||
std::string GetUrl() const { return this->url; }
|
std::string GetUrl() const { return this->url; }
|
||||||
|
|
||||||
|
musik::core::sdk::StreamState GetStreamState() { return this->streamState; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void playerThreadLoop(Player* player);
|
friend void playerThreadLoop(Player* player);
|
||||||
|
|
||||||
@ -135,7 +137,7 @@ namespace musik { namespace core { namespace audio {
|
|||||||
Idle = 0,
|
Idle = 0,
|
||||||
Playing = 1,
|
Playing = 1,
|
||||||
Quit = 2
|
Quit = 2
|
||||||
} States;
|
} InternalState;
|
||||||
|
|
||||||
bool Exited();
|
bool Exited();
|
||||||
int State();
|
int State();
|
||||||
@ -162,7 +164,8 @@ namespace musik { namespace core { namespace audio {
|
|||||||
double nextMixPoint;
|
double nextMixPoint;
|
||||||
std::atomic<double> currentPosition;
|
std::atomic<double> currentPosition;
|
||||||
std::atomic<double> seekToPosition;
|
std::atomic<double> seekToPosition;
|
||||||
int state;
|
std::atomic<musik::core::sdk::StreamState> streamState;
|
||||||
|
std::atomic<int> internalState;
|
||||||
bool notifiedStarted;
|
bool notifiedStarted;
|
||||||
float* spectrum;
|
float* spectrum;
|
||||||
DestroyMode destroyMode;
|
DestroyMode destroyMode;
|
||||||
|
@ -1019,7 +1019,7 @@ class mcsdk_audio_player_callback_proxy: public Player::EventListener {
|
|||||||
public:
|
public:
|
||||||
std::set<mcsdk_audio_player_callbacks*> callbacks;
|
std::set<mcsdk_audio_player_callbacks*> callbacks;
|
||||||
mcsdk_player_context_internal* context;
|
mcsdk_player_context_internal* context;
|
||||||
virtual void OnPlayerPrepared(Player *player) {
|
virtual void OnPlayerBuffered(Player *player) {
|
||||||
std::unique_lock<std::mutex> lock(this->context->event_mutex);
|
std::unique_lock<std::mutex> lock(this->context->event_mutex);
|
||||||
for (auto c : callbacks) {
|
for (auto c : callbacks) {
|
||||||
if (c->on_prepared) {
|
if (c->on_prepared) {
|
||||||
|
@ -47,9 +47,9 @@ namespace musik {
|
|||||||
PlaybackPlaying = 4,
|
PlaybackPlaying = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum StreamEventType {
|
enum StreamState {
|
||||||
StreamScheduled = 1,
|
StreamBuffering = 1,
|
||||||
StreamPrepared = 2,
|
StreamBuffered = 2,
|
||||||
StreamPlaying = 3,
|
StreamPlaying = 3,
|
||||||
StreamAlmostDone = 4,
|
StreamAlmostDone = 4,
|
||||||
StreamFinished = 5,
|
StreamFinished = 5,
|
||||||
|
@ -51,8 +51,8 @@ using namespace musik::core::sdk;
|
|||||||
using namespace musik::cube;
|
using namespace musik::cube;
|
||||||
using namespace cursespp;
|
using namespace cursespp;
|
||||||
|
|
||||||
static inline size_t longestStringLength(const std::vector<std::string>&& keys) {
|
static inline int longestStringLength(const std::vector<std::string>&& keys) {
|
||||||
size_t max = 0;
|
int max = 0;
|
||||||
for (auto& str: keys) {
|
for (auto& str: keys) {
|
||||||
size_t len = u8cols(_TSTR(str));
|
size_t len = u8cols(_TSTR(str));
|
||||||
max = len > max ? len : max;
|
max = len > max ? len : max;
|
||||||
@ -71,7 +71,7 @@ RemoteLibrarySettingsLayout::~RemoteLibrarySettingsLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RemoteLibrarySettingsLayout::OnLayout() {
|
void RemoteLibrarySettingsLayout::OnLayout() {
|
||||||
size_t labelWidth = longestStringLength({
|
const int labelWidth = longestStringLength({
|
||||||
"settings_library_type_remote_hostname",
|
"settings_library_type_remote_hostname",
|
||||||
"settings_library_type_remote_wss_port",
|
"settings_library_type_remote_wss_port",
|
||||||
"settings_library_type_remote_http_port",
|
"settings_library_type_remote_http_port",
|
||||||
|
@ -56,17 +56,18 @@ namespace musik {
|
|||||||
static const int RequeryTrackList = First + 5;
|
static const int RequeryTrackList = First + 5;
|
||||||
static const int RequeryCategoryList = First + 6;
|
static const int RequeryCategoryList = First + 6;
|
||||||
static const int RefreshTransport = First + 7;
|
static const int RefreshTransport = First + 7;
|
||||||
static const int RefreshLogs = First + 8;
|
static const int TransportBuffering = First + 8;
|
||||||
static const int UpdateCheckFinished = First + 9;
|
static const int RefreshLogs = First + 9;
|
||||||
static const int JumpToConsole = First + 10;
|
static const int UpdateCheckFinished = First + 10;
|
||||||
static const int JumpToLibrary = First + 11;
|
static const int JumpToConsole = First + 11;
|
||||||
static const int JumpToSettings = First + 12;
|
static const int JumpToLibrary = First + 12;
|
||||||
static const int JumpToLyrics = First + 13;
|
static const int JumpToSettings = First + 13;
|
||||||
static const int JumpToHotkeys = First + 14;
|
static const int JumpToLyrics = First + 14;
|
||||||
static const int JumpToPlayQueue = First + 15;
|
static const int JumpToHotkeys = First + 15;
|
||||||
static const int SetLastFmState = First + 16;
|
static const int JumpToPlayQueue = First + 16;
|
||||||
static const int UpdateEqualizer = First + 17;
|
static const int SetLastFmState = First + 17;
|
||||||
static const int DebugLog = First + 18;
|
static const int UpdateEqualizer = First + 18;
|
||||||
|
static const int DebugLog = First + 19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,8 @@ void tokenize(const std::string& format, TokenList& tokens) {
|
|||||||
/* a cache of localized, pre-formatted strings we use every second. */
|
/* a cache of localized, pre-formatted strings we use every second. */
|
||||||
static struct StringCache {
|
static struct StringCache {
|
||||||
std::string PLAYING_FORMAT;
|
std::string PLAYING_FORMAT;
|
||||||
|
std::string PLAYING;
|
||||||
|
std::string BUFFERING;
|
||||||
std::string STOPPED;
|
std::string STOPPED;
|
||||||
std::string EMPTY_SONG;
|
std::string EMPTY_SONG;
|
||||||
std::string EMPTY_ALBUM;
|
std::string EMPTY_ALBUM;
|
||||||
@ -146,6 +148,8 @@ static struct StringCache {
|
|||||||
|
|
||||||
void Initialize() {
|
void Initialize() {
|
||||||
PLAYING_FORMAT = _TSTR("transport_playing_format");
|
PLAYING_FORMAT = _TSTR("transport_playing_format");
|
||||||
|
PLAYING = _TSTR("transport_playing_format_playing");
|
||||||
|
BUFFERING = _TSTR("transport_playing_format_buffering");
|
||||||
STOPPED = _TSTR("transport_stopped");
|
STOPPED = _TSTR("transport_stopped");
|
||||||
EMPTY_SONG = _TSTR("transport_empty_song");
|
EMPTY_SONG = _TSTR("transport_empty_song");
|
||||||
EMPTY_ALBUM = _TSTR("transport_empty_album");
|
EMPTY_ALBUM = _TSTR("transport_empty_album");
|
||||||
@ -248,6 +252,7 @@ utf8 characters and ellipsizing */
|
|||||||
static size_t writePlayingFormat(
|
static size_t writePlayingFormat(
|
||||||
WINDOW *w,
|
WINDOW *w,
|
||||||
TransportDisplayCache& displayCache,
|
TransportDisplayCache& displayCache,
|
||||||
|
bool buffering,
|
||||||
size_t width)
|
size_t width)
|
||||||
{
|
{
|
||||||
TokenList tokens;
|
TokenList tokens;
|
||||||
@ -255,6 +260,7 @@ static size_t writePlayingFormat(
|
|||||||
|
|
||||||
Color dim = Color::TextDisabled;
|
Color dim = Color::TextDisabled;
|
||||||
Color gb = Color::TextActive;
|
Color gb = Color::TextActive;
|
||||||
|
Color warn = Color::TextWarning;
|
||||||
size_t remaining = width;
|
size_t remaining = width;
|
||||||
|
|
||||||
auto it = tokens.begin();
|
auto it = tokens.begin();
|
||||||
@ -263,19 +269,30 @@ static size_t writePlayingFormat(
|
|||||||
|
|
||||||
Color attr = dim;
|
Color attr = dim;
|
||||||
std::string value;
|
std::string value;
|
||||||
size_t cols;
|
size_t cols = 0;
|
||||||
|
|
||||||
if (token->type == Token::Placeholder) {
|
if (token->type == Token::Placeholder) {
|
||||||
attr = gb;
|
if (token->value == "$state") {
|
||||||
|
if (buffering) {
|
||||||
|
attr = warn;
|
||||||
|
value = Strings.BUFFERING;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = Strings.PLAYING;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (token->value == "$title") {
|
if (token->value == "$title") {
|
||||||
|
attr = gb;
|
||||||
value = displayCache.title;
|
value = displayCache.title;
|
||||||
cols = displayCache.titleCols;
|
cols = displayCache.titleCols;
|
||||||
}
|
}
|
||||||
else if (token->value == "$album") {
|
else if (token->value == "$album") {
|
||||||
|
attr = gb;
|
||||||
value = displayCache.album;
|
value = displayCache.album;
|
||||||
cols = displayCache.albumCols;
|
cols = displayCache.albumCols;
|
||||||
}
|
}
|
||||||
else if (token->value == "$artist") {
|
else if (token->value == "$artist") {
|
||||||
|
attr = gb;
|
||||||
value = displayCache.artist;
|
value = displayCache.artist;
|
||||||
cols = displayCache.artistCols;
|
cols = displayCache.artistCols;
|
||||||
}
|
}
|
||||||
@ -349,6 +366,7 @@ TransportWindow::TransportWindow(
|
|||||||
this->playback.Shuffled.connect(this, &TransportWindow::OnPlaybackShuffled);
|
this->playback.Shuffled.connect(this, &TransportWindow::OnPlaybackShuffled);
|
||||||
this->playback.VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged);
|
this->playback.VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged);
|
||||||
this->playback.TimeChanged.connect(this, &TransportWindow::OnTransportTimeChanged);
|
this->playback.TimeChanged.connect(this, &TransportWindow::OnTransportTimeChanged);
|
||||||
|
this->playback.StreamStateChanged.connect(this, &TransportWindow::OnPlaybackStreamStateChanged);
|
||||||
this->paused = false;
|
this->paused = false;
|
||||||
this->lastTime = DEFAULT_TIME;
|
this->lastTime = DEFAULT_TIME;
|
||||||
this->shufflePos.y = 0;
|
this->shufflePos.y = 0;
|
||||||
@ -478,7 +496,7 @@ void TransportWindow::OnFocusChanged(bool focused) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TransportWindow::ProcessMessage(IMessage &message) {
|
void TransportWindow::ProcessMessage(IMessage &message) {
|
||||||
int type = message.Type();
|
const int type = message.Type();
|
||||||
|
|
||||||
if (type == message::RefreshTransport) {
|
if (type == message::RefreshTransport) {
|
||||||
this->Update((TimeMode) message.UserData1());
|
this->Update((TimeMode) message.UserData1());
|
||||||
@ -487,6 +505,10 @@ void TransportWindow::ProcessMessage(IMessage &message) {
|
|||||||
DEBOUNCE_REFRESH(TimeSmooth, REFRESH_INTERVAL_MS)
|
DEBOUNCE_REFRESH(TimeSmooth, REFRESH_INTERVAL_MS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (type == message::TransportBuffering) {
|
||||||
|
this->buffering = true;
|
||||||
|
this->Update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransportWindow::OnPlaybackServiceTrackChanged(size_t index, TrackPtr track) {
|
void TransportWindow::OnPlaybackServiceTrackChanged(size_t index, TrackPtr track) {
|
||||||
@ -496,6 +518,17 @@ void TransportWindow::OnPlaybackServiceTrackChanged(size_t index, TrackPtr track
|
|||||||
DEBOUNCE_REFRESH(TimeSync, 0);
|
DEBOUNCE_REFRESH(TimeSync, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransportWindow::OnPlaybackStreamStateChanged(StreamState state) {
|
||||||
|
if (state == StreamBuffering) {
|
||||||
|
this->Debounce(message::TransportBuffering, 0, 0, 250);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->Remove(message::TransportBuffering);
|
||||||
|
this->buffering = false;
|
||||||
|
this->Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TransportWindow::OnPlaybackModeChanged() {
|
void TransportWindow::OnPlaybackModeChanged() {
|
||||||
DEBOUNCE_REFRESH(TimeSync, 0);
|
DEBOUNCE_REFRESH(TimeSync, 0);
|
||||||
}
|
}
|
||||||
@ -587,7 +620,7 @@ void TransportWindow::Update(TimeMode timeMode) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
displayCache->Update(transport, this->currentTrack);
|
displayCache->Update(transport, this->currentTrack);
|
||||||
writePlayingFormat(c, *this->displayCache, cx - shuffleWidth);
|
writePlayingFormat(c, *this->displayCache, this->buffering, cx - shuffleWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* draw the "shuffle" label */
|
/* draw the "shuffle" label */
|
||||||
|
@ -116,6 +116,7 @@ namespace musik {
|
|||||||
|
|
||||||
void OnPlaybackServiceTrackChanged(size_t index, musik::core::TrackPtr track);
|
void OnPlaybackServiceTrackChanged(size_t index, musik::core::TrackPtr track);
|
||||||
void OnPlaybackModeChanged();
|
void OnPlaybackModeChanged();
|
||||||
|
void OnPlaybackStreamStateChanged(musik::core::sdk::StreamState);
|
||||||
void OnTransportVolumeChanged();
|
void OnTransportVolumeChanged();
|
||||||
void OnTransportTimeChanged(double time);
|
void OnTransportTimeChanged(double time);
|
||||||
void OnPlaybackShuffled(bool shuffled);
|
void OnPlaybackShuffled(bool shuffled);
|
||||||
@ -131,6 +132,7 @@ namespace musik {
|
|||||||
musik::core::TrackPtr currentTrack;
|
musik::core::TrackPtr currentTrack;
|
||||||
FocusTarget focus, lastFocus;
|
FocusTarget focus, lastFocus;
|
||||||
std::unique_ptr<TransportDisplayCache> displayCache;
|
std::unique_ptr<TransportDisplayCache> displayCache;
|
||||||
|
bool buffering{ false };
|
||||||
double lastTime;
|
double lastTime;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,9 @@
|
|||||||
"shortcuts_tracks": "tracks",
|
"shortcuts_tracks": "tracks",
|
||||||
"shortcuts_play_queue": "play queue",
|
"shortcuts_play_queue": "play queue",
|
||||||
|
|
||||||
"transport_playing_format": "playing $title by $artist from $album",
|
"transport_playing_format": "$state $title by $artist from $album",
|
||||||
|
"transport_playing_format_playing": "playing",
|
||||||
|
"transport_playing_format_buffering": "buffering",
|
||||||
"transport_stopped": "playback is stopped",
|
"transport_stopped": "playback is stopped",
|
||||||
"transport_empty_song": "[song]",
|
"transport_empty_song": "[song]",
|
||||||
"transport_empty_album": "[album]",
|
"transport_empty_album": "[album]",
|
||||||
|
Loading…
Reference in New Issue
Block a user