diff --git a/README.md b/README.md index 115c5261f..4ccad6c1a 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ the current hotkeys are generally based around holding the alt/meta key with you - `ALT+o` forward 10 seconds - `ALT+r` repaint the screen - `ALT+,` toggle repeat mode (off/track/list) +- `ALT+.` (un)shuffle play queue - `CTRL+p` pause/resume (globally) - `CTRL+x` stop (unload streams, free resources) - `CTRL+d` quit diff --git a/src/musikbox/app/layout/NowPlayingLayout.cpp b/src/musikbox/app/layout/NowPlayingLayout.cpp index a533112e0..74816170a 100755 --- a/src/musikbox/app/layout/NowPlayingLayout.cpp +++ b/src/musikbox/app/layout/NowPlayingLayout.cpp @@ -55,6 +55,7 @@ NowPlayingLayout::NowPlayingLayout( , playback(playback) , library(library) { this->InitializeWindows(); + this->playback.Shuffled.connect(this, &NowPlayingLayout::OnPlaybackShuffled); } NowPlayingLayout::~NowPlayingLayout() { @@ -105,6 +106,10 @@ void NowPlayingLayout::OnTrackListRequeried() { } } +void NowPlayingLayout::OnPlaybackShuffled(bool shuffled) { + this->RequeryTrackList(); +} + void NowPlayingLayout::RequeryTrackList() { this->trackList->Requery(std::shared_ptr( new NowPlayingTrackListQuery(this->library, this->playback))); diff --git a/src/musikbox/app/layout/NowPlayingLayout.h b/src/musikbox/app/layout/NowPlayingLayout.h index bc1c2e23b..1ef085e1d 100755 --- a/src/musikbox/app/layout/NowPlayingLayout.h +++ b/src/musikbox/app/layout/NowPlayingLayout.h @@ -68,6 +68,7 @@ namespace musik { private: void OnTrackListRequeried(); + void OnPlaybackShuffled(bool shuffled); void InitializeWindows(); void RequeryTrackList(); diff --git a/src/musikbox/app/model/TrackList.cpp b/src/musikbox/app/model/TrackList.cpp index 496fcb805..23c038e39 100755 --- a/src/musikbox/app/model/TrackList.cpp +++ b/src/musikbox/app/model/TrackList.cpp @@ -107,8 +107,7 @@ TrackPtr TrackList::Get(size_t index) { } void TrackList::CopyFrom(TrackList& from) { - this->ids.clear(); - this->ClearCache(); + this->Clear(); std::copy( from.ids.begin(), @@ -116,6 +115,15 @@ void TrackList::CopyFrom(TrackList& from) { std::back_inserter(this->ids)); } +void TrackList::Shuffle() { + std::random_shuffle(this->ids.begin(), this->ids.end()); +} + +void TrackList::Clear() { + this->ClearCache(); + this->ids.clear(); +} + void TrackList::ClearCache() { this->cacheList.clear(); this->cacheMap.clear(); diff --git a/src/musikbox/app/model/TrackList.h b/src/musikbox/app/model/TrackList.h index 78f9e1861..e615bd7f0 100755 --- a/src/musikbox/app/model/TrackList.h +++ b/src/musikbox/app/model/TrackList.h @@ -51,8 +51,10 @@ namespace musik { void Add(const DBID& id); musik::core::TrackPtr Get(size_t index); void ClearCache(); + void Clear(); void Swap(TrackList& list); void CopyFrom(TrackList& from); + void Shuffle(); private: typedef std::list CacheList; diff --git a/src/musikbox/app/service/PlaybackService.cpp b/src/musikbox/app/service/PlaybackService.cpp index 5c4d5e7eb..6c69c5718 100755 --- a/src/musikbox/app/service/PlaybackService.cpp +++ b/src/musikbox/app/service/PlaybackService.cpp @@ -87,6 +87,7 @@ PlaybackService::PlaybackService(LibraryPtr library, ITransport& transport) : library(library) , transport(transport) , playlist(library) +, unshuffled(library) , repeatMode(RepeatNone) { transport.StreamEvent.connect(this, &PlaybackService::OnStreamEvent); transport.PlaybackEvent.connect(this, &PlaybackService::OnPlaybackEvent); @@ -130,6 +131,20 @@ void PlaybackService::SetRepeatMode(RepeatMode mode) { } } +void PlaybackService::ToggleShuffle() { + boost::recursive_mutex::scoped_lock lock(this->playlistMutex); + if (this->unshuffled.Count() > 0) { + this->playlist.Clear(); + this->playlist.Swap(this->unshuffled); + this->Shuffled(false); + } + else { + this->unshuffled.CopyFrom(this->playlist); + this->playlist.Shuffle(); + this->Shuffled(true); + } +} + void PlaybackService::ProcessMessage(IMessage &message) { if (message.Type() == MESSAGE_STREAM_EVENT) { StreamMessage* streamMessage = static_cast(&message); @@ -140,7 +155,7 @@ void PlaybackService::ProcessMessage(IMessage &message) { if (this->nextIndex != NO_POSITION) { /* 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 - things are asynchronous, this may not always be the case, especially if + things are asynchronous, this may not always be the case, especially if the tracks are very short, or the user is advancing through songs very quickly. make compare the track URIs before we update internal state. */ if (this->GetTrackAtIndex(this->nextIndex)->URI() == streamMessage->GetUri()) { @@ -226,6 +241,7 @@ void PlaybackService::Play(TrackList& tracks, size_t index) { { boost::recursive_mutex::scoped_lock lock(this->playlistMutex); this->playlist.Swap(temp); + this->unshuffled.Clear(); } if (index <= tracks.Count()) { diff --git a/src/musikbox/app/service/PlaybackService.h b/src/musikbox/app/service/PlaybackService.h index 73aae4e83..e0fac655d 100755 --- a/src/musikbox/app/service/PlaybackService.h +++ b/src/musikbox/app/service/PlaybackService.h @@ -52,6 +52,7 @@ namespace musik { public: sigslot::signal2 TrackChanged; sigslot::signal0<> ModeChanged; + sigslot::signal1 Shuffled; enum RepeatMode { RepeatNone, @@ -79,6 +80,9 @@ namespace musik { RepeatMode GetRepeatMode() { return this->repeatMode; } void SetRepeatMode(RepeatMode mode); + bool IsShuffled() { return this->unshuffled.Count() > 0; } + void ToggleShuffle(); + musik::core::TrackPtr GetTrackAtIndex(size_t index); size_t GetIndex(); @@ -90,6 +94,7 @@ namespace musik { void PrepareNextTrack(); TrackList playlist; + TrackList unshuffled; boost::recursive_mutex playlistMutex; musik::core::LibraryPtr library; diff --git a/src/musikbox/app/util/GlobalHotkeys.cpp b/src/musikbox/app/util/GlobalHotkeys.cpp index 6c38e4f77..a1a41c58b 100755 --- a/src/musikbox/app/util/GlobalHotkeys.cpp +++ b/src/musikbox/app/util/GlobalHotkeys.cpp @@ -86,6 +86,10 @@ bool GlobalHotkeys::Handle(const std::string& kn) { playback::ToggleRepeatMode(this->playback); return true; } + else if (kn == "M-." || kn == "M-period") { + this->playback.ToggleShuffle(); + return true; + } else if (kn == "^X") { this->playback.Stop(); return true;