From 0a271d786be4cc1a1691fa38f184a091721a5251 Mon Sep 17 00:00:00 2001 From: ailurux Date: Tue, 16 Jul 2024 01:23:43 +0000 Subject: [PATCH] daniel/playlist-queue (#83) Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/83 Reviewed-by: cooljqln Co-authored-by: ailurux Co-committed-by: ailurux --- lib/fatfs/src/ffconf.h | 2 +- src/tangara/audio/audio_fsm.cpp | 20 +-- src/tangara/audio/playlist.cpp | 154 ++++++++++++++++++++++ src/tangara/audio/playlist.hpp | 56 ++++++++ src/tangara/audio/track_queue.cpp | 158 ++++++++--------------- src/tangara/audio/track_queue.hpp | 24 ++-- src/tangara/system_fsm/booting.cpp | 2 +- src/tangara/test/CMakeLists.txt | 2 +- src/tangara/test/audio/test_playlist.cpp | 86 ++++++++++++ src/tangara/ui/ui_fsm.cpp | 6 +- test/sdkconfig.test | 1 + 11 files changed, 377 insertions(+), 134 deletions(-) create mode 100644 src/tangara/audio/playlist.cpp create mode 100644 src/tangara/audio/playlist.hpp create mode 100644 src/tangara/test/audio/test_playlist.cpp diff --git a/lib/fatfs/src/ffconf.h b/lib/fatfs/src/ffconf.h index 77bd5981..a5a4ae17 100644 --- a/lib/fatfs/src/ffconf.h +++ b/lib/fatfs/src/ffconf.h @@ -58,7 +58,7 @@ /* This option switches f_forward() function. (0:Disable or 1:Enable) */ -#define FF_USE_STRFUNC 0 +#define FF_USE_STRFUNC 1 #define FF_PRINT_LLI 0 #define FF_PRINT_FLOAT 0 #define FF_STRF_ENCODE 3 diff --git a/src/tangara/audio/audio_fsm.cpp b/src/tangara/audio/audio_fsm.cpp index fbc38f97..65261d75 100644 --- a/src/tangara/audio/audio_fsm.cpp +++ b/src/tangara/audio/audio_fsm.cpp @@ -96,9 +96,7 @@ void AudioState::react(const QueueUpdate& ev) { }; auto current = sServices->track_queue().current(); - if (current) { - cmd.new_track = *current; - } + cmd.new_track = current; switch (ev.reason) { case QueueUpdate::kExplicitUpdate: @@ -176,18 +174,21 @@ void AudioState::react(const internal::DecodingFinished& ev) { sServices->bg_worker().Dispatch([=]() { auto& queue = sServices->track_queue(); auto current = queue.current(); - if (!current) { + if (std::holds_alternative(current)) { return; } auto db = sServices->database().lock(); if (!db) { return; } - auto path = db->getTrackPath(*current); - if (!path) { - return; + std::string path; + if (std::holds_alternative(current)) { + path = std::get(current); + } else if (std::holds_alternative(current)) { + auto tid = std::get(current); + path = db->getTrackPath(tid).value_or(""); } - if (*path == ev.track->uri) { + if (path == ev.track->uri) { queue.finish(); } }); @@ -449,6 +450,9 @@ void Standby::react(const system_fsm::SdStateChanged& ev) { return; } + // Open the queue file + sServices->track_queue().open(); + // Restore the currently playing file before restoring the queue. This way, // we can fall back to restarting the queue's current track if there's any // issue restoring the current file. diff --git a/src/tangara/audio/playlist.cpp b/src/tangara/audio/playlist.cpp new file mode 100644 index 00000000..850b7335 --- /dev/null +++ b/src/tangara/audio/playlist.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2024 ailurux + * + * SPDX-License-Identifier: GPL-3.0-only + */ +#include "playlist.hpp" + +#include + +#include "audio/playlist.hpp" +#include "database/database.hpp" +#include "esp_log.h" +#include "ff.h" + +namespace audio { +[[maybe_unused]] static constexpr char kTag[] = "playlist"; + +Playlist::Playlist(std::string playlistFilepath) + : filepath_(playlistFilepath), mutex_(), total_size_(0), pos_(0) {} + +auto Playlist::open() -> bool { + 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; + } + // Count all entries + consumeAndCount(-1); + // Grab the first one + skipTo(0); + return true; +} + +Playlist::~Playlist() { + f_close(&file_); +} + +auto Playlist::currentPosition() const -> size_t { + return pos_; +} + +auto Playlist::size() const -> size_t { + return total_size_; +} + +auto Playlist::append(Item i) -> void { + std::unique_lock lock(mutex_); + auto offset = f_tell(&file_); + // Seek to end and append + auto res = f_lseek(&file_, f_size(&file_)); + if (res != FR_OK) { + ESP_LOGE(kTag, "Seek to end of file failed? Error %d", res); + return; + } + // TODO: Resolve paths for track id, etc + std::string path; + if (std::holds_alternative(i)) { + path = std::get(i); + f_printf(&file_, "%s\n", path.c_str()); + total_size_++; + if (current_value_.empty()) { + current_value_ = path; + } + } + // Restore position + res = f_lseek(&file_, offset); + if (res != FR_OK) { + ESP_LOGE(kTag, "Failed to restore file position after append?"); + return; + } + res = f_sync(&file_); + if (res != FR_OK) { + ESP_LOGE(kTag, "Failed to sync playlist file after append"); + return; + } +} + +auto Playlist::skipTo(size_t position) -> void { + pos_ = position; + consumeAndCount(position); +} + +auto Playlist::next() -> void { + if (!atEnd()) { + pos_++; + skipTo(pos_); + } +} + +auto Playlist::prev() -> void { + // Naive approach to see how that goes for now + pos_--; + skipTo(pos_); +} + +auto Playlist::value() const -> std::string { + return current_value_; +} + +auto Playlist::clear() -> bool { + auto res = f_close(&file_); + if (res != FR_OK) { + return false; + } + res = + f_open(&file_, filepath_.c_str(), FA_READ | FA_WRITE | FA_CREATE_ALWAYS); + if (res != FR_OK) { + return false; + } + total_size_ = 0; + current_value_.clear(); + pos_ = 0; + return true; +} + +auto Playlist::atEnd() -> bool { + return pos_ + 1 >= total_size_; +} + +auto Playlist::filepath() -> std::string { + return filepath_; +} + +auto Playlist::consumeAndCount(ssize_t upto) -> bool { + std::unique_lock lock(mutex_); + TCHAR buff[512]; + size_t count = 0; + f_rewind(&file_); + while (!f_eof(&file_)) { + // TODO: Correctly handle lines longer than this + // TODO: Also correctly handle the case where the last entry doesn't end in + // \n + auto res = f_gets(buff, 512, &file_); + if (res == NULL) { + ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); + return false; + } + count++; + + if (upto >= 0 && count > upto) { + size_t len = strlen(buff); + current_value_.assign(buff, len - 1); + break; + } + } + if (upto < 0) { + total_size_ = count; + f_rewind(&file_); + } + return true; +} + +} // namespace audio \ No newline at end of file diff --git a/src/tangara/audio/playlist.hpp b/src/tangara/audio/playlist.hpp new file mode 100644 index 00000000..b278d8a6 --- /dev/null +++ b/src/tangara/audio/playlist.hpp @@ -0,0 +1,56 @@ + +/* + * Copyright 2024 ailurux + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once +#include +#include +#include "database/database.hpp" +#include "database/track.hpp" +#include "ff.h" + +namespace audio { + +/* + * Owns and manages a playlist file. + * Each line in the playlist file is the absolute filepath of the track to play. + * In order to avoid mapping to byte offsets, each line must contain only a + * filepath (ie, no comments are supported). This limitation may be removed + * later if benchmarks show that the file can be quickly scanned from 'bookmark' + * offsets. This is a subset of the m3u format and ideally will be + * import/exportable to and from this format, to better support playlists from + * beets import and other music management software. + */ +class Playlist { + public: + Playlist(std::string playlistFilepath); + ~Playlist(); + using Item = + std::variant; + auto open() -> bool; + auto currentPosition() const -> size_t; + auto size() const -> size_t; + auto append(Item i) -> void; + auto skipTo(size_t position) -> void; + auto next() -> void; + auto prev() -> void; + auto value() const -> std::string; + auto clear() -> bool; + auto atEnd() -> bool; + auto filepath() -> std::string; + + private: + std::string filepath_; + std::mutex mutex_; + size_t total_size_; + size_t pos_; + FIL file_; + std::string current_value_; + + auto consumeAndCount(ssize_t upto) -> bool; +}; + +} // namespace audio \ No newline at end of file diff --git a/src/tangara/audio/track_queue.cpp b/src/tangara/audio/track_queue.cpp index 603b0de1..1689f06a 100644 --- a/src/tangara/audio/track_queue.cpp +++ b/src/tangara/audio/track_queue.cpp @@ -28,6 +28,7 @@ #include "memory_resource.hpp" #include "tasks.hpp" #include "ui/ui_fsm.hpp" +#include "track_queue.hpp" namespace audio { @@ -83,65 +84,67 @@ auto notifyChanged(bool current_changed, Reason reason) -> void { events::Audio().Dispatch(ev); } -TrackQueue::TrackQueue(tasks::WorkerPool& bg_worker) +TrackQueue::TrackQueue(tasks::WorkerPool& bg_worker, database::Handle db) : mutex_(), bg_worker_(bg_worker), - pos_(0), - tracks_(&memory::kSpiRamResource), + db_(db), + playlist_("queue.playlist"), // TODO shuffle_(), repeat_(false), replay_(false) {} -auto TrackQueue::current() const -> std::optional { +auto TrackQueue::current() const -> TrackItem { const std::shared_lock lock(mutex_); - if (pos_ >= tracks_.size()) { + std::string val = playlist_.value(); + if (val.empty()) { return {}; } - return tracks_[pos_]; + return val; } -auto TrackQueue::peekNext(std::size_t limit) const - -> std::vector { +auto TrackQueue::currentPosition() const -> size_t { const std::shared_lock lock(mutex_); - std::vector out; - for (size_t i = pos_ + 1; i < pos_ + limit + 1 && i < tracks_.size(); i++) { - out.push_back(i); - } - return out; + return playlist_.currentPosition(); } -auto TrackQueue::peekPrevious(std::size_t limit) const - -> std::vector { +auto TrackQueue::totalSize() const -> size_t { const std::shared_lock lock(mutex_); - std::vector out; - for (size_t i = pos_ - 1; i < pos_ - limit - 1 && i >= tracks_.size(); i--) { - out.push_back(i); - } - return out; + return playlist_.size(); } -auto TrackQueue::currentPosition() const -> size_t { - const std::shared_lock lock(mutex_); - return pos_; +auto TrackQueue::open() -> bool { + // FIX ME: If playlist opening fails, should probably fall back to a vector of tracks or something + // so that we're not necessarily always needing mounted storage + return playlist_.open(); } -auto TrackQueue::totalSize() const -> size_t { - const std::shared_lock lock(mutex_); - return tracks_.size(); +auto TrackQueue::getFilepath(database::TrackId id) -> std::optional { + auto db = db_.lock(); + if (!db) { + return {}; + } + return db->getTrackPath(id); } + +// TODO WIP: Atm only appends are allowed, this will only ever append regardless of what index +// is given. But it is kept like this for compatability for now. auto TrackQueue::insert(Item i, size_t index) -> void { + append(i); +} + +auto TrackQueue::append(Item i) -> void { bool was_queue_empty; bool current_changed; { const std::shared_lock lock(mutex_); - was_queue_empty = pos_ == tracks_.size(); - current_changed = was_queue_empty || index == pos_; + was_queue_empty = playlist_.currentPosition() >= playlist_.size(); + current_changed = was_queue_empty; // Dont support inserts yet } auto update_shuffler = [=, this]() { if (shuffle_) { - shuffle_->resize(tracks_.size()); + shuffle_->resize(playlist_.size()); // If there wasn't anything already playing, then we should make sure we // begin playback at a random point, instead of always starting with // whatever was inserted first and *then* shuffling. @@ -149,7 +152,7 @@ auto TrackQueue::insert(Item i, size_t index) -> void { // 'play this track now' (by inserting at the current pos) to work even // when shuffling is enabled. if (was_queue_empty) { - pos_ = shuffle_->current(); + playlist_.skipTo(shuffle_->current()); } } }; @@ -157,10 +160,11 @@ auto TrackQueue::insert(Item i, size_t index) -> void { if (std::holds_alternative(i)) { { const std::unique_lock lock(mutex_); - if (index <= tracks_.size()) { - tracks_.insert(tracks_.begin() + index, std::get(i)); - update_shuffler(); + auto filename = getFilepath(std::get(i)); + if (filename) { + playlist_.append(*filename); } + update_shuffler(); } notifyChanged(current_changed, Reason::kExplicitUpdate); } else if (std::holds_alternative(i)) { @@ -169,7 +173,6 @@ auto TrackQueue::insert(Item i, size_t index) -> void { // doesn't block. bg_worker_.Dispatch([=, this]() { database::TrackIterator it = std::get(i); - size_t working_pos = index; while (true) { auto next = *it; if (!next) { @@ -179,11 +182,11 @@ auto TrackQueue::insert(Item i, size_t index) -> void { // like current(). { const std::unique_lock lock(mutex_); - if (working_pos <= tracks_.size()) { - tracks_.insert(tracks_.begin() + working_pos, *next); + auto filename = *getFilepath(*next); + if (!filename.empty()) { + playlist_.append(filename); } } - working_pos++; it++; } { @@ -195,15 +198,6 @@ auto TrackQueue::insert(Item i, size_t index) -> void { } } -auto TrackQueue::append(Item i) -> void { - size_t end; - { - const std::shared_lock lock(mutex_); - end = tracks_.size(); - } - insert(i, end); -} - auto TrackQueue::next() -> void { next(Reason::kExplicitUpdate); } @@ -215,17 +209,16 @@ auto TrackQueue::next(Reason r) -> void { const std::unique_lock lock(mutex_); if (shuffle_) { shuffle_->next(); - pos_ = shuffle_->current(); + playlist_.skipTo(shuffle_->current()); } else { - if (pos_ + 1 >= tracks_.size()) { + if (playlist_.atEnd()) { if (replay_) { - pos_ = 0; + playlist_.skipTo(0); } else { - pos_ = tracks_.size(); changed = false; } } else { - pos_++; + playlist_.next(); } } } @@ -240,16 +233,16 @@ auto TrackQueue::previous() -> void { const std::unique_lock lock(mutex_); if (shuffle_) { shuffle_->prev(); - pos_ = shuffle_->current(); + playlist_.skipTo(shuffle_->current()); } else { - if (pos_ == 0) { + if (playlist_.currentPosition() == 0) { if (repeat_) { - pos_ = tracks_.size() - 1; + playlist_.skipTo(playlist_.size()-1); } else { changed = false; } } else { - pos_--; + playlist_.prev(); } } } @@ -265,39 +258,10 @@ auto TrackQueue::finish() -> void { } } -auto TrackQueue::skipTo(database::TrackId id) -> void { - // Defer this work to the background not because it's particularly - // long-running (although it could be), but because we want to ensure we - // only search for the given id after any previously pending iterator - // insertions have finished. - bg_worker_.Dispatch([=, this]() { - bool found = false; - { - const std::unique_lock lock(mutex_); - for (size_t i = 0; i < tracks_.size(); i++) { - if (tracks_[i] == id) { - pos_ = i; - found = true; - break; - } - } - } - if (found) { - notifyChanged(true, Reason::kExplicitUpdate); - } - }); -} - auto TrackQueue::clear() -> void { { const std::unique_lock lock(mutex_); - if (tracks_.empty()) { - return; - } - - pos_ = 0; - tracks_.clear(); - + playlist_.clear(); if (shuffle_) { shuffle_->resize(0); } @@ -309,10 +273,8 @@ auto TrackQueue::clear() -> void { auto TrackQueue::random(bool en) -> void { { const std::unique_lock lock(mutex_); - // Don't check for en == true already; this has the side effect that - // repeated calls with en == true will re-shuffle. if (en) { - shuffle_.emplace(tracks_.size()); + shuffle_.emplace(playlist_.size()); shuffle_->replay(replay_); } else { shuffle_.reset(); @@ -360,14 +322,11 @@ auto TrackQueue::replay() const -> bool { auto TrackQueue::serialise() -> std::string { cppbor::Array tracks{}; - for (database::TrackId track : tracks_) { - tracks.add(cppbor::Uint(track)); - } cppbor::Map encoded; encoded.add(cppbor::Uint{0}, cppbor::Array{ - cppbor::Uint{pos_}, cppbor::Bool{repeat_}, cppbor::Bool{replay_}, + cppbor::Uint{playlist_.currentPosition()}, }); if (shuffle_) { encoded.add(cppbor::Uint{1}, cppbor::Array{ @@ -376,7 +335,6 @@ auto TrackQueue::serialise() -> std::string { cppbor::Uint{shuffle_->pos()}, }); } - encoded.add(cppbor::Uint{2}, std::move(tracks)); return encoded.toString(); } @@ -401,9 +359,6 @@ cppbor::ParseClient* TrackQueue::QueueParseClient::item( case 1: state_ = State::kShuffle; break; - case 2: - state_ = State::kTracks; - break; default: state_ = State::kFinished; } @@ -412,7 +367,8 @@ cppbor::ParseClient* TrackQueue::QueueParseClient::item( if (item->type() == cppbor::ARRAY) { i_ = 0; } else if (item->type() == cppbor::UINT) { - queue_.pos_ = item->asUint()->unsignedValue(); + auto val = item->asUint()->unsignedValue(); + queue_.playlist_.skipTo(val); } else if (item->type() == cppbor::SIMPLE) { bool val = item->asBool()->value(); if (i_ == 0) { @@ -444,10 +400,6 @@ cppbor::ParseClient* TrackQueue::QueueParseClient::item( } i_++; } - } else if (state_ == State::kTracks) { - if (item->type() == cppbor::UINT) { - queue_.tracks_.push_back(item->asUint()->unsignedValue()); - } } else if (state_ == State::kFinished) { } return this; @@ -470,10 +422,6 @@ cppbor::ParseClient* TrackQueue::QueueParseClient::itemEnd( if (item->type() == cppbor::ARRAY) { state_ = State::kRoot; } - } else if (state_ == State::kTracks) { - if (item->type() == cppbor::ARRAY) { - state_ = State::kRoot; - } } else if (state_ == State::kFinished) { } return this; diff --git a/src/tangara/audio/track_queue.hpp b/src/tangara/audio/track_queue.hpp index 427d5f75..6f50f162 100644 --- a/src/tangara/audio/track_queue.hpp +++ b/src/tangara/audio/track_queue.hpp @@ -17,6 +17,7 @@ #include "database/database.hpp" #include "database/track.hpp" #include "tasks.hpp" +#include "playlist.hpp" namespace audio { @@ -64,22 +65,15 @@ class RandomIterator { */ class TrackQueue { public: - TrackQueue(tasks::WorkerPool& bg_worker); + TrackQueue(tasks::WorkerPool& bg_worker, database::Handle db); /* Returns the currently playing track. */ - auto current() const -> std::optional; - - /* Returns, in order, tracks that have been queued to be played next. */ - auto peekNext(std::size_t limit) const -> std::vector; - - /* - * 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; + using TrackItem = std::variant; + auto current() const -> TrackItem; auto currentPosition() const -> size_t; auto totalSize() const -> size_t; + auto open() -> bool; using Item = std::variant; auto insert(Item, size_t index = 0) -> void; @@ -97,8 +91,6 @@ class TrackQueue { */ auto finish() -> void; - auto skipTo(database::TrackId) -> void; - /* * Removes all tracks from all queues, and stops any currently playing track. */ @@ -122,13 +114,14 @@ class TrackQueue { private: auto next(QueueUpdate::Reason r) -> void; + auto getFilepath(database::TrackId id) -> std::optional; mutable std::shared_mutex mutex_; tasks::WorkerPool& bg_worker_; + database::Handle db_; - size_t pos_; - std::pmr::vector tracks_; + Playlist playlist_; std::optional shuffle_; bool repeat_; @@ -159,7 +152,6 @@ class TrackQueue { kRoot, kMetadata, kShuffle, - kTracks, kFinished, }; State state_; diff --git a/src/tangara/system_fsm/booting.cpp b/src/tangara/system_fsm/booting.cpp index 86993767..a3fed9fa 100644 --- a/src/tangara/system_fsm/booting.cpp +++ b/src/tangara/system_fsm/booting.cpp @@ -97,7 +97,7 @@ auto Booting::entry() -> void { sServices->samd(), std::unique_ptr(adc))); sServices->track_queue( - std::make_unique(sServices->bg_worker())); + std::make_unique(sServices->bg_worker(), sServices->database())); sServices->tag_parser(std::make_unique()); sServices->collator(locale::CreateCollator()); sServices->tts(std::make_unique()); diff --git a/src/tangara/test/CMakeLists.txt b/src/tangara/test/CMakeLists.txt index 728c06b0..58882f9f 100644 --- a/src/tangara/test/CMakeLists.txt +++ b/src/tangara/test/CMakeLists.txt @@ -3,5 +3,5 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRC_DIRS "battery" + SRC_DIRS "battery" "audio" INCLUDE_DIRS "." REQUIRES catch2 cmock tangara fixtures) diff --git a/src/tangara/test/audio/test_playlist.cpp b/src/tangara/test/audio/test_playlist.cpp new file mode 100644 index 00000000..147b3ac0 --- /dev/null +++ b/src/tangara/test/audio/test_playlist.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2023 ailurux + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "audio/playlist.hpp" + +#include + +#include +#include +#include + +#include "catch2/catch.hpp" + +#include "drivers/gpios.hpp" +#include "drivers/i2c.hpp" +#include "drivers/storage.hpp" +#include "drivers/spi.hpp" +#include "i2c_fixture.hpp" +#include "spi_fixture.hpp" +#include "ff.h" + +namespace audio { + +static const std::string kTestFilename = "test_playlist2.m3u"; +static const std::string kTestFilePath = kTestFilename; + +TEST_CASE("playlist file", "[integration]") { + I2CFixture i2c; + SpiFixture spi; + std::unique_ptr gpios{drivers::Gpios::Create(false)}; + + if (gpios->Get(drivers::IGpios::Pin::kSdCardDetect)) { + // Skip if nothing is inserted. + SKIP("no sd card detected; skipping storage tests"); + return; + } + + { + std::unique_ptr result(drivers::SdStorage::Create(*gpios).value()); + Playlist plist(kTestFilePath); + REQUIRE(plist.clear()); + + SECTION("write to the playlist file") { + plist.append("test1.mp3"); + plist.append("test2.mp3"); + plist.append("test3.mp3"); + plist.append("test4.wav"); + plist.append("directory/test1.mp3"); + plist.append("directory/test2.mp3"); + plist.append("a/really/long/directory/test1.mp3"); + plist.append("directory/and/another/test2.mp3"); + REQUIRE(plist.size() == 8); + + SECTION("read from the playlist file") { + Playlist plist2(kTestFilePath); + REQUIRE(plist2.size() == 8); + REQUIRE(plist2.value() == "test1.mp3"); + plist2.next(); + REQUIRE(plist2.value() == "test2.mp3"); + plist2.prev(); + REQUIRE(plist2.value() == "test1.mp3"); + } + } + + BENCHMARK("appending item") { + plist.append("A/New/Item.wav"); + }; + + BENCHMARK("opening playlist file") { + Playlist plist2(kTestFilePath); + REQUIRE(plist2.size() > 100); + return plist2.size(); + }; + + BENCHMARK("opening playlist file and appending entry") { + Playlist plist2(kTestFilePath); + REQUIRE(plist2.size() > 100); + plist2.append("A/Nother/New/Item.opus"); + return plist2.size(); + }; + } +} +} // namespace audio diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp index c5ede83c..476732db 100644 --- a/src/tangara/ui/ui_fsm.cpp +++ b/src/tangara/ui/ui_fsm.cpp @@ -398,10 +398,12 @@ void UiState::react(const system_fsm::BatteryStateChanged& ev) { void UiState::react(const audio::QueueUpdate&) { auto& queue = sServices->track_queue(); - sQueueSize.setDirect(static_cast(queue.totalSize())); + auto queue_size = queue.totalSize(); + sQueueSize.setDirect(static_cast(queue_size)); int current_pos = queue.currentPosition(); - if (queue.current()) { + // If there is nothing in the queue, the position should be 0, otherwise, add one because lua + if (queue_size > 0) { current_pos++; } sQueuePosition.setDirect(current_pos); diff --git a/test/sdkconfig.test b/test/sdkconfig.test index c0fbf922..10d9595c 100644 --- a/test/sdkconfig.test +++ b/test/sdkconfig.test @@ -11,3 +11,4 @@ CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y CONFIG_COMPILER_STACK_CHECK=y CONFIG_ESP_TASK_WDT=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="../partitions.csv" +CONFIG_ESP_TASK_WDT_EN=n