Merge branch 'main' of codeberg.org:cool-tech-zone/tangara-fw

custom
jacqueline 1 year ago
commit 36f4c77fb2
  1. 11
      .env
  2. 4
      ldoc-stubs/queue.lua
  3. BIN
      lua/img/repeat.png
  4. BIN
      lua/img/repeat_disabled.png
  5. BIN
      lua/img/shuffle.png
  6. BIN
      lua/img/shuffle_disabled.png
  7. 65
      lua/playing.lua
  8. 1
      luals-stubs/queue.lua
  9. 2
      src/audio/audio_fsm.cpp
  10. 14
      src/audio/include/track_queue.hpp
  11. 42
      src/audio/track_queue.cpp
  12. 1
      src/tasks/tasks.hpp
  13. 1
      src/ui/include/themes.hpp
  14. 2
      src/ui/include/ui_fsm.hpp
  15. 10
      src/ui/themes.cpp
  16. 39
      src/ui/ui_fsm.cpp

11
.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"

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

@ -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) }

@ -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 = {}

@ -357,7 +357,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<Standby>();
}

@ -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<RandomIterator> shuffle_;
bool repeat_;
bool replay_;
};
} // namespace audio

@ -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<database::TrackId> {
const std::shared_lock<std::shared_mutex> 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<std::shared_mutex> lock(mutex_);
repeat_ = en;
if (shuffle_) {
shuffle_->repeat(en);
}
}
notifyChanged(false);
@ -311,6 +317,22 @@ auto TrackQueue::repeat() const -> bool {
return repeat_;
}
auto TrackQueue::replay(bool en) -> void {
{
const std::unique_lock<std::shared_mutex> lock(mutex_);
replay_ = en;
if (shuffle_) {
shuffle_->replay(en);
}
}
notifyChanged(false);
}
auto TrackQueue::replay() const -> bool {
const std::shared_lock<std::shared_mutex> lock(mutex_);
return replay_;
}
auto TrackQueue::serialise() -> std::string {
cppbor::Array tracks{};
for (database::TrackId track : tracks_) {

@ -10,6 +10,7 @@
#include <functional>
#include <future>
#include <memory>
#include <memory_resource>
#include <string>
#include "esp_heap_caps.h"

@ -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_;

@ -116,6 +116,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
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;

@ -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);
}
}

@ -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<bool>(val)) {
return false;
}
bool new_val = std::get<bool>(val);
sServices->track_queue().repeat(new_val);
return true;
}};
lua::Property UiState::sQueueReplay{false, [](const lua::LuaValue& val) {
if (!std::holds_alternative<bool>(val)) {
return false;
}
bool new_val = std::get<bool>(val);
sServices->track_queue().replay(new_val);
return true;
}};
lua::Property UiState::sQueueRandom{false, [](const lua::LuaValue& val) {
if (!std::holds_alternative<bool>(val)) {
return false;
}
bool new_val = std::get<bool>(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) {
@ -423,7 +446,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",
@ -567,6 +591,15 @@ auto Lua::SetRepeat(const lua::LuaValue& val) -> bool {
return true;
}
auto Lua::SetReplay(const lua::LuaValue& val) -> bool {
if (!std::holds_alternative<bool>(val)) {
return false;
}
bool b = std::get<bool>(val);
sServices->track_queue().replay(b);
return true;
}
void Lua::exit() {
lv_group_set_default(NULL);
}

Loading…
Cancel
Save