summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-12-07 16:57:05 +1100
committerjacqueline <me@jacqueline.id.au>2023-12-07 17:00:30 +1100
commit3f7f199cb940c8d5f6d48f77fd59971adffe49ef (patch)
treeaa22162e46c5e9ccce4c7ee8537b493f437664d9 /src/audio
parent009f69c929eb1d1b65d75b0937fbf3b8de5d9148 (diff)
downloadtangara-fw-3f7f199cb940c8d5f6d48f77fd59971adffe49ef.tar.gz
Remove pre-iterator concepts
- No more IndexRecord/Result/dbGetPage nonsense - Queue is just track ids - i am so tired and have so much to do
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/CMakeLists.txt2
-rw-r--r--src/audio/audio_fsm.cpp40
-rw-r--r--src/audio/fatfs_audio_input.cpp32
-rw-r--r--src/audio/include/audio_events.hpp2
-rw-r--r--src/audio/include/audio_fsm.hpp2
-rw-r--r--src/audio/include/fatfs_audio_input.hpp9
-rw-r--r--src/audio/include/track_queue.hpp60
-rw-r--r--src/audio/track_queue.cpp439
8 files changed, 147 insertions, 439 deletions
diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt
index 95bab4c2..0f90334b 100644
--- a/src/audio/CMakeLists.txt
+++ b/src/audio/CMakeLists.txt
@@ -9,6 +9,6 @@ idf_component_register(
"audio_source.cpp"
INCLUDE_DIRS "include"
REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm"
- "database" "system_fsm" "playlist" "speexdsp")
+ "database" "system_fsm" "speexdsp")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp
index ce610abb..3bd4d396 100644
--- a/src/audio/audio_fsm.cpp
+++ b/src/audio/audio_fsm.cpp
@@ -93,6 +93,17 @@ void AudioState::react(const OutputModeChanged& ev) {
sOutput->SetMode(IAudioOutput::Modes::kOnPaused);
}
+auto AudioState::playTrack(database::TrackId id) -> void {
+ sCurrentTrack = id;
+ sServices->bg_worker().Dispatch<void>([=]() {
+ auto db = sServices->database().lock();
+ if (!db) {
+ return;
+ }
+ sFileSource->SetPath(db->getTrackPath(id));
+ });
+}
+
namespace states {
void Uninitialised::react(const system_fsm::BootComplete& ev) {
@@ -143,19 +154,11 @@ void Standby::react(const internal::InputFileOpened& ev) {
}
void Standby::react(const QueueUpdate& ev) {
- auto current_track = sServices->track_queue().Current();
+ auto current_track = sServices->track_queue().current();
if (!current_track || (sCurrentTrack && *sCurrentTrack == *current_track)) {
return;
}
-
- sCurrentTrack = current_track;
-
- auto db = sServices->database().lock();
- if (!db) {
- ESP_LOGW(kTag, "database not open; ignoring play request");
- return;
- }
- sFileSource->SetPath(db->GetTrackPath(*current_track));
+ playTrack(*current_track);
}
void Standby::react(const TogglePlayPause& ev) {
@@ -187,22 +190,14 @@ void Playback::react(const QueueUpdate& ev) {
if (!ev.current_changed) {
return;
}
- auto current_track = sServices->track_queue().Current();
+ auto current_track = sServices->track_queue().current();
if (!current_track) {
sFileSource->SetPath();
sCurrentTrack.reset();
transit<Standby>();
return;
}
-
- sCurrentTrack = current_track;
-
- auto db = sServices->database().lock();
- if (!db) {
- return;
- }
-
- sFileSource->SetPath(db->GetTrackPath(*current_track));
+ playTrack(*current_track);
}
void Playback::react(const TogglePlayPause& ev) {
@@ -220,9 +215,8 @@ void Playback::react(const internal::InputFileClosed& ev) {}
void Playback::react(const internal::InputFileFinished& ev) {
ESP_LOGI(kTag, "finished playing file");
- auto editor = sServices->track_queue().Edit();
- sServices->track_queue().Next(editor);
- if (!sServices->track_queue().Current()) {
+ sServices->track_queue().next();
+ if (!sServices->track_queue().current()) {
transit<Standby>();
}
}
diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp
index 5594718f..58d5852f 100644
--- a/src/audio/fatfs_audio_input.cpp
+++ b/src/audio/fatfs_audio_input.cpp
@@ -50,23 +50,19 @@ FatfsAudioInput::FatfsAudioInput(database::ITagParser& tag_parser,
bg_worker_(bg_worker),
new_stream_mutex_(),
new_stream_(),
- has_new_stream_(false),
- pending_path_() {}
+ has_new_stream_(false) {}
FatfsAudioInput::~FatfsAudioInput() {}
-auto FatfsAudioInput::SetPath(std::future<std::optional<std::pmr::string>> fut)
- -> void {
- std::lock_guard<std::mutex> guard{new_stream_mutex_};
- pending_path_.reset(
- new database::FutureFetcher<std::optional<std::pmr::string>>(
- std::move(fut)));
-
- has_new_stream_ = true;
- has_new_stream_.notify_one();
+auto FatfsAudioInput::SetPath(std::optional<std::string> path) -> void {
+ if (path) {
+ SetPath(*path);
+ } else {
+ SetPath();
+ }
}
-auto FatfsAudioInput::SetPath(const std::pmr::string& path) -> void {
+auto FatfsAudioInput::SetPath(const std::string& path) -> void {
std::lock_guard<std::mutex> guard{new_stream_mutex_};
if (OpenFile(path)) {
has_new_stream_ = true;
@@ -96,16 +92,6 @@ auto FatfsAudioInput::NextStream() -> std::shared_ptr<TaggedStream> {
continue;
}
- // If the path is a future, then wait for it to complete.
- if (pending_path_) {
- auto res = pending_path_->Result();
- pending_path_.reset();
-
- if (res && *res) {
- OpenFile(**res);
- }
- }
-
if (new_stream_ == nullptr) {
continue;
}
@@ -117,7 +103,7 @@ auto FatfsAudioInput::NextStream() -> std::shared_ptr<TaggedStream> {
}
}
-auto FatfsAudioInput::OpenFile(const std::pmr::string& path) -> bool {
+auto FatfsAudioInput::OpenFile(const std::string& path) -> bool {
ESP_LOGI(kTag, "opening file %s", path.c_str());
auto tags = tag_parser_.ReadAndParseTags(path);
diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp
index 9994a9f6..68efcafb 100644
--- a/src/audio/include/audio_events.hpp
+++ b/src/audio/include/audio_events.hpp
@@ -42,7 +42,7 @@ struct QueueUpdate : tinyfsm::Event {
};
struct PlayFile : tinyfsm::Event {
- std::pmr::string filename;
+ std::string filename;
};
struct StepUpVolume : tinyfsm::Event {};
diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp
index 256e1430..c9fac08b 100644
--- a/src/audio/include/audio_fsm.hpp
+++ b/src/audio/include/audio_fsm.hpp
@@ -60,6 +60,8 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
virtual void react(const internal::AudioPipelineIdle&) {}
protected:
+ auto playTrack(database::TrackId id) -> void;
+
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
static std::shared_ptr<FatfsAudioInput> sFileSource;
diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp
index c7d52ca3..f9635b86 100644
--- a/src/audio/include/fatfs_audio_input.hpp
+++ b/src/audio/include/fatfs_audio_input.hpp
@@ -38,8 +38,8 @@ class FatfsAudioInput : public IAudioSource {
* Immediately cease reading any current source, and begin reading from the
* given file path.
*/
- auto SetPath(std::future<std::optional<std::pmr::string>>) -> void;
- auto SetPath(const std::pmr::string&) -> void;
+ auto SetPath(std::optional<std::string>) -> void;
+ auto SetPath(const std::string&) -> void;
auto SetPath() -> void;
auto HasNewStream() -> bool override;
@@ -49,7 +49,7 @@ class FatfsAudioInput : public IAudioSource {
FatfsAudioInput& operator=(const FatfsAudioInput&) = delete;
private:
- auto OpenFile(const std::pmr::string& path) -> bool;
+ auto OpenFile(const std::string& path) -> bool;
auto ContainerToStreamType(database::Container)
-> std::optional<codecs::StreamType>;
@@ -61,9 +61,6 @@ class FatfsAudioInput : public IAudioSource {
std::shared_ptr<TaggedStream> new_stream_;
std::atomic<bool> has_new_stream_;
-
- std::unique_ptr<database::FutureFetcher<std::optional<std::pmr::string>>>
- pending_path_;
};
} // namespace audio
diff --git a/src/audio/include/track_queue.hpp b/src/audio/include/track_queue.hpp
index bec887ae..4a1984c9 100644
--- a/src/audio/include/track_queue.hpp
+++ b/src/audio/include/track_queue.hpp
@@ -9,10 +9,11 @@
#include <list>
#include <memory>
#include <mutex>
+#include <shared_mutex>
#include <vector>
#include "database.hpp"
-#include "source.hpp"
+#include "tasks.hpp"
#include "track.hpp"
namespace audio {
@@ -32,77 +33,52 @@ namespace audio {
*/
class TrackQueue {
public:
- TrackQueue();
-
- class Editor {
- public:
- ~Editor();
-
- // Cannot be copied or moved.
- Editor(const Editor&) = delete;
- Editor& operator=(const Editor&) = delete;
-
- private:
- friend TrackQueue;
-
- Editor(TrackQueue&);
-
- std::lock_guard<std::recursive_mutex> lock_;
- bool has_current_changed_;
- };
-
- auto Edit() -> Editor;
+ TrackQueue(tasks::Worker& bg_worker);
/* Returns the currently playing track. */
- auto Current() const -> std::optional<database::TrackId>;
+ auto current() const -> std::optional<database::TrackId>;
/* Returns, in order, tracks that have been queued to be played next. */
- auto PeekNext(std::size_t limit) const -> std::vector<database::TrackId>;
+ auto peekNext(std::size_t limit) const -> std::vector<database::TrackId>;
/*
* Returns the tracks in the queue that have already been played, ordered
* most recently played first.
*/
- auto PeekPrevious(std::size_t limit) const -> std::vector<database::TrackId>;
+ auto peekPrevious(std::size_t limit) const -> std::vector<database::TrackId>;
- auto GetCurrentPosition() const -> size_t;
- auto GetTotalSize() const -> size_t;
+ auto currentPosition() const -> size_t;
+ auto totalSize() const -> size_t;
using Item = std::variant<database::TrackId, database::TrackIterator>;
- auto Insert(Editor&, Item, size_t) -> void;
- auto Append(Editor&, Item i) -> void;
+ auto insert(Item) -> void;
+ auto append(Item i) -> void;
/*
* Advances to the next track in the queue, placing the current track at the
* front of the 'played' queue.
*/
- auto Next(Editor&) -> std::optional<database::TrackId>;
- auto Previous(Editor&) -> std::optional<database::TrackId>;
+ auto next() -> void;
+ auto previous() -> void;
- auto SkipTo(Editor&, database::TrackId) -> void;
+ auto skipTo(database::TrackId) -> void;
/*
* Removes all tracks from all queues, and stops any currently playing track.
*/
- auto Clear(Editor&) -> void;
-
- auto Save(std::weak_ptr<database::Database>) -> void;
- auto Load(std::weak_ptr<database::Database>) -> void;
+ auto clear() -> void;
// Cannot be copied or moved.
TrackQueue(const TrackQueue&) = delete;
TrackQueue& operator=(const TrackQueue&) = delete;
private:
- // FIXME: Make this a shared_mutex so that multithread reads don't block.
- mutable std::recursive_mutex mutex_;
+ mutable std::shared_mutex mutex_;
- std::optional<database::TrackId> current_;
+ tasks::Worker& bg_worker_;
- // Note: stored in reverse order, i.e. most recent played it at the *back* of
- // this vector.
- std::pmr::vector<database::TrackId> played_;
- std::pmr::vector<Item> enqueued_;
+ size_t pos_;
+ std::pmr::vector<database::TrackId> tracks_;
};
} // namespace audio
diff --git a/src/audio/track_queue.cpp b/src/audio/track_queue.cpp
index eb761590..c1187107 100644
--- a/src/audio/track_queue.cpp
+++ b/src/audio/track_queue.cpp
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <algorithm>
+#include <memory>
#include <mutex>
#include <optional>
#include <variant>
@@ -19,7 +20,7 @@
#include "database.hpp"
#include "event_queue.hpp"
#include "memory_resource.hpp"
-#include "source.hpp"
+#include "tasks.hpp"
#include "track.hpp"
#include "ui_fsm.hpp"
@@ -27,385 +28,137 @@ namespace audio {
[[maybe_unused]] static constexpr char kTag[] = "tracks";
-static const std::string kSerialiseKey = "queue";
-static const std::string kCurrentKey = "cur";
-static const std::string kPlayedKey = "prev";
-static const std::string kEnqueuedKey = "next";
-
-TrackQueue::Editor::Editor(TrackQueue& queue)
- : lock_(queue.mutex_), has_current_changed_(false) {}
-
-TrackQueue::Editor::~Editor() {
- QueueUpdate ev{.current_changed = has_current_changed_};
- events::Audio().Dispatch(ev);
+auto notifyChanged(bool current_changed) -> void {
+ QueueUpdate ev{.current_changed = current_changed};
events::Ui().Dispatch(ev);
+ events::Audio().Dispatch(ev);
}
-TrackQueue::TrackQueue()
+TrackQueue::TrackQueue(tasks::Worker& bg_worker)
: mutex_(),
- current_(),
- played_(&memory::kSpiRamResource),
- enqueued_(&memory::kSpiRamResource) {}
+ bg_worker_(bg_worker),
+ pos_(0),
+ tracks_(&memory::kSpiRamResource) {}
-auto TrackQueue::Edit() -> Editor {
- return Editor(*this);
-}
-
-auto TrackQueue::Current() const -> std::optional<database::TrackId> {
- const std::lock_guard<std::recursive_mutex> lock(mutex_);
- return current_;
-}
-
-auto TrackQueue::PeekNext(std::size_t limit) const
- -> std::vector<database::TrackId> {
- const std::lock_guard<std::recursive_mutex> lock(mutex_);
- std::vector<database::TrackId> ret;
-
- for (auto it = enqueued_.begin(); it != enqueued_.end() && limit > 0; it++) {
- std::visit(
- [&](auto&& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, database::TrackId>) {
- ret.push_back(arg);
- limit--;
- } else if constexpr (std::is_same_v<T, database::TrackIterator>) {
- auto copy = arg;
- while (limit > 0) {
- auto next = copy.Next();
- if (!next) {
- break;
- }
- ret.push_back(*next);
- limit--;
- }
- }
- },
- *it);
+auto TrackQueue::current() const -> std::optional<database::TrackId> {
+ const std::shared_lock<std::shared_mutex> lock(mutex_);
+ if (pos_ >= tracks_.size()) {
+ return {};
}
-
- return ret;
+ return tracks_[pos_];
}
-auto TrackQueue::PeekPrevious(std::size_t limit) const
+auto TrackQueue::peekNext(std::size_t limit) const
-> std::vector<database::TrackId> {
- const std::lock_guard<std::recursive_mutex> lock(mutex_);
- std::vector<database::TrackId> ret;
- ret.reserve(limit);
-
- for (auto it = played_.rbegin(); it != played_.rend(); it++, limit--) {
- ret.push_back(*it);
- }
-
- return ret;
-}
-
-auto TrackQueue::GetCurrentPosition() const -> size_t {
- const std::lock_guard<std::recursive_mutex> lock(mutex_);
- size_t played = played_.size();
- if (current_) {
- played += 1;
+ const std::shared_lock<std::shared_mutex> lock(mutex_);
+ std::vector<database::TrackId> out;
+ for (size_t i = pos_ + 1; i < pos_ + limit + 1 && i < tracks_.size(); i++) {
+ out.push_back(i);
}
- return played;
+ return out;
}
-auto TrackQueue::GetTotalSize() const -> size_t {
- const std::lock_guard<std::recursive_mutex> lock(mutex_);
- size_t total = GetCurrentPosition();
-
- for (const auto& item : enqueued_) {
- std::visit(
- [&](auto&& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, database::TrackId>) {
- total++;
- } else if constexpr (std::is_same_v<T, database::TrackIterator>) {
- total += arg.Size();
- }
- },
- item);
+auto TrackQueue::peekPrevious(std::size_t limit) const
+ -> std::vector<database::TrackId> {
+ const std::shared_lock<std::shared_mutex> lock(mutex_);
+ std::vector<database::TrackId> out;
+ for (size_t i = pos_ - 1; i < pos_ - limit - 1 && i >= tracks_.size(); i--) {
+ out.push_back(i);
}
-
- return total;
+ return out;
}
-auto TrackQueue::Insert(Editor& ed, Item i, size_t index) -> void {
- if (index == 0) {
- enqueued_.insert(enqueued_.begin(), i);
- }
-
- // We can't insert halfway through an iterator, so we need to ensure that the
- // first `index` items in the queue are reified into track ids.
- size_t current_index = 0;
- while (current_index < index && current_index < enqueued_.size()) {
- std::visit(
- [&](auto&& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, database::TrackId>) {
- // This item is already a track id; nothing to do.
- current_index++;
- } else if constexpr (std::is_same_v<T, database::TrackIterator>) {
- // This item is an iterator. Push it back one, replacing its old
- // index with the next value from it.
- auto next = arg.Next();
- auto iterator_index = enqueued_.begin() + current_index;
- if (!next) {
- // Out of values. Remove the iterator completely.
- enqueued_.erase(iterator_index);
- // Don't increment current_index, since the next item in the
- // queue will have been moved down.
- } else {
- enqueued_.insert(iterator_index, *next);
- current_index++;
- }
- }
- },
- enqueued_[current_index]);
- }
-
- // Double check the previous loop didn't run out of items.
- if (index > enqueued_.size()) {
- ESP_LOGE(kTag, "insert index was out of bounds");
- return;
- }
-
- // Finally, we can now do the actual insertion.
- enqueued_.insert(enqueued_.begin() + index, i);
+auto TrackQueue::currentPosition() const -> size_t {
+ const std::shared_lock<std::shared_mutex> lock(mutex_);
+ return pos_;
}
-auto TrackQueue::Append(Editor& ed, Item i) -> void {
- enqueued_.push_back(i);
- if (!current_) {
- Next(ed);
- }
+auto TrackQueue::totalSize() const -> size_t {
+ const std::shared_lock<std::shared_mutex> lock(mutex_);
+ return tracks_.size();
}
-auto TrackQueue::Next(Editor& ed) -> std::optional<database::TrackId> {
- if (current_) {
- ed.has_current_changed_ = true;
- played_.push_back(*current_);
- }
- current_.reset();
-
- while (!current_ && !enqueued_.empty()) {
- ed.has_current_changed_ = true;
- std::visit(
- [&](auto&& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, database::TrackId>) {
- current_ = arg;
- enqueued_.erase(enqueued_.begin());
- } else if constexpr (std::is_same_v<T, database::TrackIterator>) {
- auto next = arg.Next();
- if (!next) {
- enqueued_.erase(enqueued_.begin());
- } else {
- current_ = *next;
- }
- }
- },
- enqueued_.front());
+auto TrackQueue::insert(Item i) -> void {
+ bool current_changed = pos_ == tracks_.size();
+ if (std::holds_alternative<database::TrackId>(i)) {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ tracks_.push_back(std::get<database::TrackId>(i));
+ notifyChanged(current_changed);
+ } else if (std::holds_alternative<database::TrackIterator>(i)) {
+ bg_worker_.Dispatch<void>([=, this]() {
+ database::TrackIterator it = std::get<database::TrackIterator>(i);
+ size_t working_pos = pos_;
+ while (true) {
+ auto next = *it;
+ if (!next) {
+ break;
+ }
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ tracks_.insert(tracks_.begin() + working_pos, *next);
+ working_pos++;
+ it++;
+ }
+ notifyChanged(current_changed);
+ });
}
-
- return current_;
}
-auto TrackQueue::Previous(Editor& ed) -> std::optional<database::TrackId> {
- if (played_.empty()) {
- return current_;
- }
- ed.has_current_changed_ = true;
- if (current_) {
- enqueued_.insert(enqueued_.begin(), *current_);
+auto TrackQueue::append(Item i) -> void {
+ bool current_changed = pos_ == tracks_.size();
+ if (std::holds_alternative<database::TrackId>(i)) {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ tracks_.push_back(std::get<database::TrackId>(i));
+ notifyChanged(current_changed);
+ } else if (std::holds_alternative<database::TrackIterator>(i)) {
+ bg_worker_.Dispatch<void>([=, this]() {
+ database::TrackIterator it = std::get<database::TrackIterator>(i);
+ while (true) {
+ auto next = *it;
+ if (!next) {
+ break;
+ }
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ tracks_.push_back(*next);
+ it++;
+ }
+ notifyChanged(current_changed);
+ });
}
- current_ = played_.back();
- played_.pop_back();
- return current_;
}
-auto TrackQueue::SkipTo(Editor& ed, database::TrackId id) -> void {
- while ((!current_ || *current_ != id) && !enqueued_.empty()) {
- Next(ed);
- }
-}
+auto TrackQueue::next() -> void {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ pos_ = std::min<size_t>(pos_ + 1, tracks_.size());
-auto TrackQueue::Clear(Editor& ed) -> void {
- ed.has_current_changed_ = current_.has_value();
- current_.reset();
- played_.clear();
- enqueued_.clear();
+ notifyChanged(true);
}
-auto TrackQueue::Save(std::weak_ptr<database::Database> db) -> void {
- cppbor::Map root{};
-
- if (current_) {
- root.add(cppbor::Bstr{kCurrentKey}, cppbor::Uint{*current_});
- }
-
- cppbor::Array played{};
- for (const auto& id : played_) {
- played.add(cppbor::Uint{id});
- }
- root.add(cppbor::Bstr{kPlayedKey}, std::move(played));
-
- cppbor::Array enqueued{};
- for (const auto& item : enqueued_) {
- std::visit(
- [&](auto&& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, database::TrackId>) {
- enqueued.add(cppbor::Uint{arg});
- } else if constexpr (std::is_same_v<T, database::TrackIterator>) {
- enqueued.add(arg.cbor());
- }
- },
- item);
+auto TrackQueue::previous() -> void {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ if (pos_ > 0) {
+ pos_--;
}
- root.add(cppbor::Bstr{kEnqueuedKey}, std::move(enqueued));
- auto db_lock = db.lock();
- if (!db_lock) {
- return;
- }
- db_lock->Put(kSerialiseKey, root.toString());
+ notifyChanged(true);
}
-class Parser : public cppbor::ParseClient {
- public:
- Parser(std::weak_ptr<database::Database> db,
- std::optional<database::TrackId>& current,
- std::pmr::vector<database::TrackId>& played,
- std::pmr::vector<TrackQueue::Item>& enqueued)
- : state_(State::kInit),
- db_(db),
- current_(current),
- played_(played),
- enqueued_(enqueued) {}
-
- virtual ParseClient* item(std::unique_ptr<cppbor::Item>& item,
- const uint8_t* hdrBegin,
- const uint8_t* valueBegin,
- const uint8_t* end) override {
- switch (state_) {
- case State::kInit:
- if (item->type() == cppbor::MAP) {
- state_ = State::kRoot;
- }
- break;
- case State::kRoot:
- if (item->type() != cppbor::TSTR) {
- break;
- }
- if (item->asTstr()->value() == kCurrentKey) {
- state_ = State::kCurrent;
- } else if (item->asTstr()->value() == kPlayedKey) {
- state_ = State::kPlayed;
- } else if (item->asTstr()->value() == kEnqueuedKey) {
- state_ = State::kEnqueued;
- }
- break;
- case State::kCurrent:
- if (item->type() == cppbor::UINT) {
- current_ = item->asUint()->value();
- }
- state_ = State::kRoot;
- break;
- case State::kPlayed:
- if (item->type() == cppbor::UINT) {
- played_.push_back(item->asUint()->value());
- }
- break;
- case State::kEnqueued:
- if (item->type() == cppbor::UINT) {
- played_.push_back(item->asUint()->value());
- } else if (item->type() == cppbor::ARRAY) {
- queue_depth_ = 1;
- state_ = State::kEnqueuedIterator;
- }
- break;
- case State::kEnqueuedIterator:
- if (item->type() == cppbor::MAP || item->type() == cppbor::ARRAY) {
- queue_depth_++;
- }
- break;
- case State::kFinished:
- break;
- }
-
- return this;
- }
-
- ParseClient* itemEnd(std::unique_ptr<cppbor::Item>& item,
- const uint8_t* hdrBegin,
- const uint8_t* valueBegin,
- const uint8_t* end) override {
- std::optional<database::TrackIterator> parsed_it;
- switch (state_) {
- case State::kInit:
- case State::kRoot:
- case State::kCurrent:
- state_ = State::kFinished;
- break;
- case State::kEnqueued:
- case State::kPlayed:
- state_ = State::kRoot;
- break;
- case State::kEnqueuedIterator:
- if (item->type() == cppbor::MAP || item->type() == cppbor::ARRAY) {
- queue_depth_++;
- }
- if (queue_depth_ == 0) {
- parsed_it = database::TrackIterator::Parse(db_, *item->asArray());
- if (parsed_it) {
- enqueued_.push_back(std::move(*parsed_it));
- }
- }
- state_ = State::kEnqueued;
- break;
- case State::kFinished:
- break;
+auto TrackQueue::skipTo(database::TrackId id) -> void {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ for (size_t i = pos_; i < tracks_.size(); i++) {
+ if (tracks_[i] == id) {
+ pos_ = i;
}
- return this;
}
- void error(const uint8_t* position,
- const std::string& errorMessage) override {
- ESP_LOGE(kTag, "restoring saved queue failed: %s", errorMessage.c_str());
- }
-
- private:
- enum class State {
- kInit,
- kRoot,
- kCurrent,
- kPlayed,
- kEnqueued,
- kEnqueuedIterator,
- kFinished,
- } state_;
-
- std::weak_ptr<database::Database> db_;
-
- int queue_depth_;
-
- std::optional<database::TrackId>& current_;
- std::pmr::vector<database::TrackId>& played_;
- std::pmr::vector<TrackQueue::Item>& enqueued_;
-};
+ notifyChanged(true);
+}
-auto TrackQueue::Load(std::weak_ptr<database::Database> db) -> void {
- auto db_lock = db.lock();
- if (!db_lock) {
- return;
- }
- auto raw = db_lock->Get(kSerialiseKey);
- if (!raw) {
- return;
- }
+auto TrackQueue::clear() -> void {
+ const std::unique_lock<std::shared_mutex> lock(mutex_);
+ pos_ = 0;
+ tracks_.clear();
- Parser p{db, current_, played_, enqueued_};
- const uint8_t* data = reinterpret_cast<const uint8_t*>(raw->data());
- cppbor::parse(data, data + raw->size(), &p);
+ notifyChanged(true);
}
} // namespace audio