mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 04:18:36 +00:00
Experimental changes to support automatically enqueuing of previously
playing track. Required minor surgery to the ITransport interface, both transport types, and PlaybackService.
This commit is contained in:
parent
a22c346293
commit
3dd4bbbad7
@ -69,34 +69,43 @@ PlaybackState CrossfadeTransport::GetPlaybackState() {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PrepareNextTrack(const std::string& trackUrl, Gain gain) {
|
||||
void CrossfadeTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
||||
Lock lock(this->stateMutex);
|
||||
this->next.Reset(trackUrl, this, gain);
|
||||
this->next.Reset(uri, this, gain, false);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::Start(const std::string& url, Gain gain) {
|
||||
void CrossfadeTransport::Start(const std::string& uri, Gain gain, StartMode mode) {
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
musik::debug::info(TAG, "trying to play " + url);
|
||||
musik::debug::info(TAG, "trying to play " + uri);
|
||||
|
||||
bool immediate = mode == StartMode::Immediate;
|
||||
|
||||
/* in many cases (e.g. the user is skipping through tracks,
|
||||
the requested track may already be queued up. use it, if it is */
|
||||
if (this->next.player && this->next.player->GetUrl() == url) {
|
||||
if (this->next.player && this->next.player->GetUrl() == uri) {
|
||||
this->active.Reset();
|
||||
this->next.TransferTo(this->active);
|
||||
this->active.Start(this->volume);
|
||||
|
||||
if (immediate) {
|
||||
this->active.Start(this->volume);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->active.Reset(url, this, gain);
|
||||
this->active.Reset(uri, this, gain, immediate);
|
||||
this->next.Stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this->RaiseStreamEvent(StreamScheduled, this->active.player);
|
||||
}
|
||||
|
||||
std::string CrossfadeTransport::Uri() {
|
||||
auto player = this->active.player;
|
||||
return player ? player->GetUrl() : "";
|
||||
}
|
||||
|
||||
void CrossfadeTransport::ReloadOutput() {
|
||||
this->Stop();
|
||||
}
|
||||
@ -141,7 +150,7 @@ bool CrossfadeTransport::Resume() {
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
this->crossfader.Resume();
|
||||
this->active.Resume();
|
||||
this->active.Resume(this->volume);
|
||||
}
|
||||
|
||||
if (this->active.player) {
|
||||
@ -262,12 +271,19 @@ void CrossfadeTransport::OnPlayerPrepared(Player* player) {
|
||||
|
||||
if (player == active.player) {
|
||||
active.canFade = canFade;
|
||||
active.Start(this->volume);
|
||||
if (active.startImmediate) {
|
||||
active.Start(this->volume);
|
||||
}
|
||||
}
|
||||
else if (player == next.player) {
|
||||
next.canFade = canFade;
|
||||
}
|
||||
}
|
||||
|
||||
if (player == this->active.player) {
|
||||
this->RaiseStreamEvent(StreamPrepared, player);
|
||||
this->SetPlaybackState(PlaybackPrepared);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::OnPlayerStarted(Player* player) {
|
||||
@ -368,7 +384,8 @@ CrossfadeTransport::PlayerContext::PlayerContext(
|
||||
: transport(transport)
|
||||
, crossfader(crossfader)
|
||||
, player(nullptr)
|
||||
, canFade(false) {
|
||||
, canFade(false)
|
||||
, startImmediate(false) {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::StopIf(Player* player) {
|
||||
@ -378,17 +395,19 @@ void CrossfadeTransport::PlayerContext::StopIf(Player* player) {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Reset() {
|
||||
this->Reset("", nullptr, Gain());
|
||||
this->Reset("", nullptr, Gain(), false);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Reset(
|
||||
const std::string& url,
|
||||
Player::EventListener* listener,
|
||||
Gain gain)
|
||||
Gain gain,
|
||||
bool startImmediate)
|
||||
{
|
||||
this->startImmediate = false;
|
||||
|
||||
if (this->player && this->output) {
|
||||
this->player->Detach(&this->transport);
|
||||
|
||||
if (this->started && this->canFade) {
|
||||
crossfader.Cancel(
|
||||
this->player,
|
||||
@ -407,6 +426,7 @@ void CrossfadeTransport::PlayerContext::Reset(
|
||||
}
|
||||
}
|
||||
|
||||
this->startImmediate = startImmediate;
|
||||
this->canFade = this->started = false;
|
||||
this->output = url.size() ? outputs::SelectedOutput() : nullptr;
|
||||
this->player = url.size() ? Player::Create(url, this->output, Player::Drain, listener, gain) : nullptr;
|
||||
@ -462,12 +482,17 @@ void CrossfadeTransport::PlayerContext::Pause() {
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Resume() {
|
||||
if (this->output) {
|
||||
this->output->Resume();
|
||||
void CrossfadeTransport::PlayerContext::Resume(double transportVolume) {
|
||||
if (!this->started) {
|
||||
this->Start(transportVolume);
|
||||
}
|
||||
else {
|
||||
if (this->output) {
|
||||
this->output->Resume();
|
||||
|
||||
if (this->player) {
|
||||
this->player->Play();
|
||||
if (this->player) {
|
||||
this->player->Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,10 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
void StopImmediately();
|
||||
|
||||
virtual void Start(const std::string& trackUrl, Gain gain);
|
||||
virtual void PrepareNextTrack(const std::string& trackUrl, Gain gain);
|
||||
virtual void Start(const std::string& uri, Gain gain, StartMode mode);
|
||||
virtual void PrepareNextTrack(const std::string& uri, Gain gain);
|
||||
|
||||
virtual std::string Uri();
|
||||
|
||||
virtual void Stop();
|
||||
virtual bool Pause();
|
||||
@ -100,7 +102,8 @@ namespace musik { namespace core { namespace audio {
|
||||
void Reset(
|
||||
const std::string& url,
|
||||
Player::EventListener* listener,
|
||||
Gain gain);
|
||||
Gain gain,
|
||||
bool startImmediate);
|
||||
|
||||
void TransferTo(PlayerContext& context);
|
||||
|
||||
@ -108,10 +111,11 @@ namespace musik { namespace core { namespace audio {
|
||||
void Stop();
|
||||
void StopIf(Player* player);
|
||||
void Pause();
|
||||
void Resume();
|
||||
void Resume(double transportVolume);
|
||||
void SetVolume(double volume);
|
||||
bool IsEmpty();
|
||||
|
||||
bool startImmediate;
|
||||
bool started;
|
||||
bool canFade;
|
||||
Output output;
|
||||
|
@ -80,15 +80,15 @@ PlaybackState GaplessTransport::GetPlaybackState() {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void GaplessTransport::PrepareNextTrack(const std::string& trackUrl, Gain gain) {
|
||||
void GaplessTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
||||
bool startNext = false;
|
||||
{
|
||||
LockT lock(this->stateMutex);
|
||||
|
||||
RESET_NEXT_PLAYER(this);
|
||||
|
||||
if (trackUrl.size()) {
|
||||
this->nextPlayer = Player::Create(trackUrl, this->output, Player::NoDrain, this, gain);
|
||||
if (uri.size()) {
|
||||
this->nextPlayer = Player::Create(uri, this->output, Player::NoDrain, this, gain);
|
||||
startNext = this->nextCanStart;
|
||||
}
|
||||
}
|
||||
@ -98,16 +98,16 @@ void GaplessTransport::PrepareNextTrack(const std::string& trackUrl, Gain gain)
|
||||
}
|
||||
}
|
||||
|
||||
void GaplessTransport::Start(const std::string& url, Gain gain) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
void GaplessTransport::Start(const std::string& uri, Gain gain, StartMode mode) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + uri);
|
||||
|
||||
Player* newPlayer = Player::Create(url, this->output, Player::NoDrain, this, gain);
|
||||
Player* newPlayer = Player::Create(uri, this->output, Player::NoDrain, this, gain);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
|
||||
this->StartWithPlayer(newPlayer);
|
||||
this->StartWithPlayer(newPlayer, mode);
|
||||
}
|
||||
|
||||
void GaplessTransport::StartWithPlayer(Player* newPlayer) {
|
||||
void GaplessTransport::StartWithPlayer(Player* newPlayer, StartMode mode) {
|
||||
if (newPlayer) {
|
||||
bool playingNext = false;
|
||||
|
||||
@ -134,8 +134,10 @@ void GaplessTransport::StartWithPlayer(Player* newPlayer) {
|
||||
this->StopInternal(true, !playingNext, newPlayer);
|
||||
this->SetNextCanStart(false);
|
||||
this->output->Resume();
|
||||
newPlayer->Play();
|
||||
musik::debug::info(TAG, "play()");
|
||||
|
||||
if (mode == StartMode::Immediate) {
|
||||
newPlayer->Play();
|
||||
}
|
||||
|
||||
this->RaiseStreamEvent(StreamScheduled, newPlayer);
|
||||
}
|
||||
@ -151,6 +153,11 @@ void GaplessTransport::Stop() {
|
||||
this->StopInternal(false, true);
|
||||
}
|
||||
|
||||
std::string GaplessTransport::Uri() {
|
||||
auto player = this->activePlayer;
|
||||
return player ? player->GetUrl() : "";
|
||||
}
|
||||
|
||||
void GaplessTransport::StopInternal(
|
||||
bool suppressStopEvent,
|
||||
bool stopOutput,
|
||||
@ -286,6 +293,13 @@ void GaplessTransport::SetNextCanStart(bool nextCanStart) {
|
||||
this->nextCanStart = nextCanStart;
|
||||
}
|
||||
|
||||
void GaplessTransport::OnPlayerPrepared(Player* player) {
|
||||
if (player == this->activePlayer) {
|
||||
this->RaiseStreamEvent(StreamPrepared, player);
|
||||
this->SetPlaybackState(PlaybackPrepared);
|
||||
}
|
||||
}
|
||||
|
||||
void GaplessTransport::OnPlayerStarted(Player* player) {
|
||||
this->RaiseStreamEvent(StreamPlaying, player);
|
||||
this->SetPlaybackState(PlaybackPlaying);
|
||||
|
@ -53,8 +53,10 @@ namespace musik { namespace core { namespace audio {
|
||||
GaplessTransport();
|
||||
virtual ~GaplessTransport();
|
||||
|
||||
virtual void Start(const std::string& trackUrl, Gain gain);
|
||||
virtual void PrepareNextTrack(const std::string& trackUrl, Gain gain);
|
||||
virtual void Start(const std::string& uri, Gain gain, StartMode mode);
|
||||
virtual void PrepareNextTrack(const std::string& uri, Gain gain);
|
||||
|
||||
virtual std::string Uri();
|
||||
|
||||
virtual void Stop();
|
||||
virtual bool Pause();
|
||||
@ -78,7 +80,7 @@ namespace musik { namespace core { namespace audio {
|
||||
private:
|
||||
using LockT = std::unique_lock<std::recursive_mutex>;
|
||||
|
||||
void StartWithPlayer(Player* player);
|
||||
void StartWithPlayer(Player* player, StartMode mode = StartMode::Immediate);
|
||||
|
||||
void StopInternal(
|
||||
bool suppressStopEvent,
|
||||
@ -91,6 +93,7 @@ namespace musik { namespace core { namespace audio {
|
||||
void SetPlaybackState(int state);
|
||||
|
||||
virtual void OnPlayerStarted(Player* player);
|
||||
virtual void OnPlayerPrepared(Player* player);
|
||||
virtual void OnPlayerAlmostEnded(Player* player);
|
||||
virtual void OnPlayerFinished(Player* player);
|
||||
virtual void OnPlayerError(Player* player);
|
||||
|
@ -45,6 +45,10 @@ namespace musik { namespace core { namespace audio {
|
||||
public:
|
||||
using Gain = Player::Gain;
|
||||
|
||||
enum class StartMode : int {
|
||||
Immediate = 0, Wait = 1
|
||||
};
|
||||
|
||||
sigslot::signal2<int, std::string> StreamEvent;
|
||||
sigslot::signal1<int> PlaybackEvent;
|
||||
sigslot::signal0<> VolumeChanged;
|
||||
@ -52,8 +56,10 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
virtual ~ITransport() { }
|
||||
|
||||
virtual void Start(const std::string& trackUrl, Gain gain) = 0;
|
||||
virtual void PrepareNextTrack(const std::string& trackUrl, Gain gain) = 0;
|
||||
virtual void Start(const std::string& uri, Gain gain, StartMode mode) = 0;
|
||||
virtual void PrepareNextTrack(const std::string& uri, Gain gain) = 0;
|
||||
|
||||
virtual std::string Uri() = 0;
|
||||
|
||||
virtual void Stop() = 0;
|
||||
virtual bool Pause() = 0;
|
||||
|
@ -103,12 +103,16 @@ MasterTransport::Type MasterTransport::GetType() {
|
||||
return this->type;
|
||||
}
|
||||
|
||||
void MasterTransport::PrepareNextTrack(const std::string& trackUrl, Gain gain) {
|
||||
this->transport->PrepareNextTrack(trackUrl, gain);
|
||||
void MasterTransport::PrepareNextTrack(const std::string& uri, Gain gain) {
|
||||
this->transport->PrepareNextTrack(uri, gain);
|
||||
}
|
||||
|
||||
void MasterTransport::Start(const std::string& trackUrl, Gain gain) {
|
||||
this->transport->Start(trackUrl, gain);
|
||||
void MasterTransport::Start(const std::string& uri, Gain gain, StartMode type) {
|
||||
this->transport->Start(uri, gain, type);
|
||||
}
|
||||
|
||||
std::string MasterTransport::Uri() {
|
||||
return this->transport->Uri();
|
||||
}
|
||||
|
||||
void MasterTransport::Stop() {
|
||||
|
@ -49,8 +49,10 @@ namespace musik { namespace core { namespace audio {
|
||||
MasterTransport();
|
||||
virtual ~MasterTransport();
|
||||
|
||||
virtual void Start(const std::string& trackUrl, Gain gain);
|
||||
virtual void PrepareNextTrack(const std::string& trackUrl, Gain gain);
|
||||
virtual void Start(const std::string& uri, Gain gain, StartMode mode);
|
||||
virtual void PrepareNextTrack(const std::string& uri, Gain gain);
|
||||
|
||||
virtual std::string Uri();
|
||||
|
||||
virtual void Stop();
|
||||
virtual bool Pause();
|
||||
|
@ -328,6 +328,16 @@ void PlaybackService::ProcessMessage(IMessage &message) {
|
||||
if (eventType == PlaybackStopped) {
|
||||
this->OnTrackChanged(NO_POSITION, TrackPtr());
|
||||
}
|
||||
else if (eventType == PlaybackPrepared) {
|
||||
/* notify track change as soon as we're prepared. if we wait until
|
||||
we start playing, it may be a while until the UI knows to redraw! */
|
||||
if (this->UriAtIndex(this->index) == transport.Uri()) {
|
||||
auto track = this->playlist.Get(this->index);
|
||||
if (track) {
|
||||
this->OnTrackChanged(this->index, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = remotes.begin(); it != remotes.end(); it++) {
|
||||
(*it)->OnPlaybackStateChanged((PlaybackState) eventType);
|
||||
@ -664,21 +674,27 @@ void PlaybackService::CopyFrom(const musik::core::sdk::ITrackList* source) {
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackService::Play(size_t index) {
|
||||
void PlaybackService::PlayAt(size_t index, ITransport::StartMode mode) {
|
||||
index = std::min(this->Count(), index);
|
||||
|
||||
std::string uri = this->UriAtIndex(index);
|
||||
auto gain = this->GainAtIndex(index);
|
||||
|
||||
if (uri.size()) {
|
||||
transport.Start(
|
||||
this->UriAtIndex(index),
|
||||
this->GainAtIndex(index));
|
||||
|
||||
transport.Start(uri, gain, mode);
|
||||
this->nextIndex = NO_POSITION;
|
||||
this->index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackService::Play(size_t index) {
|
||||
this->PlayAt(index, ITransport::StartMode::Immediate);
|
||||
}
|
||||
|
||||
void PlaybackService::Prepare(size_t index) {
|
||||
this->PlayAt(index, ITransport::StartMode::Wait);
|
||||
}
|
||||
|
||||
size_t PlaybackService::GetIndex() {
|
||||
return this->index;
|
||||
}
|
||||
@ -694,7 +710,7 @@ void PlaybackService::PauseOrResume() {
|
||||
this->Play(0);
|
||||
}
|
||||
}
|
||||
else if (state == PlaybackPaused) {
|
||||
else if (state == PlaybackPaused || state == PlaybackPrepared) {
|
||||
transport.Resume();
|
||||
}
|
||||
else if (state == PlaybackPlaying) {
|
||||
|
@ -115,6 +115,7 @@ namespace musik { namespace core { namespace audio {
|
||||
concrete data types with known optimizations */
|
||||
musik::core::audio::ITransport& GetTransport() { return this->transport; }
|
||||
void Play(const musik::core::TrackList& tracks, size_t index);
|
||||
void Prepare(size_t index);
|
||||
void CopyTo(musik::core::TrackList& target);
|
||||
void CopyFrom(const musik::core::TrackList& source);
|
||||
musik::core::TrackPtr GetTrackAtIndex(size_t index);
|
||||
@ -181,6 +182,8 @@ namespace musik { namespace core { namespace audio {
|
||||
void InitRemotes();
|
||||
void ResetRemotes();
|
||||
|
||||
void PlayAt(size_t index, ITransport::StartMode mode);
|
||||
|
||||
std::string UriAtIndex(size_t index);
|
||||
musik::core::audio::ITransport::Gain GainAtIndex(size_t index);
|
||||
|
||||
|
@ -40,17 +40,19 @@ namespace musik {
|
||||
namespace core {
|
||||
namespace sdk {
|
||||
enum PlaybackState {
|
||||
PlaybackStopped,
|
||||
PlaybackPaused,
|
||||
PlaybackPlaying
|
||||
PlaybackStopped = 1,
|
||||
PlaybackPaused = 2,
|
||||
PlaybackPrepared = 3,
|
||||
PlaybackPlaying = 4,
|
||||
};
|
||||
|
||||
enum StreamEventType {
|
||||
StreamScheduled = 1,
|
||||
StreamPlaying = 2,
|
||||
StreamAlmostDone = 3,
|
||||
StreamFinished = 4,
|
||||
StreamStopped = 5,
|
||||
StreamPrepared = 2,
|
||||
StreamPlaying = 3,
|
||||
StreamAlmostDone = 4,
|
||||
StreamFinished = 5,
|
||||
StreamStopped = 6,
|
||||
StreamError = -1
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ namespace musik {
|
||||
namespace playback {
|
||||
void PauseOrResume(ITransport& transport) {
|
||||
int state = transport.GetPlaybackState();
|
||||
if (state == PlaybackPaused) {
|
||||
if (state == PlaybackPaused || state == PlaybackPrepared) {
|
||||
transport.Resume();
|
||||
}
|
||||
else if (state == PlaybackPlaying) {
|
||||
|
@ -298,7 +298,7 @@ bool ConsoleLayout::ProcessCommand(const std::string& cmd) {
|
||||
bool ConsoleLayout::PlayFile(const std::vector<std::string>& args) {
|
||||
if (args.size() > 0) {
|
||||
std::string filename = boost::algorithm::join(args, " ");
|
||||
transport.Start(filename, ITransport::Gain());
|
||||
transport.Start(filename, ITransport::Gain(), ITransport::StartMode::Immediate);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,10 +95,22 @@ void NowPlayingLayout::LoadLastSession() {
|
||||
PersistedPlayQueueQuery::Restore(this->library, this->playback));
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
int index = this->prefs->GetInt(keys::LastPlayQueueIndex, -1);
|
||||
if (index >= 0) {
|
||||
this->playback.Prepare(index);
|
||||
}
|
||||
}
|
||||
|
||||
void NowPlayingLayout::SaveSession() {
|
||||
if (this->prefs->GetBool(keys::SaveSessionOnExit, false)) {
|
||||
if (playback.GetPlaybackState() != sdk::PlaybackStopped) {
|
||||
this->prefs->SetInt(keys::LastPlayQueueIndex, (int) playback.GetIndex());
|
||||
}
|
||||
else {
|
||||
this->prefs->SetInt(keys::LastPlayQueueIndex, -1);
|
||||
}
|
||||
|
||||
auto query = std::shared_ptr<PersistedPlayQueueQuery>(
|
||||
PersistedPlayQueueQuery::Save(this->library, this->playback));
|
||||
|
||||
|
@ -484,8 +484,9 @@ void TransportWindow::Update(TimeMode timeMode) {
|
||||
}
|
||||
|
||||
WINDOW *c = this->GetContent();
|
||||
bool paused = (transport.GetPlaybackState() == PlaybackPaused);
|
||||
bool stopped = (transport.GetPlaybackState() == PlaybackStopped);
|
||||
auto state = transport.GetPlaybackState();
|
||||
bool paused = (state == PlaybackPrepared || state == PlaybackPaused);
|
||||
bool stopped = (state == PlaybackStopped);
|
||||
bool muted = transport.IsMuted();
|
||||
bool replayGainEnabled = (this->replayGainMode != ReplayGainMode::Disabled);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user