Move UI task to priority 0 during playback
Also other misc task cleanup
This commit is contained in:
@@ -23,8 +23,9 @@
|
|||||||
|
|
||||||
static constexpr char kTag[] = "mixer";
|
static constexpr char kTag[] = "mixer";
|
||||||
|
|
||||||
static constexpr std::size_t kSourceBufferLength = 8 * 1024;
|
static constexpr std::size_t kSampleBufferLength = 240 * 2 * 8;
|
||||||
static constexpr std::size_t kSampleBufferLength = 240 * 2 * 4;
|
static constexpr std::size_t kSourceBufferLength =
|
||||||
|
kSampleBufferLength * 2 * sizeof(sample::Sample);
|
||||||
|
|
||||||
namespace audio {
|
namespace audio {
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ SampleConverter::SampleConverter()
|
|||||||
kSampleBufferLength, sizeof(sample::Sample), MALLOC_CAP_SPIRAM)),
|
kSampleBufferLength, sizeof(sample::Sample), MALLOC_CAP_SPIRAM)),
|
||||||
kSampleBufferLength};
|
kSampleBufferLength};
|
||||||
|
|
||||||
tasks::StartPersistent<tasks::Type::kMixer>([&]() { Main(); });
|
tasks::StartPersistent<tasks::Type::kAudioConverter>([&]() { Main(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleConverter::~SampleConverter() {
|
SampleConverter::~SampleConverter() {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace audio {
|
|||||||
|
|
||||||
static const char* kTag = "audio_dec";
|
static const char* kTag = "audio_dec";
|
||||||
|
|
||||||
static constexpr std::size_t kCodecBufferLength = 240 * 4 * 4;
|
static constexpr std::size_t kCodecBufferLength = 240 * 4 * 16;
|
||||||
|
|
||||||
Timer::Timer(const codecs::ICodec::OutputFormat& format)
|
Timer::Timer(const codecs::ICodec::OutputFormat& format)
|
||||||
: current_seconds_(0),
|
: current_seconds_(0),
|
||||||
@@ -79,7 +79,8 @@ auto Timer::AddSamples(std::size_t samples) -> void {
|
|||||||
auto Decoder::Start(std::shared_ptr<IAudioSource> source,
|
auto Decoder::Start(std::shared_ptr<IAudioSource> source,
|
||||||
std::shared_ptr<SampleConverter> sink) -> Decoder* {
|
std::shared_ptr<SampleConverter> sink) -> Decoder* {
|
||||||
Decoder* task = new Decoder(source, sink);
|
Decoder* task = new Decoder(source, sink);
|
||||||
tasks::StartPersistent<tasks::Type::kAudio>(1, [=]() { task->Main(); });
|
tasks::StartPersistent<tasks::Type::kAudioDecoder>(1,
|
||||||
|
[=]() { task->Main(); });
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,9 @@ void Standby::react(const TogglePlayPause& ev) {
|
|||||||
void Playback::entry() {
|
void Playback::entry() {
|
||||||
ESP_LOGI(kTag, "beginning playback");
|
ESP_LOGI(kTag, "beginning playback");
|
||||||
sOutput->SetInUse(true);
|
sOutput->SetInUse(true);
|
||||||
|
|
||||||
|
events::System().Dispatch(PlaybackStarted{});
|
||||||
|
events::Ui().Dispatch(PlaybackStarted{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playback::exit() {
|
void Playback::exit() {
|
||||||
@@ -153,6 +156,7 @@ void Playback::exit() {
|
|||||||
sOutput->SetInUse(false);
|
sOutput->SetInUse(false);
|
||||||
|
|
||||||
events::System().Dispatch(PlaybackFinished{});
|
events::System().Dispatch(PlaybackFinished{});
|
||||||
|
events::Ui().Dispatch(PlaybackFinished{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playback::react(const QueueUpdate& ev) {
|
void Playback::react(const QueueUpdate& ev) {
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
|
|
||||||
namespace audio {
|
namespace audio {
|
||||||
|
|
||||||
struct PlaybackStarted : tinyfsm::Event {
|
struct PlaybackStarted : tinyfsm::Event {};
|
||||||
database::Track track;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlaybackUpdate : tinyfsm::Event {
|
struct PlaybackUpdate : tinyfsm::Event {
|
||||||
uint32_t seconds_elapsed;
|
uint32_t seconds_elapsed;
|
||||||
|
|||||||
+7
-45
@@ -60,19 +60,6 @@ static const int kDisplayBufferSize = (kDisplayWidth * kDisplayHeight) / 10;
|
|||||||
DMA_ATTR static lv_color_t sBuffer1[kDisplayBufferSize];
|
DMA_ATTR static lv_color_t sBuffer1[kDisplayBufferSize];
|
||||||
DMA_ATTR static lv_color_t sBuffer2[kDisplayBufferSize];
|
DMA_ATTR static lv_color_t sBuffer2[kDisplayBufferSize];
|
||||||
|
|
||||||
struct RenderTaskArgs {
|
|
||||||
std::atomic<bool>* quit;
|
|
||||||
QueueHandle_t work_queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FlushArgs {
|
|
||||||
lv_disp_drv_t* driver;
|
|
||||||
const lv_area_t* area;
|
|
||||||
lv_color_t* color_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
void RenderMain(void* raw_args);
|
|
||||||
|
|
||||||
namespace drivers {
|
namespace drivers {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -81,8 +68,10 @@ namespace drivers {
|
|||||||
extern "C" void FlushDataCallback(lv_disp_drv_t* disp_drv,
|
extern "C" void FlushDataCallback(lv_disp_drv_t* disp_drv,
|
||||||
const lv_area_t* area,
|
const lv_area_t* area,
|
||||||
lv_color_t* color_map) {
|
lv_color_t* color_map) {
|
||||||
|
taskYIELD();
|
||||||
Display* instance = static_cast<Display*>(disp_drv->user_data);
|
Display* instance = static_cast<Display*>(disp_drv->user_data);
|
||||||
instance->OnLvglFlush(disp_drv, area, color_map);
|
instance->OnLvglFlush(disp_drv, area, color_map);
|
||||||
|
taskYIELD();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Display::Create(IGpios& expander,
|
auto Display::Create(IGpios& expander,
|
||||||
@@ -155,7 +144,6 @@ auto Display::Create(IGpios& expander,
|
|||||||
auto display = std::make_unique<Display>(expander, handle);
|
auto display = std::make_unique<Display>(expander, handle);
|
||||||
|
|
||||||
// Now we reset the display into a known state, then configure it
|
// Now we reset the display into a known state, then configure it
|
||||||
// TODO(jacqueline): set rotation
|
|
||||||
ESP_LOGI(kTag, "Sending init sequences");
|
ESP_LOGI(kTag, "Sending init sequences");
|
||||||
for (int i = 0; i < init_data.num_sequences; i++) {
|
for (int i = 0; i < init_data.num_sequences; i++) {
|
||||||
display->SendInitialisationSequence(init_data.sequences[i]);
|
display->SendInitialisationSequence(init_data.sequences[i]);
|
||||||
@@ -183,13 +171,7 @@ auto Display::Create(IGpios& expander,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Display::Display(IGpios& gpio, spi_device_handle_t handle)
|
Display::Display(IGpios& gpio, spi_device_handle_t handle)
|
||||||
: gpio_(gpio),
|
: gpio_(gpio), handle_(handle), display_on_(false), brightness_(0) {}
|
||||||
handle_(handle),
|
|
||||||
worker_task_(tasks::Worker::Start<tasks::Type::kUiFlush>()),
|
|
||||||
display_on_(false),
|
|
||||||
brightness_(0) {
|
|
||||||
SetBrightness(50);
|
|
||||||
}
|
|
||||||
|
|
||||||
Display::~Display() {
|
Display::~Display() {
|
||||||
ledc_fade_func_uninstall();
|
ledc_fade_func_uninstall();
|
||||||
@@ -303,10 +285,6 @@ void Display::SendTransaction(TransactionType type,
|
|||||||
void Display::OnLvglFlush(lv_disp_drv_t* disp_drv,
|
void Display::OnLvglFlush(lv_disp_drv_t* disp_drv,
|
||||||
const lv_area_t* area,
|
const lv_area_t* area,
|
||||||
lv_color_t* color_map) {
|
lv_color_t* color_map) {
|
||||||
// area is stack-allocated, so it isn't safe to reference from the flush
|
|
||||||
// thread.
|
|
||||||
lv_area_t area_copy = *area;
|
|
||||||
// worker_task_->Dispatch<void>([=, this]() {
|
|
||||||
// Ideally we want to complete a single flush as quickly as possible, so
|
// Ideally we want to complete a single flush as quickly as possible, so
|
||||||
// grab the bus for this entire transaction sequence.
|
// grab the bus for this entire transaction sequence.
|
||||||
spi_device_acquire_bus(handle_, portMAX_DELAY);
|
spi_device_acquire_bus(handle_, portMAX_DELAY);
|
||||||
@@ -314,13 +292,13 @@ void Display::OnLvglFlush(lv_disp_drv_t* disp_drv,
|
|||||||
// First we need to specify the rectangle of the display we're writing into.
|
// First we need to specify the rectangle of the display we're writing into.
|
||||||
uint16_t data[2] = {0, 0};
|
uint16_t data[2] = {0, 0};
|
||||||
|
|
||||||
data[0] = SPI_SWAP_DATA_TX(area_copy.x1, 16);
|
data[0] = SPI_SWAP_DATA_TX(area->x1, 16);
|
||||||
data[1] = SPI_SWAP_DATA_TX(area_copy.x2, 16);
|
data[1] = SPI_SWAP_DATA_TX(area->x2, 16);
|
||||||
SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast<uint8_t*>(data),
|
SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast<uint8_t*>(data),
|
||||||
4);
|
4);
|
||||||
|
|
||||||
data[0] = SPI_SWAP_DATA_TX(area_copy.y1, 16);
|
data[0] = SPI_SWAP_DATA_TX(area->y1, 16);
|
||||||
data[1] = SPI_SWAP_DATA_TX(area_copy.y2, 16);
|
data[1] = SPI_SWAP_DATA_TX(area->y2, 16);
|
||||||
SendCommandWithData(displays::ST77XX_RASET, reinterpret_cast<uint8_t*>(data),
|
SendCommandWithData(displays::ST77XX_RASET, reinterpret_cast<uint8_t*>(data),
|
||||||
4);
|
4);
|
||||||
|
|
||||||
@@ -332,22 +310,6 @@ void Display::OnLvglFlush(lv_disp_drv_t* disp_drv,
|
|||||||
spi_device_release_bus(handle_);
|
spi_device_release_bus(handle_);
|
||||||
|
|
||||||
lv_disp_flush_ready(&driver_);
|
lv_disp_flush_ready(&driver_);
|
||||||
//});
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderMain(void* raw_args) {
|
|
||||||
RenderTaskArgs* args = reinterpret_cast<RenderTaskArgs*>(raw_args);
|
|
||||||
QueueHandle_t queue = args->work_queue;
|
|
||||||
std::atomic<bool>* quit = args->quit;
|
|
||||||
delete args;
|
|
||||||
|
|
||||||
while (!quit->load()) {
|
|
||||||
// TODO: flush data here! Yay speed.
|
|
||||||
}
|
|
||||||
|
|
||||||
vQueueDelete(queue);
|
|
||||||
delete quit;
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace drivers
|
} // namespace drivers
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ class Display {
|
|||||||
IGpios& gpio_;
|
IGpios& gpio_;
|
||||||
spi_device_handle_t handle_;
|
spi_device_handle_t handle_;
|
||||||
|
|
||||||
std::unique_ptr<tasks::Worker> worker_task_;
|
|
||||||
|
|
||||||
bool display_on_;
|
bool display_on_;
|
||||||
uint_fast8_t brightness_;
|
uint_fast8_t brightness_;
|
||||||
|
|
||||||
|
|||||||
+33
-69
@@ -19,44 +19,36 @@ auto Name() -> std::string;
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kUi>() -> std::string {
|
auto Name<Type::kUi>() -> std::string {
|
||||||
return "LVGL";
|
return "ui";
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kUiFlush>() -> std::string {
|
auto Name<Type::kAudioDecoder>() -> std::string {
|
||||||
return "DISPLAY";
|
return "audio_dec";
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kFileStreamer>() -> std::string {
|
auto Name<Type::kAudioConverter>() -> std::string {
|
||||||
return "FSTREAMER";
|
return "audio_conv";
|
||||||
}
|
|
||||||
template <>
|
|
||||||
auto Name<Type::kAudio>() -> std::string {
|
|
||||||
return "AUDIO";
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
auto Name<Type::kMixer>() -> std::string {
|
|
||||||
return "MIXER";
|
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kDatabase>() -> std::string {
|
auto Name<Type::kDatabase>() -> std::string {
|
||||||
return "DB";
|
return "db_fg";
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kDatabaseBackground>() -> std::string {
|
auto Name<Type::kDatabaseBackground>() -> std::string {
|
||||||
return "DB_BG";
|
return "db_bg";
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Name<Type::kNvsWriter>() -> std::string {
|
auto Name<Type::kNvsWriter>() -> std::string {
|
||||||
return "NVS";
|
return "nvs";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Type t>
|
template <Type t>
|
||||||
auto AllocateStack() -> cpp::span<StackType_t>;
|
auto AllocateStack() -> cpp::span<StackType_t>;
|
||||||
|
|
||||||
// Decoders run on the audio task, and these sometimes require a fairly large
|
// Decoders often require a very large amount of stack space, since they aren't
|
||||||
// amount of stack space.
|
// usually written with embedded use cases in mind.
|
||||||
template <>
|
template <>
|
||||||
auto AllocateStack<Type::kAudio>() -> cpp::span<StackType_t> {
|
auto AllocateStack<Type::kAudioDecoder>() -> cpp::span<StackType_t> {
|
||||||
std::size_t size = 64 * 1024;
|
std::size_t size = 64 * 1024;
|
||||||
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
||||||
size};
|
size};
|
||||||
@@ -69,29 +61,15 @@ auto AllocateStack<Type::kUi>() -> cpp::span<StackType_t> {
|
|||||||
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
||||||
size};
|
size};
|
||||||
}
|
}
|
||||||
// UI flushes *must* be done from internal RAM. Thankfully, there is very little
|
|
||||||
// stack required to perform them, and the amount of stack needed is fixed.
|
|
||||||
template <>
|
template <>
|
||||||
auto AllocateStack<Type::kUiFlush>() -> cpp::span<StackType_t> {
|
// PCM conversion and resampling uses a very small amount of stack. It works
|
||||||
std::size_t size = 1024;
|
// entirely with PSRAM-allocated buffers, so no real speed gain from allocating
|
||||||
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)),
|
// it internally.
|
||||||
size};
|
auto AllocateStack<Type::kAudioConverter>() -> cpp::span<StackType_t> {
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
auto AllocateStack<Type::kFileStreamer>() -> cpp::span<StackType_t> {
|
|
||||||
std::size_t size = 4 * 1024;
|
std::size_t size = 4 * 1024;
|
||||||
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
||||||
size};
|
size};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
auto AllocateStack<Type::kMixer>() -> cpp::span<StackType_t> {
|
|
||||||
std::size_t size = 4 * 1024;
|
|
||||||
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
|
|
||||||
size};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leveldb is designed for non-embedded use cases, where stack space isn't so
|
// Leveldb is designed for non-embedded use cases, where stack space isn't so
|
||||||
// much of a concern. It therefore uses an eye-wateringly large amount of stack.
|
// much of a concern. It therefore uses an eye-wateringly large amount of stack.
|
||||||
template <>
|
template <>
|
||||||
@@ -114,8 +92,8 @@ auto AllocateStack<Type::kNvsWriter>() -> cpp::span<StackType_t> {
|
|||||||
size};
|
size};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2048 bytes in internal ram
|
// 2 KiB in internal ram
|
||||||
// 302 KiB in external ram.
|
// 612 KiB in external ram.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Please keep the priorities below in descending order for better readability.
|
* Please keep the priorities below in descending order for better readability.
|
||||||
@@ -124,39 +102,22 @@ auto AllocateStack<Type::kNvsWriter>() -> cpp::span<StackType_t> {
|
|||||||
template <Type t>
|
template <Type t>
|
||||||
auto Priority() -> UBaseType_t;
|
auto Priority() -> UBaseType_t;
|
||||||
|
|
||||||
// NVS writing requires suspending one of our cores, and disabling tasks with
|
// Realtime audio is the entire point of this device, so give these tasks the
|
||||||
// their stacks in PSRAM. Get it over and done with as soon as possible.
|
|
||||||
template <>
|
|
||||||
auto Priority<Type::kNvsWriter>() -> UBaseType_t {
|
|
||||||
return 13;
|
|
||||||
}
|
|
||||||
// Realtime audio is the entire point of this device, so give this task the
|
|
||||||
// highest priority.
|
// highest priority.
|
||||||
template <>
|
template <>
|
||||||
auto Priority<Type::kMixer>() -> UBaseType_t {
|
auto Priority<Type::kAudioDecoder>() -> UBaseType_t {
|
||||||
return 12;
|
return 18;
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Priority<Type::kAudio>() -> UBaseType_t {
|
auto Priority<Type::kAudioConverter>() -> UBaseType_t {
|
||||||
return 11;
|
return 18;
|
||||||
}
|
}
|
||||||
// After audio issues, UI jank is the most noticeable kind of scheduling-induced
|
// After audio issues, UI jank is the most noticeable kind of scheduling-induced
|
||||||
// slowness that the user is likely to notice or care about. Therefore we place
|
// slowness that the user is likely to notice or care about. Therefore we place
|
||||||
// this task directly below audio in terms of priority.
|
// this task directly below audio in terms of priority. Note that during audio
|
||||||
|
// playback, this priority will be downgraded.
|
||||||
template <>
|
template <>
|
||||||
auto Priority<Type::kUi>() -> UBaseType_t {
|
auto Priority<Type::kUi>() -> UBaseType_t {
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
// UI flushing should use the same priority as the UI task, so as to maximise
|
|
||||||
// the chance of the happy case: one of our cores is writing to the screen,
|
|
||||||
// whilst the other is simultaneously preparing the next buffer to be flushed.
|
|
||||||
template <>
|
|
||||||
auto Priority<Type::kUiFlush>() -> UBaseType_t {
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
auto Priority<Type::kFileStreamer>() -> UBaseType_t {
|
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
// Database interactions are all inherently async already, due to their
|
// Database interactions are all inherently async already, due to their
|
||||||
@@ -165,11 +126,18 @@ auto Priority<Type::kFileStreamer>() -> UBaseType_t {
|
|||||||
// priority.
|
// priority.
|
||||||
template <>
|
template <>
|
||||||
auto Priority<Type::kDatabase>() -> UBaseType_t {
|
auto Priority<Type::kDatabase>() -> UBaseType_t {
|
||||||
return 8;
|
return 2;
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
auto Priority<Type::kDatabaseBackground>() -> UBaseType_t {
|
auto Priority<Type::kDatabaseBackground>() -> UBaseType_t {
|
||||||
return 7;
|
return 1;
|
||||||
|
}
|
||||||
|
// NVS writing requires suspending one of our cores, and disabling tasks with
|
||||||
|
// their stacks in PSRAM. Only do it when there's not more important work
|
||||||
|
// pending.
|
||||||
|
template <>
|
||||||
|
auto Priority<Type::kNvsWriter>() -> UBaseType_t {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Type t>
|
template <Type t>
|
||||||
@@ -184,10 +152,6 @@ auto WorkerQueueSize<Type::kDatabaseBackground>() -> std::size_t {
|
|||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
auto WorkerQueueSize<Type::kUiFlush>() -> std::size_t {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
template <>
|
template <>
|
||||||
auto WorkerQueueSize<Type::kNvsWriter>() -> std::size_t {
|
auto WorkerQueueSize<Type::kNvsWriter>() -> std::size_t {
|
||||||
return 2;
|
return 2;
|
||||||
|
|||||||
+7
-8
@@ -30,18 +30,17 @@ namespace tasks {
|
|||||||
enum class Type {
|
enum class Type {
|
||||||
// The main UI task. This runs the LVGL main loop.
|
// The main UI task. This runs the LVGL main loop.
|
||||||
kUi,
|
kUi,
|
||||||
// Task for flushing graphics buffers to the display.
|
// The main audio pipeline task. Decodes files into PCM stream.
|
||||||
kUiFlush,
|
kAudioDecoder,
|
||||||
// TODO.
|
// Second audio task. Converts the PCM stream into one suitable for the
|
||||||
kFileStreamer,
|
// current output (e.g. downsampling for bluetooth).
|
||||||
// The main audio pipeline task.
|
kAudioConverter,
|
||||||
kAudio,
|
|
||||||
// TODO
|
|
||||||
kMixer,
|
|
||||||
// Task for running database queries.
|
// Task for running database queries.
|
||||||
kDatabase,
|
kDatabase,
|
||||||
// Task for internal database operations
|
// Task for internal database operations
|
||||||
kDatabaseBackground,
|
kDatabaseBackground,
|
||||||
|
// Task for interacting with NVS -- this needs to be done with an internal
|
||||||
|
// stack.
|
||||||
kNvsWriter,
|
kNvsWriter,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ class UiTask {
|
|||||||
|
|
||||||
std::shared_ptr<TouchWheelEncoder> input_device_;
|
std::shared_ptr<TouchWheelEncoder> input_device_;
|
||||||
std::shared_ptr<Screen> current_screen_;
|
std::shared_ptr<Screen> current_screen_;
|
||||||
|
|
||||||
std::atomic<bool> quit_;
|
|
||||||
SemaphoreHandle_t frame_semaphore_;
|
|
||||||
TimerHandle_t frame_timer_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|||||||
@@ -48,10 +48,9 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
|||||||
void react(const tinyfsm::Event& ev) {}
|
void react(const tinyfsm::Event& ev) {}
|
||||||
|
|
||||||
virtual void react(const system_fsm::BatteryStateChanged&);
|
virtual void react(const system_fsm::BatteryStateChanged&);
|
||||||
|
virtual void react(const audio::PlaybackStarted&);
|
||||||
virtual void react(const audio::PlaybackStarted&) {}
|
virtual void react(const audio::PlaybackFinished&);
|
||||||
virtual void react(const audio::PlaybackUpdate&) {}
|
virtual void react(const audio::PlaybackUpdate&) {}
|
||||||
virtual void react(const audio::PlaybackFinished&) {}
|
|
||||||
virtual void react(const audio::QueueUpdate&) {}
|
virtual void react(const audio::QueueUpdate&) {}
|
||||||
|
|
||||||
virtual void react(const system_fsm::KeyLockChanged&);
|
virtual void react(const system_fsm::KeyLockChanged&);
|
||||||
|
|||||||
+4
-22
@@ -49,24 +49,8 @@
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
static const char* kTag = "ui_task";
|
static const char* kTag = "ui_task";
|
||||||
static const TickType_t kMaxFrameRate = pdMS_TO_TICKS(100);
|
|
||||||
|
|
||||||
static auto next_frame(TimerHandle_t t) {
|
UiTask::UiTask() {}
|
||||||
SemaphoreHandle_t sem =
|
|
||||||
reinterpret_cast<SemaphoreHandle_t>(pvTimerGetTimerID(t));
|
|
||||||
xSemaphoreGive(sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
UiTask::UiTask()
|
|
||||||
: quit_(false),
|
|
||||||
frame_semaphore_(xSemaphoreCreateBinary()),
|
|
||||||
frame_timer_(xTimerCreate("ui_frame",
|
|
||||||
kMaxFrameRate,
|
|
||||||
pdTRUE,
|
|
||||||
frame_semaphore_,
|
|
||||||
next_frame)) {
|
|
||||||
xTimerStart(frame_timer_, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
UiTask::~UiTask() {
|
UiTask::~UiTask() {
|
||||||
assert(false);
|
assert(false);
|
||||||
@@ -98,10 +82,8 @@ auto UiTask::Main() -> void {
|
|||||||
current_screen_->Tick();
|
current_screen_->Tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_task_handler();
|
TickType_t delay = lv_timer_handler();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(std::clamp<TickType_t>(delay, 0, 100)));
|
||||||
// Wait for the signal to loop again.
|
|
||||||
xSemaphoreTake(frame_semaphore_, portMAX_DELAY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +96,7 @@ auto UiTask::SetInputDevice(std::shared_ptr<TouchWheelEncoder> dev) -> void {
|
|||||||
|
|
||||||
auto UiTask::Start() -> UiTask* {
|
auto UiTask::Start() -> UiTask* {
|
||||||
UiTask* ret = new UiTask();
|
UiTask* ret = new UiTask();
|
||||||
tasks::StartPersistent<tasks::Type::kUi>(0, [=]() { ret->Main(); });
|
tasks::StartPersistent<tasks::Type::kUi>([=]() { ret->Main(); });
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ void UiState::react(const system_fsm::BatteryStateChanged&) {
|
|||||||
UpdateTopBar();
|
UpdateTopBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UiState::react(const audio::PlaybackStarted&) {
|
||||||
|
vTaskPrioritySet(NULL, 0);
|
||||||
|
}
|
||||||
|
void UiState::react(const audio::PlaybackFinished&) {
|
||||||
|
vTaskPrioritySet(NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
void UiState::UpdateTopBar() {
|
void UiState::UpdateTopBar() {
|
||||||
auto battery_state = sServices->battery().State();
|
auto battery_state = sServices->battery().State();
|
||||||
bool has_queue = sServices->track_queue().GetCurrent().has_value();
|
bool has_queue = sServices->track_queue().GetCurrent().has_value();
|
||||||
@@ -251,6 +258,7 @@ void Playing::exit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Playing::react(const audio::PlaybackStarted& ev) {
|
void Playing::react(const audio::PlaybackStarted& ev) {
|
||||||
|
vTaskPrioritySet(NULL, 0);
|
||||||
UpdateTopBar();
|
UpdateTopBar();
|
||||||
sPlayingScreen->OnTrackUpdate();
|
sPlayingScreen->OnTrackUpdate();
|
||||||
}
|
}
|
||||||
@@ -261,6 +269,7 @@ void Playing::react(const audio::PlaybackUpdate& ev) {
|
|||||||
|
|
||||||
void Playing::react(const audio::PlaybackFinished& ev) {
|
void Playing::react(const audio::PlaybackFinished& ev) {
|
||||||
UpdateTopBar();
|
UpdateTopBar();
|
||||||
|
vTaskPrioritySet(NULL, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playing::react(const audio::QueueUpdate& ev) {
|
void Playing::react(const audio::QueueUpdate& ev) {
|
||||||
|
|||||||
Reference in New Issue
Block a user