From 514540d89cb99725aa38455c52340f2cd4115896 Mon Sep 17 00:00:00 2001 From: ailurux Date: Mon, 22 Jul 2024 14:37:05 +1000 Subject: [PATCH] Queue now keeps file offsets in memory to speed up search --- src/tangara/audio/playlist.cpp | 68 ++++++++++++++++++++++++++++++---- src/tangara/audio/playlist.hpp | 9 +++++ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/tangara/audio/playlist.cpp b/src/tangara/audio/playlist.cpp index 850b7335..f34be9c9 100644 --- a/src/tangara/audio/playlist.cpp +++ b/src/tangara/audio/playlist.cpp @@ -16,7 +16,12 @@ namespace audio { [[maybe_unused]] static constexpr char kTag[] = "playlist"; Playlist::Playlist(std::string playlistFilepath) - : filepath_(playlistFilepath), mutex_(), total_size_(0), pos_(0) {} + : filepath_(playlistFilepath), + mutex_(), + total_size_(0), + pos_(-1), + offset_cache_(&memory::kSpiRamResource), + sample_size_(50) {} auto Playlist::open() -> bool { FRESULT res = @@ -47,8 +52,10 @@ auto Playlist::size() const -> size_t { auto Playlist::append(Item i) -> void { std::unique_lock lock(mutex_); auto offset = f_tell(&file_); + bool first_entry = current_value_.empty(); // Seek to end and append - auto res = f_lseek(&file_, f_size(&file_)); + auto end = f_size(&file_); + auto res = f_lseek(&file_, end); if (res != FR_OK) { ESP_LOGE(kTag, "Seek to end of file failed? Error %d", res); return; @@ -58,16 +65,19 @@ auto Playlist::append(Item i) -> void { if (std::holds_alternative(i)) { path = std::get(i); f_printf(&file_, "%s\n", path.c_str()); - total_size_++; - if (current_value_.empty()) { + if (total_size_ % sample_size_ == 0) { + offset_cache_.push_back(end); + } + if (first_entry) { current_value_ = path; } + total_size_++; } // Restore position res = f_lseek(&file_, offset); - if (res != FR_OK) { - ESP_LOGE(kTag, "Failed to restore file position after append?"); - return; + if (res != FR_OK) { + ESP_LOGE(kTag, "Failed to restore file position after append?"); + return; } res = f_sync(&file_); if (res != FR_OK) { @@ -77,8 +87,25 @@ auto Playlist::append(Item i) -> void { } auto Playlist::skipTo(size_t position) -> void { + std::unique_lock lock(mutex_); + // Check our cache and go to nearest entry pos_ = position; - consumeAndCount(position); + auto remainder = position % sample_size_; + auto quotient = (position - remainder) / sample_size_; + if (offset_cache_.size() < quotient) { + // Fall back case + ESP_LOGW(kTag, "File offset cache failed, falling back..."); + f_rewind(&file_); + advanceBy(pos_); + } + auto entry = offset_cache_.at(quotient); + // Go to byte offset + auto res = f_lseek(&file_, entry); + if (res != FR_OK) { + ESP_LOGW(kTag, "Error going to byte offset %llu for playlist entry index %d", entry, pos_); + } + // Count ahead entries + advanceBy(remainder+1); } auto Playlist::next() -> void { @@ -99,6 +126,7 @@ auto Playlist::value() const -> std::string { } auto Playlist::clear() -> bool { + std::unique_lock lock(mutex_); auto res = f_close(&file_); if (res != FR_OK) { return false; @@ -110,6 +138,7 @@ auto Playlist::clear() -> bool { } total_size_ = 0; current_value_.clear(); + offset_cache_.clear(); pos_ = 0; return true; } @@ -128,6 +157,7 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { size_t count = 0; f_rewind(&file_); while (!f_eof(&file_)) { + auto offset = f_tell(&file_); // TODO: Correctly handle lines longer than this // TODO: Also correctly handle the case where the last entry doesn't end in // \n @@ -136,6 +166,9 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); return false; } + if (count % sample_size_ == 0) { + offset_cache_.push_back(offset); + } count++; if (upto >= 0 && count > upto) { @@ -151,4 +184,23 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { return true; } +auto Playlist::advanceBy(ssize_t amt) -> bool { + TCHAR buff[512]; + size_t count = 0; + while (!f_eof(&file_)) { + auto res = f_gets(buff, 512, &file_); + if (res == NULL) { + ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); + return false; + } + count++; + if (count >= amt) { + size_t len = strlen(buff); + current_value_.assign(buff, len - 1); + break; + } + } + return true; +} + } // namespace audio \ No newline at end of file diff --git a/src/tangara/audio/playlist.hpp b/src/tangara/audio/playlist.hpp index b278d8a6..3d914663 100644 --- a/src/tangara/audio/playlist.hpp +++ b/src/tangara/audio/playlist.hpp @@ -50,7 +50,16 @@ class Playlist { FIL file_; std::string current_value_; + + std::pmr::vector offset_cache_; // List of offsets determined by sample size; + /* + * How many tracks per offset saved (ie, a value of 100 means every 100 tracks the file offset is saved) + * This speeds up searches, especially in the case of shuffling a lot of tracks. + */ + const uint32_t sample_size_; + auto consumeAndCount(ssize_t upto) -> bool; + auto advanceBy(ssize_t amt) -> bool; }; } // namespace audio \ No newline at end of file