summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcooljqln <cooljqln@noreply.codeberg.org>2024-10-04 05:40:18 +0000
committercooljqln <cooljqln@noreply.codeberg.org>2024-10-04 05:40:18 +0000
commit3287580647338035f02f9c631df6153a5c4fc7d7 (patch)
treeed69c72aca940ea5af535606d16de64064f602c7 /src
parent4c2bcbfb56e589c61d065e285655456ac833aa08 (diff)
parent7cc6f198cf437ae60185b65bfa556341be8ec63f (diff)
downloadtangara-fw-3287580647338035f02f9c631df6153a5c4fc7d7.tar.gz
Merge pull request 'daniel/track-toc' (#110) from daniel/track-toc into main
Yay! Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/110
Diffstat (limited to 'src')
-rw-r--r--src/tangara/audio/playlist.cpp151
-rw-r--r--src/tangara/audio/playlist.hpp12
-rw-r--r--src/tangara/audio/track_queue.cpp10
-rw-r--r--src/tangara/audio/track_queue.hpp5
-rw-r--r--src/tangara/system_fsm/running.cpp1
5 files changed, 171 insertions, 8 deletions
diff --git a/src/tangara/audio/playlist.cpp b/src/tangara/audio/playlist.cpp
index 1436e2d2..f57f078d 100644
--- a/src/tangara/audio/playlist.cpp
+++ b/src/tangara/audio/playlist.cpp
@@ -4,9 +4,12 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "playlist.hpp"
+#include <stdint.h>
#include <string>
+#include "cppbor.h"
+#include "cppbor_parse.h"
#include "esp_log.h"
#include "ff.h"
@@ -29,6 +32,7 @@ Playlist::Playlist(const std::string& playlistFilepath)
auto Playlist::open() -> bool {
std::unique_lock<std::mutex> lock(mutex_);
+
if (file_open_) {
return true;
}
@@ -42,10 +46,12 @@ auto Playlist::open() -> bool {
file_open_ = true;
file_error_ = false;
- // Count the playlist size and build our offset cache.
- countItems();
- // Advance to the first item.
- skipToWithoutCache(0);
+ if (!deserialiseCache()) {
+ // Count the playlist size and build our offset cache.
+ countItems();
+ // Advance to the first item.
+ skipToWithoutCache(0);
+ }
return !file_error_;
}
@@ -100,6 +106,106 @@ auto Playlist::skipTo(size_t position) -> void {
skipToLocked(position);
}
+// Serialise the cache to a file to avoid having to rescan
+// the entire queue when resuming
+auto Playlist::serialiseCache() -> bool {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (!file_open_) {
+ return false;
+ }
+
+ FIL file;
+
+ // Open the cache file
+ std::string cache_file = filepath_ + ".cache";
+ FRESULT res =
+ f_open(&file, cache_file.c_str(), FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
+ if (res != FR_OK) {
+ ESP_LOGE(kTag, "failed to open cache file! res: %i", res);
+ return false;
+ }
+
+ cppbor::Array data;
+ // First item = file size of queue file (for checking this file matches)
+ data.add(f_size(&file_));
+ // Next item = number of tracks in this queue
+ data.add(total_size_);
+
+ // Next, write out every cached offset
+ for (uint64_t offset : offset_cache_) {
+ data.add(offset);
+ }
+
+ auto encoded = data.encode();
+
+ UINT bytes_written = 0;
+ f_write(&file, encoded.data(), encoded.size(), &bytes_written);
+ if (bytes_written != encoded.size()) {
+ return false;
+ }
+
+ f_close(&file);
+ return true;
+}
+
+auto Playlist::deserialiseCache() -> bool {
+ if (!file_open_) {
+ return false;
+ }
+
+ FIL file;
+
+ // Open the cache file
+ std::string cache_file = filepath_ + ".cache";
+ FRESULT res =
+ f_open(&file, cache_file.c_str(), FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
+ if (res != FR_OK) {
+ ESP_LOGE(kTag, "failed to open cache file! res: %i", res);
+ return false;
+ }
+
+ std::vector<uint8_t> encoded;
+ encoded.resize(f_size(&file));
+
+ UINT bytes_read;
+ f_read(&file, encoded.data(), encoded.size(), &bytes_read);
+ if (bytes_read != encoded.size()) {
+ return false;
+ }
+
+ auto [data, unused, err] = cppbor::parse(encoded);
+ if (!data || data->type() != cppbor::ARRAY) {
+ return false;
+ }
+ auto entries = data->asArray();
+
+ // Double check the expected file size matches.
+ if (entries->get(0)->asUint()->unsignedValue() != f_size(&file_)) {
+ return false;
+ }
+
+ total_size_ = entries->get(1)->asUint()->unsignedValue();
+
+ // In case we have existing entries
+ offset_cache_.clear();
+
+ // Read in the cache
+ for (size_t i = 2; i < entries->size(); i++) {
+ offset_cache_.push_back(entries->get(i)->asUint()->unsignedValue());
+ }
+
+ f_close(&file);
+ return true;
+}
+
+auto Playlist::close() -> void {
+ if (file_open_) {
+ f_close(&file_);
+ file_open_ = false;
+ file_error_ = false;
+ }
+}
+
auto Playlist::skipToLocked(size_t position) -> void {
if (!file_open_ || file_error_) {
return;
@@ -210,9 +316,46 @@ auto Playlist::nextItem(std::span<TCHAR> buf)
MutablePlaylist::MutablePlaylist(const std::string& playlistFilepath)
: Playlist(playlistFilepath) {}
+auto MutablePlaylist::open() -> bool {
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (file_open_) {
+ return true;
+ }
+
+ FRESULT res =
+ f_open(&file_, filepath_.c_str(), FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
+ if (res != FR_OK) {
+ ESP_LOGE(kTag, "failed to open file! res: %i", res);
+ return false;
+ }
+ file_open_ = true;
+ file_error_ = false;
+
+ auto queue_filesize = f_size(&file_);
+
+ if (!deserialiseCache()) {
+ // If there's no cache (or deserialising failed) and the queue is
+ // sufficiently large, abort and clear the queue
+ if (queue_filesize > 50000) {
+ clearLocked();
+ } else {
+ // Otherwise, read in the existing entries
+ countItems();
+ // Advance to the first item.
+ skipToWithoutCache(0);
+ }
+ }
+
+ return !file_error_;
+}
+
auto MutablePlaylist::clear() -> bool {
std::unique_lock<std::mutex> lock(mutex_);
+ return clearLocked();
+}
+auto MutablePlaylist::clearLocked() -> bool {
// Try to recover from any IO errors.
if (file_error_ && file_open_) {
file_error_ = false;
diff --git a/src/tangara/audio/playlist.hpp b/src/tangara/audio/playlist.hpp
index ac62c82e..1e05e9c4 100644
--- a/src/tangara/audio/playlist.hpp
+++ b/src/tangara/audio/playlist.hpp
@@ -33,7 +33,7 @@ class Playlist {
virtual ~Playlist();
using Item =
std::variant<database::TrackId, database::TrackIterator, std::string>;
- auto open() -> bool;
+ virtual auto open() -> bool;
auto filepath() const -> std::string;
auto currentPosition() const -> size_t;
@@ -45,6 +45,10 @@ class Playlist {
auto prev() -> void;
auto skipTo(size_t position) -> void;
+ auto serialiseCache() -> bool;
+ auto deserialiseCache() -> bool;
+ auto close() -> void;
+
protected:
const std::string filepath_;
@@ -68,7 +72,7 @@ class Playlist {
*/
const uint32_t sample_size_;
- private:
+ protected:
auto skipToLocked(size_t position) -> void;
auto countItems() -> void;
auto advanceBy(ssize_t amt) -> bool;
@@ -79,9 +83,13 @@ class Playlist {
class MutablePlaylist : public Playlist {
public:
MutablePlaylist(const std::string& playlistFilepath);
+ auto open() -> bool override;
auto clear() -> bool;
auto append(Item i) -> void;
+
+ private:
+ auto clearLocked() -> bool;
};
} // namespace audio
diff --git a/src/tangara/audio/track_queue.cpp b/src/tangara/audio/track_queue.cpp
index ff24637b..05ac0b95 100644
--- a/src/tangara/audio/track_queue.cpp
+++ b/src/tangara/audio/track_queue.cpp
@@ -159,6 +159,13 @@ auto TrackQueue::open() -> bool {
return playlist_.open();
}
+auto TrackQueue::close() -> void {
+ playlist_.close();
+ if (opened_playlist_) {
+ opened_playlist_->close();
+ }
+}
+
auto TrackQueue::openPlaylist(const std::string& playlist_file, bool notify)
-> bool {
opened_playlist_.emplace(playlist_file);
@@ -422,6 +429,9 @@ auto TrackQueue::serialise() -> std::string {
cppbor::Uint{shuffle_->pos()},
});
}
+
+ playlist_.serialiseCache();
+
return encoded.toString();
}
diff --git a/src/tangara/audio/track_queue.hpp b/src/tangara/audio/track_queue.hpp
index 727b4be0..383c204e 100644
--- a/src/tangara/audio/track_queue.hpp
+++ b/src/tangara/audio/track_queue.hpp
@@ -76,8 +76,9 @@ class TrackQueue {
auto currentPosition(size_t position) -> bool;
auto totalSize() const -> size_t;
auto open() -> bool;
- auto openPlaylist(const std::string& playlist_file, bool notify = true)
- -> bool;
+ auto close() -> void;
+ auto openPlaylist(const std::string& playlist_file,
+ bool notify = true) -> bool;
auto playFromPosition(const std::string& filepath, uint32_t position) -> void;
using Item =
diff --git a/src/tangara/system_fsm/running.cpp b/src/tangara/system_fsm/running.cpp
index f065737b..227eac2c 100644
--- a/src/tangara/system_fsm/running.cpp
+++ b/src/tangara/system_fsm/running.cpp
@@ -214,6 +214,7 @@ void Running::react(const internal::Mount& ev) {
auto Running::unmountStorage() -> void {
ESP_LOGW(kTag, "unmounting storage");
+ sServices->track_queue().close();
sServices->database({});
sStorage.reset();
updateSdState(drivers::SdState::kNotMounted);