mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 04:52:32 +00:00
Added the ability for TrackList to make batch queries asynchronously
with built-in debounce. Disabled by default for now because it flickers; this will be useful in situations where there's high latency.
This commit is contained in:
parent
710f237b04
commit
1d69bb4455
@ -125,7 +125,7 @@ bool TrackList::Delete(size_t index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TrackPtr TrackList::Get(size_t index) const {
|
||||
TrackPtr TrackList::Get(size_t index, bool async) const {
|
||||
#if 0
|
||||
/* one at a time */
|
||||
auto id = this->ids.at(index);
|
||||
@ -147,14 +147,21 @@ TrackPtr TrackList::Get(size_t index) const {
|
||||
auto cached = this->GetFromCache(id);
|
||||
if (cached) { return cached; }
|
||||
|
||||
int half = (this->cacheSize - 1) / 2;
|
||||
int remain = this->cacheSize - 1;
|
||||
int from = index - half;
|
||||
int half = ((int) this->cacheSize - 1) / 2;
|
||||
int remain = (int) this->cacheSize - 1;
|
||||
int from = (int) index - half;
|
||||
remain -= from > 0 ? half : (half + from);
|
||||
int to = index + remain;
|
||||
this->CacheWindow(std::max(0, from), to);
|
||||
int to = (int) index + remain;
|
||||
this->CacheWindow(std::max(0, from), to, async);
|
||||
|
||||
cached = this->GetFromCache(id);
|
||||
|
||||
if (async && !cached) {
|
||||
auto loadingTrack = std::make_shared<LibraryTrack>(this->ids[index], this->library);
|
||||
loadingTrack->SetMetadataState(MetadataState::Loading);
|
||||
return loadingTrack;
|
||||
}
|
||||
|
||||
return cached;
|
||||
#endif
|
||||
}
|
||||
@ -239,11 +246,14 @@ void TrackList::PruneCache() const {
|
||||
}
|
||||
}
|
||||
|
||||
void TrackList::CacheWindow(size_t from, size_t to) const {
|
||||
void TrackList::CacheWindow(size_t from, size_t to, bool async) const {
|
||||
std::unordered_set<int64_t> idsNotInCache;
|
||||
for (size_t i = from; i <= std::min(to, this->ids.size() - 1); i++) {
|
||||
auto id = this->ids[i];
|
||||
if (this->cacheMap.find(id) == this->cacheMap.end()) {
|
||||
if (async && currentWindow.Contains(i)) {
|
||||
continue;
|
||||
}
|
||||
idsNotInCache.insert(id);
|
||||
}
|
||||
}
|
||||
@ -252,12 +262,41 @@ void TrackList::CacheWindow(size_t from, size_t to) const {
|
||||
return;
|
||||
}
|
||||
|
||||
if (async && currentWindow.Valid()) {
|
||||
this->nextWindow.Set(from, to);
|
||||
return; /* when this query finishes we'll start up the next one.
|
||||
this is like a poor man's debounce. or maybe a rich man's debounce? */
|
||||
}
|
||||
|
||||
auto query = std::make_shared<TrackMetadataBatchQuery>(idsNotInCache, this->library);
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
auto& result = query->Result();
|
||||
for (auto& kv : result) {
|
||||
this->AddToCache(kv.first, kv.second);
|
||||
|
||||
if (async) {
|
||||
currentWindow.Set(from, to);
|
||||
this->library->Enqueue(query, 0, [this, from, to, query](auto q) {
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
auto& result = query->Result();
|
||||
for (auto& kv : result) {
|
||||
this->AddToCache(kv.first, kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
this->currentWindow.Reset();
|
||||
if (this->nextWindow.Valid()) {
|
||||
size_t from = nextWindow.from, to = nextWindow.to;
|
||||
nextWindow.Reset();
|
||||
this->CacheWindow(from, to, true);
|
||||
}
|
||||
|
||||
this->WindowCached(const_cast<TrackList*>(this), from, to);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
auto& result = query->Result();
|
||||
for (auto& kv : result) {
|
||||
this->AddToCache(kv.first, kv.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include <core/library/track/Track.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
|
||||
#include <sigslot/sigslot.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
|
||||
@ -47,9 +49,12 @@ namespace musik { namespace core {
|
||||
|
||||
class TrackList :
|
||||
public musik::core::sdk::ITrackList,
|
||||
public std::enable_shared_from_this<TrackList>
|
||||
public std::enable_shared_from_this<TrackList>,
|
||||
public sigslot::has_slots<>
|
||||
{
|
||||
public:
|
||||
mutable sigslot::signal3<const TrackList*, size_t, size_t> WindowCached;
|
||||
|
||||
TrackList(ILibraryPtr library);
|
||||
TrackList(TrackList* other);
|
||||
TrackList(ILibraryPtr library, const int64_t* trackIds, size_t trackIdCount);
|
||||
@ -73,18 +78,29 @@ namespace musik { namespace core {
|
||||
void Shuffle();
|
||||
|
||||
/* implementation specific */
|
||||
TrackPtr Get(size_t index) const;
|
||||
TrackPtr Get(size_t index, bool async = false) const;
|
||||
TrackPtr GetSync(size_t index) const { return this->Get(index, false); }
|
||||
TrackPtr GetAsync(size_t index) const { return this->Get(index, true); }
|
||||
void ClearCache();
|
||||
void Swap(TrackList& list);
|
||||
void CopyFrom(const TrackList& from);
|
||||
void CopyTo(TrackList& to);
|
||||
void CacheWindow(size_t from, size_t to) const;
|
||||
void CacheWindow(size_t from, size_t to, bool async) const;
|
||||
void SetCacheWindowSize(size_t size);
|
||||
const std::vector<int64_t> GetIds() const { return ids; };
|
||||
|
||||
musik::core::sdk::ITrackList* GetSdkValue();
|
||||
|
||||
private:
|
||||
struct QueryWindow {
|
||||
size_t from{ 0 };
|
||||
size_t to{ 0 };
|
||||
bool Contains(size_t i) { return to > 0 && i >= from && i <= to; }
|
||||
void Reset() { from = to = 0; }
|
||||
bool Valid() { return to > 0 && to > from; }
|
||||
void Set(size_t from, size_t to) { this->from = from; this->to = to; }
|
||||
};
|
||||
|
||||
typedef std::list<int64_t> CacheList;
|
||||
typedef std::pair<TrackPtr, CacheList::iterator> CacheValue;
|
||||
typedef std::unordered_map<int64_t, CacheValue> CacheMap;
|
||||
@ -97,6 +113,8 @@ namespace musik { namespace core {
|
||||
mutable CacheList cacheList;
|
||||
mutable CacheMap cacheMap;
|
||||
mutable size_t cacheSize;
|
||||
mutable QueryWindow currentWindow;
|
||||
mutable QueryWindow nextWindow;
|
||||
|
||||
std::vector<int64_t> ids;
|
||||
ILibraryPtr library;
|
||||
|
@ -52,6 +52,8 @@
|
||||
|
||||
#define WINDOW_MESSAGE_SCROLL_TO_PLAYING 1003
|
||||
|
||||
static const bool kGetAsync = false;
|
||||
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::db;
|
||||
@ -117,13 +119,17 @@ void TrackListView::SetRowRenderer(TrackRowRenderers::Renderer renderer) {
|
||||
this->renderer = renderer;
|
||||
}
|
||||
|
||||
void TrackListView::OnTrackListWindowCached(const musik::core::TrackList* track, size_t from, size_t to) {
|
||||
this->Redraw();
|
||||
}
|
||||
|
||||
void TrackListView::OnQueryCompleted(IQuery* query) {
|
||||
if (this->query && query == this->query.get()) {
|
||||
if (this->query->GetStatus() == IQuery::Finished) {
|
||||
bool hadTracks = this->tracks && this->tracks->Count() > 0;
|
||||
bool prevQuerySame = this->lastQueryHash == this->query->GetQueryHash();
|
||||
|
||||
this->tracks = this->query->GetResult();
|
||||
this->SetTrackListAndUpateEventHandlers(this->query->GetResult());
|
||||
this->AdjustTrackListCacheWindowSize();
|
||||
this->headers.Set(this->query->GetHeaders());
|
||||
this->lastQueryHash = this->query->GetQueryHash();
|
||||
@ -153,7 +159,7 @@ std::shared_ptr<TrackList> TrackListView::GetTrackList() {
|
||||
|
||||
void TrackListView::SetTrackList(std::shared_ptr<TrackList> trackList) {
|
||||
if (this->tracks != trackList) {
|
||||
this->tracks = trackList;
|
||||
this->SetTrackListAndUpateEventHandlers(trackList);
|
||||
this->AdjustTrackListCacheWindowSize();
|
||||
this->ScrollToTop();
|
||||
this->SelectFirstTrack();
|
||||
@ -218,6 +224,16 @@ void TrackListView::ScrollToPlaying() {
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListView::SetTrackListAndUpateEventHandlers(std::shared_ptr<TrackList> trackList) {
|
||||
if (this->tracks) {
|
||||
this->tracks->WindowCached.disconnect(this);
|
||||
}
|
||||
this->tracks = trackList;
|
||||
if (this->tracks) {
|
||||
this->tracks->WindowCached.connect(this, &TrackListView::OnTrackListWindowCached);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListView::ProcessMessage(IMessage &message) {
|
||||
if (message.Type() == WINDOW_MESSAGE_SCROLL_TO_PLAYING) {
|
||||
this->ScrollToPlaying();
|
||||
@ -451,11 +467,12 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
||||
/* the next track at the next logical index will have the album
|
||||
tracks we're interesetd in. */
|
||||
auto trackIndex = this->parent.headers.AdapterToTrackListIndex(rawIndex + 1);
|
||||
TrackPtr track = parent.tracks->Get(trackIndex);
|
||||
TrackPtr track = parent.tracks->Get(trackIndex, kGetAsync);
|
||||
|
||||
if (track) {
|
||||
std::string album = track->GetString(constants::Track::ALBUM);
|
||||
|
||||
std::string album = track->GetMetadataState() == MetadataState::Loaded
|
||||
? track->GetString(constants::Track::ALBUM) : "-";
|
||||
|
||||
if (!album.size()) {
|
||||
album = _TSTR("tracklist_unknown_album");
|
||||
}
|
||||
@ -474,19 +491,7 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
||||
}
|
||||
|
||||
size_t trackIndex = this->parent.headers.AdapterToTrackListIndex(rawIndex);
|
||||
TrackPtr track = parent.tracks->Get(trackIndex);
|
||||
|
||||
if (!track ||
|
||||
track->GetMetadataState() == MetadataState::Missing ||
|
||||
track->GetMetadataState() == MetadataState::NotLoaded)
|
||||
{
|
||||
auto entry = std::shared_ptr<SingleLineEntry>(new SingleLineEntry("track missing"));
|
||||
entry->SetAttrs(selected ? Color::ListItemHighlighted : Color::TextError);
|
||||
return entry;
|
||||
}
|
||||
else if (track->GetMetadataState() == MetadataState::Loading) {
|
||||
return std::shared_ptr<SingleLineEntry>(new SingleLineEntry(" -"));
|
||||
}
|
||||
TrackPtr track = parent.tracks->Get(trackIndex, kGetAsync);
|
||||
|
||||
Color attrs = Color::Default;
|
||||
|
||||
@ -510,6 +515,12 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
||||
}
|
||||
}
|
||||
|
||||
if (!track || track->GetMetadataState() != MetadataState::Loaded) {
|
||||
auto entry = std::shared_ptr<SingleLineEntry>(new SingleLineEntry(" -"));
|
||||
entry->SetAttrs(attrs);
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::string text = parent.renderer(
|
||||
track, rawIndex, this->GetWidth(), parent.trackNumType);
|
||||
|
||||
|
@ -97,9 +97,14 @@ namespace musik {
|
||||
protected:
|
||||
virtual cursespp::IScrollAdapter& GetScrollAdapter();
|
||||
|
||||
void SetTrackListAndUpateEventHandlers(
|
||||
std::shared_ptr<musik::core::TrackList> trackList);
|
||||
|
||||
virtual void OnEntryActivated(size_t index);
|
||||
virtual void OnEntryContextMenu(size_t index);
|
||||
virtual void OnDimensionsChanged();
|
||||
virtual void OnTrackListWindowCached(
|
||||
const musik::core::TrackList* track, size_t from, size_t to);
|
||||
|
||||
void OnQueryCompleted(musik::core::db::IQuery* query);
|
||||
void ShowContextMenu();
|
||||
|
@ -79,7 +79,7 @@ static inline void DrawCursor(IInput* input) {
|
||||
WINDOW* content = inputWindow->GetContent();
|
||||
curs_set(1);
|
||||
wtimeout(content, IDLE_TIMEOUT_MS);
|
||||
wmove(content, 0, input->Position());
|
||||
wmove(content, 0, (int) input->Position());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user