diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-12-07 16:57:05 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-12-07 17:00:30 +1100 |
| commit | 3f7f199cb940c8d5f6d48f77fd59971adffe49ef (patch) | |
| tree | aa22162e46c5e9ccce4c7ee8537b493f437664d9 /src/audio | |
| parent | 009f69c929eb1d1b65d75b0937fbf3b8de5d9148 (diff) | |
| download | tangara-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.txt | 2 | ||||
| -rw-r--r-- | src/audio/audio_fsm.cpp | 40 | ||||
| -rw-r--r-- | src/audio/fatfs_audio_input.cpp | 32 | ||||
| -rw-r--r-- | src/audio/include/audio_events.hpp | 2 | ||||
| -rw-r--r-- | src/audio/include/audio_fsm.hpp | 2 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 9 | ||||
| -rw-r--r-- | src/audio/include/track_queue.hpp | 60 | ||||
| -rw-r--r-- | src/audio/track_queue.cpp | 439 |
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 |
