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/track_queue.cpp | |
| 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/track_queue.cpp')
| -rw-r--r-- | src/audio/track_queue.cpp | 439 |
1 files changed, 96 insertions, 343 deletions
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 |
