From aa2402ab569d98b81a1c7eb015c175f5f1bb56cb Mon Sep 17 00:00:00 2001 From: Hailey Somerville Date: Sat, 10 Feb 2024 18:36:07 +1100 Subject: [PATCH 1/5] include memory_resource in tasks.hpp --- src/tasks/tasks.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tasks/tasks.hpp b/src/tasks/tasks.hpp index 1623a8d8..47f26837 100644 --- a/src/tasks/tasks.hpp +++ b/src/tasks/tasks.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "esp_heap_caps.h" From 08c6c5d322615bf235210aa79f77e9a66a9f9093 Mon Sep 17 00:00:00 2001 From: Robin Howard Date: Sat, 10 Feb 2024 21:05:16 +1100 Subject: [PATCH 2/5] .env: quote vars, remove silly comment --- .env | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.env b/.env index ec2cdf69..e48a5fec 100644 --- a/.env +++ b/.env @@ -2,10 +2,7 @@ # # SPDX-License-Identifier: CC0-1.0 -# Load-bearing useless command, for bash. I have *no* idea why, but evaluating -# $@ is necessary for the rest to work. - -repo_dir=$(pwd) -export PROJ_PATH=$repo_dir -export IDF_PATH=$repo_dir/lib/esp-idf -. $IDF_PATH/export.sh +repo_dir="$(pwd)" +export PROJ_PATH="$repo_dir" +export IDF_PATH="$repo_dir/lib/esp-idf" +. "$IDF_PATH/export.sh" From 7d66572b0ff6c71687160e9ad684f9679f10a273 Mon Sep 17 00:00:00 2001 From: Robin Howard Date: Sat, 10 Feb 2024 16:56:55 +1100 Subject: [PATCH 3/5] Adds base dropdown-list styles. Fixes dropdown contents being painted directly on top of anything sitting below them. --- src/ui/include/themes.hpp | 1 + src/ui/themes.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/ui/include/themes.hpp b/src/ui/include/themes.hpp index 576ea42e..11680c0d 100644 --- a/src/ui/include/themes.hpp +++ b/src/ui/include/themes.hpp @@ -32,6 +32,7 @@ class Theme { lv_style_t button_style_; lv_style_t bar_style_; lv_style_t dropdown_style_; + lv_style_t dropdown_list_style_; lv_style_t slider_indicator_style_; lv_style_t slider_knob_style_; diff --git a/src/ui/themes.cpp b/src/ui/themes.cpp index bad73ee6..f8390570 100644 --- a/src/ui/themes.cpp +++ b/src/ui/themes.cpp @@ -89,6 +89,14 @@ Theme::Theme() { lv_style_set_border_color(&dropdown_style_, lv_palette_main(LV_PALETTE_BLUE)); lv_style_set_border_side(&dropdown_style_, LV_BORDER_SIDE_FULL); + lv_style_init(&dropdown_list_style_); + lv_style_set_radius(&dropdown_list_style_, 2); + lv_style_set_border_width(&dropdown_list_style_, 1); + lv_style_set_border_color(&dropdown_list_style_, lv_palette_main(LV_PALETTE_BLUE_GREY)); + lv_style_set_bg_opa(&dropdown_list_style_, LV_OPA_COVER); + lv_style_set_bg_color(&dropdown_list_style_, lv_color_white()); + lv_style_set_pad_all(&dropdown_list_style_, 2); + lv_theme_t* parent_theme = lv_disp_get_theme(NULL); theme_ = *parent_theme; theme_.user_data = this; @@ -124,6 +132,8 @@ void Theme::Callback(lv_obj_t* obj) { lv_obj_add_style(obj, &switch_knob_style_, LV_PART_KNOB); } else if (lv_obj_check_type(obj, &lv_dropdown_class)) { lv_obj_add_style(obj, &dropdown_style_, LV_PART_MAIN); + } else if (lv_obj_check_type(obj, &lv_dropdownlist_class)) { + lv_obj_add_style(obj, &dropdown_list_style_, LV_PART_MAIN); } } From 9512bd97bbac48fa33339cc248c76070063bbc61 Mon Sep 17 00:00:00 2001 From: ailurux Date: Mon, 12 Feb 2024 15:36:56 +1100 Subject: [PATCH 4/5] Add buttons for shuffle + repeat track --- ldoc-stubs/queue.lua | 4 ++ lua/img/repeat.png | Bin 0 -> 4786 bytes lua/img/repeat_disabled.png | Bin 0 -> 7287 bytes lua/img/shuffle.png | Bin 0 -> 4809 bytes lua/img/shuffle_disabled.png | Bin 0 -> 8706 bytes lua/playing.lua | 65 ++++++++++++++++++++++++------ luals-stubs/queue.lua | 1 + src/audio/audio_fsm.cpp | 2 +- src/audio/include/track_queue.hpp | 14 ++++++- src/audio/track_queue.cpp | 40 +++++++++++++----- src/ui/include/ui_fsm.hpp | 2 + src/ui/ui_fsm.cpp | 39 ++++++++++++++++-- 12 files changed, 138 insertions(+), 29 deletions(-) create mode 100644 lua/img/repeat.png create mode 100644 lua/img/repeat_disabled.png create mode 100644 lua/img/shuffle.png create mode 100644 lua/img/shuffle_disabled.png diff --git a/ldoc-stubs/queue.lua b/ldoc-stubs/queue.lua index 2d6a2a29..b3000040 100644 --- a/ldoc-stubs/queue.lua +++ b/ldoc-stubs/queue.lua @@ -15,6 +15,10 @@ queue.size = types.Property -- @see types.Property queue.replay = types.Property +-- Determines whether or not the current track will repeat indefinitely +-- @see types.Property +queue.repeat_track = types.Property + --- Determines whether, when progressing to the next track in the queue, the next track will be chosen randomly. The random selection algorithm used is a Miller Shuffle, which guarantees that no repeat selections will be made until every item in the queue has been played. -- @see types.Property queue.random = types.Property diff --git a/lua/img/repeat.png b/lua/img/repeat.png new file mode 100644 index 0000000000000000000000000000000000000000..9a4da7fdbc4838a6b244e29c29e9fa2572a59926 GIT binary patch literal 4786 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 literal 0 HcmV?d00001 diff --git a/lua/img/shuffle.png b/lua/img/shuffle.png new file mode 100644 index 0000000000000000000000000000000000000000..b54e359d2faaf46947f4d004a5e8dcd4be981984 GIT binary patch literal 4809 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 I2Tz&(Uoqhs1rRDdV#+E>SBcKz5=ALp^~ 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 Hr?CG6`9FZO literal 0 HcmV?d00001 diff --git a/lua/playing.lua b/lua/playing.lua index e31e10a2..c6a3f47e 100644 --- a/lua/playing.lua +++ b/lua/playing.lua @@ -12,6 +12,10 @@ local img = { next_disabled = "//lua/img/next_disabled.png", prev = "//lua/img/prev.png", prev_disabled = "//lua/img/prev_disabled.png", + shuffle = "//lua/img/shuffle.png", + shuffle_disabled = "//lua/img/shuffle_disabled.png", + repeat_enabled = "//lua/img/repeat.png", + repeat_disabled = "//lua/img/repeat_disabled.png", } return function(opts) @@ -69,10 +73,21 @@ return function(opts) align_items = "center", align_content = "center", }, + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, + } + + playlist:Object({ w = 3, h = 1 }) -- spacer + + local cur_time = playlist:Label { w = lvgl.SIZE_CONTENT, h = lvgl.SIZE_CONTENT, + text = "", + text_font = font.fusion_10, } + playlist:Object({ flex_grow = 1, h = 1 }) -- spacer + local playlist_pos = playlist:Label { text = "", text_font = font.fusion_10, @@ -86,6 +101,17 @@ return function(opts) text_font = font.fusion_10, } + playlist:Object({ flex_grow = 1, h = 1 }) -- spacer + + local end_time = playlist:Label { + w = lvgl.SIZE_CONTENT, + h = lvgl.SIZE_CONTENT, + align = lvgl.ALIGN.RIGHT_MID, + text = "", + text_font = font.fusion_10, + } + playlist:Object({ w = 3, h = 1 }) -- spacer + local scrubber = screen.root:Bar { w = lvgl.PCT(100), h = 5, @@ -106,15 +132,15 @@ return function(opts) pad_all = 2, } - local cur_time = controls:Label { - w = lvgl.SIZE_CONTENT, - h = lvgl.SIZE_CONTENT, - text = "", - text_font = font.fusion_10, - } controls:Object({ flex_grow = 1, h = 1 }) -- spacer + local repeat_btn = controls:Button {} + repeat_btn:onClicked(function() + queue.repeat_track:set(not queue.repeat_track:get()) + end) + local repeat_img = repeat_btn:Image { src = img.repeat_enabled } + local prev_btn = controls:Button {} prev_btn:onClicked(queue.previous) local prev_img = prev_btn:Image { src = img.prev_disabled } @@ -130,15 +156,14 @@ return function(opts) next_btn:onClicked(queue.next) local next_img = next_btn:Image { src = img.next_disabled } + local shuffle_btn = controls:Button {} + shuffle_btn:onClicked(function() + queue.random:set(not queue.random:get()) + end) + local shuffle_img = shuffle_btn:Image { src = img.shuffle } + controls:Object({ flex_grow = 1, h = 1 }) -- spacer - local end_time = controls:Label { - w = lvgl.SIZE_CONTENT, - h = lvgl.SIZE_CONTENT, - align = lvgl.ALIGN.RIGHT_MID, - text = "", - text_font = font.fusion_10, - } local format_time = function(time) return string.format("%d:%02d", time // 60, time % 60) @@ -181,6 +206,20 @@ return function(opts) pos > 1 and img.prev or img.prev_disabled ) end), + queue.random:bind(function(shuffling) + if shuffling then + shuffle_img:set_src(img.shuffle) + else + shuffle_img:set_src(img.shuffle_disabled) + end + end), + queue.repeat_track:bind(function(en) + if en then + repeat_img:set_src(img.repeat_enabled) + else + repeat_img:set_src(img.repeat_disabled) + end + end), queue.size:bind(function(num) if not num then return end playlist_total:set { text = tostring(num) } diff --git a/luals-stubs/queue.lua b/luals-stubs/queue.lua index ece99c69..08247799 100644 --- a/luals-stubs/queue.lua +++ b/luals-stubs/queue.lua @@ -5,6 +5,7 @@ --- @field position Property The index in the queue of the currently playing track. This may be zero if the queue is empty. Writeable. --- @field size Property The total number of tracks in the queue, including tracks which have already been played. --- @field replay Property Whether or not the queue will be restarted after the final track is played. Writeable. +--- @field repeat_track Property Whether or not the current track will repeat indefinitely. Writeable. --- @field random Property Determines whether, when progressing to the next track in the queue, the next track will be chosen randomly. The random selection algorithm used is a Miller Shuffle, which guarantees that no repeat selections will be made until every item in the queue has been played. Writeable. local queue = {} diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index 54ffa51d..b80e8c30 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -320,7 +320,7 @@ void Playback::react(const internal::InputFileClosed& ev) {} void Playback::react(const internal::InputFileFinished& ev) { ESP_LOGI(kTag, "finished playing file"); - sServices->track_queue().next(); + sServices->track_queue().finish(); if (!sServices->track_queue().current()) { transit(); } diff --git a/src/audio/include/track_queue.hpp b/src/audio/include/track_queue.hpp index 5b14fd4a..fd6061a7 100644 --- a/src/audio/include/track_queue.hpp +++ b/src/audio/include/track_queue.hpp @@ -33,13 +33,13 @@ class RandomIterator { // Note resizing has the side-effect of restarting iteration. auto resize(size_t) -> void; - auto repeat(bool) -> void; + auto replay(bool) -> void; private: size_t seed_; size_t pos_; size_t size_; - bool repeat_; + bool replay_; }; /* @@ -85,6 +85,12 @@ class TrackQueue { auto next() -> void; auto previous() -> void; + /* + * Called when the current track finishes + */ + auto finish() -> void; + + auto skipTo(database::TrackId) -> void; /* @@ -98,6 +104,9 @@ class TrackQueue { auto repeat(bool) -> void; auto repeat() const -> bool; + auto replay(bool) -> void; + auto replay() const -> bool; + auto serialise() -> std::string; auto deserialise(const std::string&) -> void; @@ -115,6 +124,7 @@ class TrackQueue { std::optional shuffle_; bool repeat_; + bool replay_; }; } // namespace audio diff --git a/src/audio/track_queue.cpp b/src/audio/track_queue.cpp index d68f2821..5ac9d1f8 100644 --- a/src/audio/track_queue.cpp +++ b/src/audio/track_queue.cpp @@ -34,12 +34,12 @@ namespace audio { [[maybe_unused]] static constexpr char kTag[] = "tracks"; RandomIterator::RandomIterator(size_t size) - : seed_(), pos_(0), size_(size), repeat_(false) { + : seed_(), pos_(0), size_(size), replay_(false) { esp_fill_random(&seed_, sizeof(seed_)); } auto RandomIterator::current() const -> size_t { - if (pos_ < size_ || repeat_) { + if (pos_ < size_ || replay_) { return MillerShuffle(pos_, seed_, size_); } return size_; @@ -65,8 +65,8 @@ auto RandomIterator::resize(size_t s) -> void { pos_ = 0; } -auto RandomIterator::repeat(bool r) -> void { - repeat_ = r; +auto RandomIterator::replay(bool r) -> void { + replay_ = r; } auto notifyChanged(bool current_changed) -> void { @@ -81,7 +81,8 @@ TrackQueue::TrackQueue(tasks::WorkerPool& bg_worker) pos_(0), tracks_(&memory::kSpiRamResource), shuffle_(), - repeat_(false) {} + repeat_(false), + replay_(false) {} auto TrackQueue::current() const -> std::optional { const std::shared_lock lock(mutex_); @@ -202,7 +203,7 @@ auto TrackQueue::next() -> void { pos_ = shuffle_->current(); } else { if (pos_ + 1 >= tracks_.size()) { - if (repeat_) { + if (replay_) { pos_ = 0; } } else { @@ -231,6 +232,14 @@ auto TrackQueue::previous() -> void { notifyChanged(true); } +auto TrackQueue::finish() -> void { + if (repeat_) { + notifyChanged(true); + } else { + next(); + } +} + 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 @@ -279,7 +288,7 @@ auto TrackQueue::random(bool en) -> void { // repeated calls with en == true will re-shuffle. if (en) { shuffle_.emplace(tracks_.size()); - shuffle_->repeat(repeat_); + shuffle_->replay(replay_); } else { shuffle_.reset(); } @@ -298,9 +307,6 @@ auto TrackQueue::repeat(bool en) -> void { { const std::unique_lock lock(mutex_); repeat_ = en; - if (shuffle_) { - shuffle_->repeat(en); - } } notifyChanged(false); @@ -311,6 +317,20 @@ auto TrackQueue::repeat() const -> bool { return repeat_; } +auto TrackQueue::replay(bool en) -> void { + const std::unique_lock lock(mutex_); + replay_ = en; + if (shuffle_) { + shuffle_->replay(en); + } + notifyChanged(false); +} + +auto TrackQueue::replay() const -> bool { + const std::shared_lock lock(mutex_); + return replay_; +} + auto TrackQueue::serialise() -> std::string { cppbor::Array tracks{}; for (database::TrackId track : tracks_) { diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index c097e764..52ab77a5 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -116,6 +116,7 @@ class UiState : public tinyfsm::Fsm { static lua::Property sQueuePosition; static lua::Property sQueueSize; + static lua::Property sQueueReplay; static lua::Property sQueueRepeat; static lua::Property sQueueRandom; @@ -165,6 +166,7 @@ class Lua : public UiState { auto SetPlaying(const lua::LuaValue&) -> bool; auto SetRandom(const lua::LuaValue&) -> bool; auto SetRepeat(const lua::LuaValue&) -> bool; + auto SetReplay(const lua::LuaValue&) -> bool; auto QueueNext(lua_State*) -> int; auto QueuePrevious(lua_State*) -> int; diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 12584ec7..6e4bbe6f 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -127,8 +127,30 @@ lua::Property UiState::sPlaybackPosition{0}; lua::Property UiState::sQueuePosition{0}; lua::Property UiState::sQueueSize{0}; -lua::Property UiState::sQueueRepeat{false}; -lua::Property UiState::sQueueRandom{false}; +lua::Property UiState::sQueueRepeat{false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + bool new_val = std::get(val); + sServices->track_queue().repeat(new_val); + return true; +}}; +lua::Property UiState::sQueueReplay{false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + bool new_val = std::get(val); + sServices->track_queue().replay(new_val); + return true; +}}; +lua::Property UiState::sQueueRandom{false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + bool new_val = std::get(val); + sServices->track_queue().random(new_val); + return true; +}}; lua::Property UiState::sVolumeCurrentPct{ 0, [](const lua::LuaValue& val) { @@ -296,6 +318,7 @@ void UiState::react(const audio::QueueUpdate&) { sQueuePosition.Update(current_pos); sQueueRandom.Update(queue.random()); sQueueRepeat.Update(queue.repeat()); + sQueueReplay.Update(queue.replay()); } void UiState::react(const audio::PlaybackStarted& ev) { @@ -417,7 +440,8 @@ void Lua::entry() { {"previous", [&](lua_State* s) { return QueuePrevious(s); }}, {"position", &sQueuePosition}, {"size", &sQueueSize}, - {"replay", &sQueueRepeat}, + {"replay", &sQueueReplay}, + {"repeat_track", &sQueueRepeat}, {"random", &sQueueRandom}, }); sLua->bridge().AddPropertyModule("volume", @@ -564,6 +588,15 @@ auto Lua::SetRepeat(const lua::LuaValue& val) -> bool { return true; } +auto Lua::SetReplay(const lua::LuaValue& val) -> bool { + if (!std::holds_alternative(val)) { + return false; + } + bool b = std::get(val); + sServices->track_queue().replay(b); + return true; +} + void Lua::exit() { lv_group_set_default(NULL); } From 527374c72e1ec52e1d5814dbee3587ae100631dd Mon Sep 17 00:00:00 2001 From: ailurux Date: Mon, 12 Feb 2024 16:26:37 +1100 Subject: [PATCH 5/5] Don't hold lock on notifyChanged --- src/audio/track_queue.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/audio/track_queue.cpp b/src/audio/track_queue.cpp index 5ac9d1f8..c4c101f6 100644 --- a/src/audio/track_queue.cpp +++ b/src/audio/track_queue.cpp @@ -318,10 +318,12 @@ auto TrackQueue::repeat() const -> bool { } auto TrackQueue::replay(bool en) -> void { - const std::unique_lock lock(mutex_); - replay_ = en; - if (shuffle_) { - shuffle_->replay(en); + { + const std::unique_lock lock(mutex_); + replay_ = en; + if (shuffle_) { + shuffle_->replay(en); + } } notifyChanged(false); }