* Constified ITrackList methods and added ITrackList::GetTrack

* Modified a couple IPlaybackService method args to accept const
  TrackLists, instead of non-const
* Added IPlaybackService::GetTrackList to get a const reference to the
  internal tracklist.
* Modified NowPlayingLayout to use the PlaybackService's TrackList by
  const reference, instead of making a copy for every mutating operation.
* Fixed a nasty bug where PlaybackService::Shuffled could be broadcast on
  a background thread.
This commit is contained in:
casey langen 2017-02-07 19:33:07 -08:00
parent 978ad2ec9d
commit ec3a1909b2
11 changed files with 133 additions and 88 deletions

View File

@ -69,7 +69,8 @@ using Editor = PlaybackService::Editor;
#define MESSAGE_VOLUME_CHANGED 1003
#define MESSAGE_TIME_CHANGED 1004
#define MESSAGE_MODE_CHANGED 1005
#define MESSAGE_NOTIFY_EDITED 1006
#define MESSAGE_SHUFFLED 1006
#define MESSAGE_NOTIFY_EDITED 1007
class StreamMessage : public Message {
public:
@ -224,16 +225,16 @@ void PlaybackService::ToggleShuffle() {
this->playlist.ClearCache();
this->unshuffled.ClearCache();
bool shuffled = false;
if (this->unshuffled.Count() > 0) { /* shuffled -> unshuffled */
this->playlist.Clear();
this->playlist.Swap(this->unshuffled);
this->Shuffled(false);
}
else { /* unshuffled -> shuffle */
this->unshuffled.CopyFrom(this->playlist);
this->playlist.Shuffle();
this->Shuffled(true);
shuffled = true;
}
/* find the new playback index and prefetch the next track */
@ -245,7 +246,8 @@ void PlaybackService::ToggleShuffle() {
}
}
POST(this, MESSAGE_MODE_CHANGED, 0, 0);
POST(this, MESSAGE_SHUFFLED, shuffled ? 1 : 0, 0);
POST(this, MESSAGE_NOTIFY_EDITED, 0, 0);
}
void PlaybackService::ProcessMessage(IMessage &message) {
@ -308,8 +310,8 @@ void PlaybackService::ProcessMessage(IMessage &message) {
if (transport.GetPlaybackState() != PlaybackStopped) {
size_t updatedIndex = (size_t)message.UserData1();
this->index = updatedIndex;
if (updatedIndex != NO_POSITION) {
this->index = updatedIndex;
this->nextIndex = NO_POSITION; /* force recalc */
}
@ -331,6 +333,9 @@ void PlaybackService::ProcessMessage(IMessage &message) {
}
this->ModeChanged();
}
else if (type == MESSAGE_SHUFFLED) {
this->Shuffled(!!message.UserData1());
}
else if (type == MESSAGE_TIME_CHANGED) {
this->TimeChanged(transport.Position());
}
@ -422,7 +427,7 @@ PlaybackState PlaybackService::GetPlaybackState() {
return transport.GetPlaybackState();
}
void PlaybackService::Play(TrackList& tracks, size_t index) {
void PlaybackService::Play(const TrackList& tracks, size_t index) {
/* do the copy outside of the critical section, then swap. */
TrackList temp(this->library);
temp.CopyFrom(tracks);
@ -438,10 +443,10 @@ void PlaybackService::Play(TrackList& tracks, size_t index) {
}
}
void PlaybackService::Play(musik::core::sdk::ITrackList* source, size_t index) {
void PlaybackService::Play(const musik::core::sdk::ITrackList* source, size_t index) {
if (source) {
/* see if we have a TrackList -- if we do we can optimize the copy */
TrackList* sourceTrackList = dynamic_cast<TrackList*>(source);
const TrackList* sourceTrackList = dynamic_cast<const TrackList*>(source);
if (sourceTrackList) {
this->Play(*sourceTrackList, index);
@ -466,7 +471,7 @@ void PlaybackService::CopyTo(TrackList& target) {
target.CopyFrom(this->playlist);
}
void PlaybackService::CopyFrom(TrackList& source) {
void PlaybackService::CopyFrom(const TrackList& source) {
std::unique_lock<std::recursive_mutex> lock(this->playlistMutex);
this->playlist.CopyFrom(source);
@ -475,14 +480,16 @@ void PlaybackService::CopyFrom(TrackList& source) {
if (this->playingTrack) {
this->index = playlist.IndexOf(this->playingTrack->GetId());
POST(this, MESSAGE_PREPARE_NEXT_TRACK, NO_POSITION, 0);
POST(this, MESSAGE_PREPARE_NEXT_TRACK, this->index, 0);
}
POST(this, MESSAGE_NOTIFY_EDITED, NO_POSITION, 0);
}
void PlaybackService::CopyFrom(musik::core::sdk::ITrackList* source) {
void PlaybackService::CopyFrom(const musik::core::sdk::ITrackList* source) {
if (source) {
/* see if we have a TrackList -- if we do we can optimize the copy */
TrackList* sourceTrackList = dynamic_cast<TrackList*>(source);
const TrackList* sourceTrackList = dynamic_cast<const TrackList*>(source);
if (sourceTrackList) {
this->CopyFrom(*sourceTrackList);
@ -504,6 +511,8 @@ void PlaybackService::CopyFrom(musik::core::sdk::ITrackList* source) {
this->index = playlist.IndexOf(this->playingTrack->GetId());
POST(this, MESSAGE_PREPARE_NEXT_TRACK, NO_POSITION, 0);
}
POST(this, MESSAGE_NOTIFY_EDITED, NO_POSITION, 0);
}
}

View File

@ -100,19 +100,24 @@ namespace musik { namespace core { namespace audio {
virtual double GetDuration();
virtual musik::core::sdk::IRetainedTrack* GetTrack(size_t index);
virtual musik::core::sdk::IRetainedTrack* GetPlayingTrack();
virtual void CopyFrom(musik::core::sdk::ITrackList* source);
virtual void Play(musik::core::sdk::ITrackList* source, size_t index);
virtual void CopyFrom(const musik::core::sdk::ITrackList* source);
virtual void Play(const musik::core::sdk::ITrackList* source, size_t index);
virtual musik::core::sdk::ITrackListEditor* EditPlaylist();
/* app-specific implementation. similar to some SDK methods, but use
concrete data types with known optimizations */
musik::core::audio::ITransport& GetTransport() { return this->transport; }
void Play(musik::core::TrackList& tracks, size_t index);
void Play(const musik::core::TrackList& tracks, size_t index);
void CopyTo(musik::core::TrackList& target);
void CopyFrom(musik::core::TrackList& source);
void CopyFrom(const musik::core::TrackList& source);
musik::core::TrackPtr GetTrackAtIndex(size_t index);
musik::core::TrackPtr GetPlaying();
std::shared_ptr<const musik::core::TrackList> GetTrackList() {
return std::shared_ptr<const musik::core::TrackList>(
&this->playlist, [](const musik::core::TrackList*) {});
}
/* required to make changes to the playlist. this little data structure
privately owns a lock to the internal data structure and will release
that lock when it's destructed. */

View File

@ -67,22 +67,26 @@ namespace musik { namespace core { namespace db { namespace local {
delete this;
}
virtual size_t Count() {
virtual size_t Count() const {
return this->wrapped->Count();
}
virtual musik::core::sdk::IRetainedTrack* GetRetainedTrack(size_t index) {
virtual musik::core::sdk::IRetainedTrack* GetRetainedTrack(size_t index) const {
return this->wrapped->GetRetainedTrack(index);
}
virtual unsigned long long GetId(size_t index) {
virtual unsigned long long GetId(size_t index) const {
return this->wrapped->GetId(index);
}
virtual int IndexOf(unsigned long long id) {
virtual int IndexOf(unsigned long long id) const {
return this->wrapped->IndexOf(id);
}
virtual musik::core::sdk::ITrack* GetTrack(size_t index) const {
return this->wrapped->GetTrack(index);
}
private:
Result wrapped;
};

View File

@ -77,7 +77,7 @@ TrackList::~TrackList() {
}
size_t TrackList::Count() {
size_t TrackList::Count() const {
return ids.size();
}
@ -124,7 +124,7 @@ bool TrackList::Delete(size_t index) {
return false;
}
TrackPtr TrackList::Get(size_t index) {
TrackPtr TrackList::Get(size_t index) const {
auto id = this->ids.at(index);
auto cached = this->GetFromCache(id);
@ -140,15 +140,19 @@ TrackPtr TrackList::Get(size_t index) {
return query->Result();
}
IRetainedTrack* TrackList::GetRetainedTrack(size_t index) {
IRetainedTrack* TrackList::GetRetainedTrack(size_t index) const {
return new RetainedTrack(this->Get(index));
}
unsigned long long TrackList::GetId(size_t index) {
ITrack* TrackList::GetTrack(size_t index) const {
return this->Get(index).get();
}
unsigned long long TrackList::GetId(size_t index) const {
return this->ids.at(index);
}
void TrackList::CopyFrom(TrackList& from) {
void TrackList::CopyFrom(const TrackList& from) {
this->Clear();
std::copy(
@ -157,7 +161,7 @@ void TrackList::CopyFrom(TrackList& from) {
std::back_inserter(this->ids));
}
int TrackList::IndexOf(unsigned long long id) {
int TrackList::IndexOf(unsigned long long id) const {
auto it = std::find(this->ids.begin(), this->ids.end(), id);
return (it == this->ids.end()) ? -1 : it - this->ids.begin();
}
@ -180,7 +184,7 @@ void TrackList::Swap(TrackList& tl) {
std::swap(tl.ids, this->ids);
}
TrackPtr TrackList::GetFromCache(DBID key) {
TrackPtr TrackList::GetFromCache(DBID key) const {
auto it = this->cacheMap.find(key);
if (it != this->cacheMap.end()) {
this->cacheList.splice( /* promote to front */
@ -194,7 +198,7 @@ TrackPtr TrackList::GetFromCache(DBID key) {
return TrackPtr();
}
void TrackList::AddToCache(DBID key, TrackPtr value) {
void TrackList::AddToCache(DBID key, TrackPtr value) const {
auto it = this->cacheMap.find(key);
if (it != this->cacheMap.end()) {
cacheList.erase(it->second.second);

View File

@ -64,10 +64,11 @@ namespace musik { namespace core {
virtual ~TrackList();
/* ITrackList */
virtual size_t Count();
virtual musik::core::sdk::IRetainedTrack* GetRetainedTrack(size_t index);
virtual unsigned long long GetId(size_t index);
virtual int IndexOf(unsigned long long id);
virtual size_t Count() const;
virtual musik::core::sdk::IRetainedTrack* GetRetainedTrack(size_t index) const;
virtual unsigned long long GetId(size_t index) const;
virtual int IndexOf(unsigned long long id) const;
virtual musik::core::sdk::ITrack* GetTrack(size_t index) const;
/* ITrackListEditor */
virtual void Add(const unsigned long long id);
@ -79,23 +80,22 @@ namespace musik { namespace core {
virtual void Shuffle();
/* implementation specific */
TrackPtr Get(size_t index);
TrackPtr Get(size_t index) const;
void ClearCache();
void Swap(TrackList& list);
void CopyFrom(TrackList& from);
void CopyFrom(const TrackList& from);
private:
typedef std::list<DBID> CacheList;
typedef std::pair<TrackPtr, CacheList::iterator> CacheValue;
typedef std::unordered_map<DBID, CacheValue> CacheMap;
TrackPtr GetFromCache(DBID key);
void AddToCache(DBID key, TrackPtr value);
TrackPtr GetFromCache(DBID key) const;
void AddToCache(DBID key, TrackPtr value) const;
/* lru cache structures */
CacheList cacheList;
CacheMap cacheMap;
mutable CacheList cacheList;
mutable CacheMap cacheMap;
std::vector<DBID> ids;
ILibraryPtr library;

View File

@ -78,8 +78,8 @@ namespace musik { namespace core { namespace sdk {
virtual IRetainedTrack* GetPlayingTrack() = 0;
/* sdk v3*/
virtual void CopyFrom(ITrackList* trackList) = 0;
virtual void Play(ITrackList* source, size_t index) = 0;
virtual void CopyFrom(const ITrackList* trackList) = 0;
virtual void Play(const ITrackList* source, size_t index) = 0;
virtual ITrackListEditor* EditPlaylist() = 0;
};

View File

@ -43,11 +43,15 @@ namespace musik {
class ITrackList {
public:
/* sdk v1 */
virtual void Release() = 0;
virtual size_t Count() = 0;
virtual IRetainedTrack* GetRetainedTrack(size_t index) = 0;
virtual unsigned long long GetId(size_t index) = 0;
virtual int IndexOf(unsigned long long id) = 0;
virtual size_t Count() const = 0;
virtual IRetainedTrack* GetRetainedTrack(size_t index) const = 0;
virtual unsigned long long GetId(size_t index) const = 0;
virtual int IndexOf(unsigned long long id) const = 0;
/* sdk v3 */
virtual ITrack* GetTrack(size_t index) const = 0;
};
}

View File

@ -68,11 +68,9 @@ NowPlayingLayout::NowPlayingLayout(
: LayoutBase()
, playback(playback)
, library(library)
, reselectIndex(-1)
, lastPlaylistQueryId(-1) {
this->InitializeWindows();
this->playback.Shuffled.connect(this, &NowPlayingLayout::OnPlaybackShuffled);
this->playback.QueueEdited.connect(this, &NowPlayingLayout::RequeryTrackList);
EDIT_KEYS = {
@ -147,15 +145,17 @@ void NowPlayingLayout::OnVisibilityChanged(bool visible) {
}
void NowPlayingLayout::OnTrackListRequeried(musik::core::db::local::TrackListQueryBase* query) {
/* if the requery just finished for a regular playlist, we need to
make sure we load it into the PlaybackService. generally we just read
FROM the playback service */
/* in most cases we pull the TrackList directly from the PlaybackService.
however, some user operations cause the TrackList to be loaded from
the database, e.g. loading regular playlists. in these cases, copy
the tracks TO the PlaybackService, then refresh! */
if (query && query->GetId() == this->lastPlaylistQueryId) {
this->playback.CopyFrom(*query->GetResult());
this->lastPlaylistQueryId = -1;
}
if (playback.Count()) {
/* regular logic (i.e, no edit operation) */
if (this->reselectIndex == -1) {
size_t index = playback.GetIndex();
@ -168,16 +168,22 @@ void NowPlayingLayout::OnTrackListRequeried(musik::core::db::local::TrackListQue
this->trackListView->ScrollTo(index == 0 ? index : index - 1);
}
}
else { /* requeried due to edit, so reselect... */
/* user just finished an edit. this is a bit of a hack; we're notified
of the edit completion asynchronously, so before we complete the edit
we stash the index we need to re-select. */
else {
/* ensure the correct index is selected, and that it's properly
scrolled into view */
this->reselectIndex = std::min((int) this->trackListView->Count() - 1, this->reselectIndex);
this->trackListView->SetSelectedIndex((int) this->reselectIndex);
this->trackListView->SetSelectedIndex((size_t)this->reselectIndex);
auto pos = this->trackListView->GetScrollPosition();
int first = (int) pos.firstVisibleEntryIndex;
int last = (int) first + pos.visibleEntryCount;
int index = (int) this->reselectIndex;
int first = (int)pos.firstVisibleEntryIndex;
int last = (int)first + pos.visibleEntryCount;
int index = (int)this->reselectIndex;
if (index < first || index >= last) {
this->trackListView->ScrollTo(this->reselectIndex);
this->trackListView->ScrollTo((size_t)this->reselectIndex);
}
this->reselectIndex = -1;
}
/* if after a bunch of monkeying around there's still nothing
@ -188,17 +194,14 @@ void NowPlayingLayout::OnTrackListRequeried(musik::core::db::local::TrackListQue
this->trackListView->ScrollTo(0);
}
}
this->reselectIndex = -1;
}
void NowPlayingLayout::OnPlaybackShuffled(bool shuffled) {
this->RequeryTrackList();
else {
this->trackListView->ScrollTo(0);
}
}
void NowPlayingLayout::RequeryTrackList() {
this->trackListView->Requery(std::shared_ptr<TrackListQueryBase>(
new NowPlayingTrackListQuery(this->library, this->playback)));
this->trackListView->SetTrackList(this->playback.GetTrackList());
this->OnTrackListRequeried(nullptr);
}
void NowPlayingLayout::OnPlaylistSelected(DBID playlistId) {
@ -246,28 +249,31 @@ bool NowPlayingLayout::KeyPress(const std::string& key) {
bool NowPlayingLayout::ProcessEditOperation(const std::string& key) {
if (EDIT_KEYS.find(key) != EDIT_KEYS.end()) {
if (!playback.IsShuffled()) {
PlaybackService::Editor editor = this->playback.Edit();
size_t selected = this->trackListView->GetSelectedIndex();
this->reselectIndex = (int)selected;
this->reselectIndex = -1;
if (Hotkeys::Is(Hotkeys::PlayQueueMoveUp, key)) {
if (selected > 0) {
size_t to = selected - 1;
editor.Move(selected, to);
this->reselectIndex = (int)to;
{
PlaybackService::Editor editor = this->playback.Edit();
if (Hotkeys::Is(Hotkeys::PlayQueueMoveUp, key)) {
if (selected > 0) {
size_t to = selected - 1;
editor.Move(selected, to);
reselectIndex = (int)to;
}
}
else if (Hotkeys::Is(Hotkeys::PlayQueueMoveDown, key)) {
if (selected < this->playback.Count() - 1) {
size_t to = selected + 1;
editor.Move(selected, to);
reselectIndex = (int)to;
}
}
else if (Hotkeys::Is(Hotkeys::PlayQueueDelete, key)) {
editor.Delete(selected);
reselectIndex = (int)selected;
}
}
else if (Hotkeys::Is(Hotkeys::PlayQueueMoveDown, key)) {
if (selected < this->playback.Count() - 1) {
size_t to = selected + 1;
editor.Move(selected, to);
this->reselectIndex = (int)to;
}
}
else if (Hotkeys::Is(Hotkeys::PlayQueueDelete, key)) {
editor.Delete(selected);
this->reselectIndex = (int)selected;
}
return true;
}
}

View File

@ -74,14 +74,13 @@ namespace musik {
/* callbacks */
void OnTrackListRequeried(musik::core::db::local::TrackListQueryBase* query);
void OnPlaybackShuffled(bool shuffled);
int64 RowDecorator(musik::core::TrackPtr track, size_t index);
void OnPlaylistSelected(DBID playlistId);
musik::core::audio::PlaybackService& playback;
musik::core::ILibraryPtr library;
std::shared_ptr<TrackListView> trackListView;
int reselectIndex; /* gross... */
int reselectIndex; /* sigh... */
int lastPlaylistQueryId;
};
}

View File

@ -55,6 +55,7 @@ using namespace musik::core::audio;
using namespace musik::core::db;
using namespace musik::core::library;
using namespace musik::core::runtime;
using namespace musik::core::sdk;
using namespace musik::box;
using namespace musik::glue;
using namespace cursespp;
@ -128,10 +129,22 @@ void TrackListView::OnQueryCompleted(IQuery* query) {
}
}
std::shared_ptr<TrackList> TrackListView::GetTrackList() {
std::shared_ptr<const TrackList> TrackListView::GetTrackList() {
return this->metadata;
}
void TrackListView::SetTrackList(std::shared_ptr<const TrackList> trackList) {
if (this->metadata != trackList) {
this->metadata = trackList;
this->SetSelectedIndex(0);
this->ScrollToTop();
this->OnAdapterChanged();
}
else {
this->OnAdapterChanged();
}
}
void TrackListView::Clear() {
this->query.reset();
this->metadata.reset(new TrackList(this->library));
@ -291,7 +304,7 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
? parent.formatter(track, this->GetWidth())
: formatWithoutAlbum(track, this->GetWidth());
if (this->parent.headers->find(index) != this->parent.headers->end()) {
if (this->parent.headers && this->parent.headers->find(index) != this->parent.headers->end()) {
std::string album = track->GetValue(constants::Track::ALBUM);
std::shared_ptr<EntryWithHeader> entry(new EntryWithHeader(album, text));
entry->SetAttrs(COLOR_PAIR(CURSESPP_LIST_ITEM_HEADER), attrs);

View File

@ -78,7 +78,8 @@ namespace musik {
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
virtual bool KeyPress(const std::string& key);
std::shared_ptr<musik::core::TrackList> GetTrackList();
std::shared_ptr<const musik::core::TrackList> GetTrackList();
void SetTrackList(std::shared_ptr<const musik::core::TrackList> trackList);
void Clear();
musik::core::TrackPtr Get(size_t index);
@ -108,7 +109,7 @@ namespace musik {
void ScrollToPlaying();
std::shared_ptr<TrackListQueryBase> query;
std::shared_ptr<musik::core::TrackList> metadata;
std::shared_ptr<const musik::core::TrackList> metadata;
Headers headers;
Adapter* adapter;
musik::core::audio::PlaybackService& playback;