diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-12-05 11:36:34 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-12-05 11:36:34 +1100 |
| commit | 4f5422e906b1d17720592d97bc0d5e82a71b1e5f (patch) | |
| tree | 4e8116510d57fb54c30fc18f1fe591a9aff4e3a0 /src/audio/track_queue.cpp | |
| parent | 67f2f2de8393951d2eabac26f9afab2dc9388713 (diff) | |
| download | tangara-fw-4f5422e906b1d17720592d97bc0d5e82a71b1e5f.tar.gz | |
Rewrite the track queue to work directly with database iterators
Diffstat (limited to 'src/audio/track_queue.cpp')
| -rw-r--r-- | src/audio/track_queue.cpp | 324 |
1 files changed, 162 insertions, 162 deletions
diff --git a/src/audio/track_queue.cpp b/src/audio/track_queue.cpp index c400e66a..b3a128b2 100644 --- a/src/audio/track_queue.cpp +++ b/src/audio/track_queue.cpp @@ -15,6 +15,7 @@ #include "audio_fsm.hpp" #include "database.hpp" #include "event_queue.hpp" +#include "memory_resource.hpp" #include "source.hpp" #include "track.hpp" #include "ui_fsm.hpp" @@ -23,208 +24,207 @@ namespace audio { [[maybe_unused]] static constexpr char kTag[] = "tracks"; -TrackQueue::TrackQueue() {} +TrackQueue::Editor::Editor(TrackQueue& queue) + : lock_(queue.mutex_), has_current_changed_(false) {} -auto TrackQueue::GetCurrent() const -> std::optional<database::TrackId> { - const std::lock_guard<std::mutex> lock(mutex_); - if (enqueued_.empty()) { - return {}; - } - auto item = enqueued_.front(); - if (std::holds_alternative<database::TrackId>(item)) { - return std::get<database::TrackId>(item); - } - if (std::holds_alternative<std::shared_ptr<playlist::ISource>>(item)) { - return std::get<std::shared_ptr<playlist::ISource>>(item)->Current(); - } - if (std::holds_alternative<std::shared_ptr<playlist::IResetableSource>>( - item)) { - return std::get<std::shared_ptr<playlist::IResetableSource>>(item) - ->Current(); - } - return {}; +TrackQueue::Editor::~Editor() { + QueueUpdate ev{.current_changed = has_current_changed_}; + events::Audio().Dispatch(ev); + events::Ui().Dispatch(ev); } -auto TrackQueue::GetUpcoming(std::size_t limit) const - -> std::vector<database::TrackId> { - const std::lock_guard<std::mutex> lock(mutex_); - std::vector<database::TrackId> ret; +TrackQueue::TrackQueue() + : mutex_(), + current_(), + played_(&memory::kSpiRamResource), + enqueued_(&memory::kSpiRamResource) {} - auto it = enqueued_.begin(); - if (it == enqueued_.end()) { - return ret; - } +auto TrackQueue::Edit() -> Editor { + return Editor(*this); +} - // Don't include the current track. This is only relevant to raw track ids, - // since sources include multiple tracks. - if (std::holds_alternative<database::TrackId>(*it)) { - it++; - } +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; - while (limit > 0 && it != enqueued_.end()) { - auto item = *it; - if (std::holds_alternative<database::TrackId>(item)) { - ret.push_back(std::get<database::TrackId>(item)); - limit--; - } else if (std::holds_alternative<std::shared_ptr<playlist::ISource>>( - item)) { - limit -= - std::get<std::shared_ptr<playlist::ISource>>(item)->Peek(limit, &ret); - } else if (std::holds_alternative< - std::shared_ptr<playlist::IResetableSource>>(item)) { - limit -= - std::get<std::shared_ptr<playlist::IResetableSource>>(item)->Peek( - limit, &ret); - } - it++; + 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); } return ret; } -auto TrackQueue::AddNext(database::TrackId t) -> void { - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_front(t); - - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); -} +auto TrackQueue::PeekPrevious(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); -auto TrackQueue::AddNext(std::shared_ptr<playlist::ISource> src) -> void { - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_front(src); + for (auto it = played_.rbegin(); it != played_.rend(); it++, limit--) { + ret.push_back(*it); + } - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); + return ret; } -auto TrackQueue::IncludeNext(std::shared_ptr<playlist::IResetableSource> src) - -> void { - assert(src.get() != nullptr); - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_front(src); - - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); +auto TrackQueue::GetCurrentPosition() const -> size_t { + const std::lock_guard<std::recursive_mutex> lock(mutex_); + size_t played = played_.size(); + if (current_) { + played += 1; + } + return played; } -auto TrackQueue::AddLast(database::TrackId t) -> void { - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_back(t); +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); + } - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); + return total; } -auto TrackQueue::AddLast(std::shared_ptr<playlist::ISource> src) -> void { - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_back(src); +auto TrackQueue::Insert(Editor& ed, Item i, size_t index) -> void { + if (index == 0) { + enqueued_.insert(enqueued_.begin(), i); + } - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); -} + // 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]); + } -auto TrackQueue::IncludeLast(std::shared_ptr<playlist::IResetableSource> src) - -> void { - assert(src.get() != nullptr); - const std::lock_guard<std::mutex> lock(mutex_); - enqueued_.push_back(src); + // 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; + } - QueueUpdate ev{.current_changed = enqueued_.size() < 2}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); + // Finally, we can now do the actual insertion. + enqueued_.insert(enqueued_.begin() + index, i); } -auto TrackQueue::Next() -> void { - const std::lock_guard<std::mutex> lock(mutex_); - if (enqueued_.empty()) { - return; +auto TrackQueue::Append(Editor& ed, Item i) -> void { + enqueued_.push_back(i); + if (!current_) { + Next(ed); } +} - auto item = enqueued_.front(); - if (std::holds_alternative<database::TrackId>(item)) { - played_.push_front(std::get<database::TrackId>(item)); - enqueued_.pop_front(); +auto TrackQueue::Next(Editor& ed) -> std::optional<database::TrackId> { + if (current_) { + ed.has_current_changed_ = true; + played_.push_back(*current_); } - if (std::holds_alternative<std::shared_ptr<playlist::ISource>>(item)) { - auto src = std::get<std::shared_ptr<playlist::ISource>>(item); - played_.push_front(*src->Current()); - if (!src->Advance()) { - enqueued_.pop_front(); - } - } - if (std::holds_alternative<std::shared_ptr<playlist::IResetableSource>>( - item)) { - auto src = std::get<std::shared_ptr<playlist::IResetableSource>>(item); - if (!src->Advance()) { - played_.push_back(src); - enqueued_.pop_front(); - } + 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()); } - QueueUpdate ev{.current_changed = true}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); + return current_; } -auto TrackQueue::Previous() -> void { - const std::lock_guard<std::mutex> lock(mutex_); - if (!enqueued_.empty() && - std::holds_alternative<std::shared_ptr<playlist::IResetableSource>>( - enqueued_.front())) { - auto src = std::get<std::shared_ptr<playlist::IResetableSource>>( - enqueued_.front()); - if (src->Previous()) { - QueueUpdate ev{.current_changed = false}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); - return; - } - } - +auto TrackQueue::Previous(Editor& ed) -> std::optional<database::TrackId> { if (played_.empty()) { - return; + return current_; } - - auto item = played_.front(); - if (std::holds_alternative<database::TrackId>(item)) { - enqueued_.push_front(std::get<database::TrackId>(item)); - } else if (std::holds_alternative< - std::shared_ptr<playlist::IResetableSource>>(item)) { - enqueued_.push_front( - std::get<std::shared_ptr<playlist::IResetableSource>>(item)); + ed.has_current_changed_ = true; + if (current_) { + enqueued_.insert(enqueued_.begin(), *current_); } - played_.pop_front(); - - QueueUpdate ev{.current_changed = true}; - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); + current_ = played_.back(); + played_.pop_back(); + return current_; } -auto TrackQueue::Clear() -> void { - const std::lock_guard<std::mutex> lock(mutex_); - if (enqueued_.empty() && played_.empty()) { - return; +auto TrackQueue::SkipTo(Editor& ed, database::TrackId id) -> void { + while ((!current_ || *current_ != id) && !enqueued_.empty()) { + Next(ed); } - QueueUpdate ev{.current_changed = !enqueued_.empty()}; - played_.clear(); - enqueued_.clear(); - - events::Audio().Dispatch(ev); - events::Ui().Dispatch(ev); } -auto TrackQueue::Position() -> size_t { - return played_.size() + (enqueued_.empty() ? 0 : 1); -} - -auto TrackQueue::Size() -> size_t { - return played_.size() + enqueued_.size(); +auto TrackQueue::Clear(Editor& ed) -> void { + ed.has_current_changed_ = current_.has_value(); + current_.reset(); + played_.clear(); + enqueued_.clear(); } } // namespace audio |
