Improve metronome

This commit is contained in:
Riku Isokoski 2021-08-13 22:03:27 +03:00 committed by JF002
parent 7c28de0b6f
commit 6192775161
3 changed files with 69 additions and 99 deletions

View file

@ -414,6 +414,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
break; break;
case Apps::Metronome: case Apps::Metronome:
currentScreen = std::make_unique<Screens::Metronome>(this, motorController, *systemTask); currentScreen = std::make_unique<Screens::Metronome>(this, motorController, *systemTask);
ReturnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::None);
break; break;
case Apps::Motion: case Apps::Motion:
currentScreen = std::make_unique<Screens::Motion>(this, motionController); currentScreen = std::make_unique<Screens::Motion>(this, motionController);

View file

@ -1,35 +1,15 @@
#include "Metronome.h" #include "Metronome.h"
#include "Screen.h"
#include "Symbols.h" #include "Symbols.h"
#include "lvgl/lvgl.h"
#include "FreeRTOSConfig.h"
#include "task.h"
#include <string>
#include <tuple>
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
float calculateDelta(const TickType_t startTime, const TickType_t currentTime) { void eventHandler(lv_obj_t* obj, lv_event_t event) {
TickType_t delta = 0; auto* screen = static_cast<Metronome*>(obj->user_data);
// Take care of overflow
if (startTime > currentTime) {
delta = 0xffffffff - startTime;
delta += (currentTime + 1);
} else {
delta = currentTime - startTime;
}
return static_cast<float>(delta) / static_cast<float>(configTICK_RATE_HZ);
}
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
Metronome* screen = static_cast<Metronome*>(obj->user_data);
screen->OnEvent(obj, event); screen->OnEvent(obj, event);
} }
lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x = 0, uint8_t y = 0) { lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x, uint8_t y) {
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
@ -41,7 +21,7 @@ namespace {
} }
Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask) Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask)
: Screen(app), running {true}, currentState {States::Stopped}, startTime {}, motorController {motorController}, systemTask {systemTask} { : Screen(app), motorController {motorController}, systemTask {systemTask} {
bpmArc = lv_arc_create(lv_scr_act(), nullptr); bpmArc = lv_arc_create(lv_scr_act(), nullptr);
bpmArc->user_data = this; bpmArc->user_data = this;
@ -52,10 +32,10 @@ Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorControl
lv_arc_set_value(bpmArc, bpm); lv_arc_set_value(bpmArc, bpm);
lv_obj_set_size(bpmArc, 210, 210); lv_obj_set_size(bpmArc, 210, 210);
lv_arc_set_adjustable(bpmArc, true); lv_arc_set_adjustable(bpmArc, true);
lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 7); lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
bpmValue = createLabel(std::to_string(lv_arc_get_value(bpmArc)).c_str(), bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55); bpmValue = createLabel("120", bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55);
bpmLegend = createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0); createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0);
bpmTap = lv_btn_create(lv_scr_act(), nullptr); bpmTap = lv_btn_create(lv_scr_act(), nullptr);
bpmTap->user_data = this; bpmTap->user_data = this;
@ -69,20 +49,23 @@ Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorControl
lv_obj_set_event_cb(bpbDropdown, eventHandler); lv_obj_set_event_cb(bpbDropdown, eventHandler);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_MAIN, LV_STATE_DEFAULT, 20); lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_LIST, LV_STATE_DEFAULT, 20); lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_LIST, LV_STATE_DEFAULT, 20);
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 15, -4); lv_obj_set_size(bpbDropdown, 115, 50);
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_dropdown_set_options(bpbDropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9"); lv_dropdown_set_options(bpbDropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9");
lv_dropdown_set_selected(bpbDropdown, bpb - 1); lv_dropdown_set_selected(bpbDropdown, bpb - 1);
bpbLegend = lv_label_create(bpbDropdown, nullptr); lv_dropdown_set_show_selected(bpbDropdown, false);
lv_label_set_text(bpbLegend, "bpb"); lv_dropdown_set_text(bpbDropdown, "");
lv_obj_align(bpbLegend, bpbDropdown, LV_ALIGN_IN_RIGHT_MID, -15, 0);
currentBpbText = lv_label_create(bpbDropdown, nullptr);
lv_label_set_text_fmt(currentBpbText, "%d bpb", bpb);
lv_obj_align(currentBpbText, bpbDropdown, LV_ALIGN_CENTER, 0, 0);
playPause = lv_btn_create(lv_scr_act(), nullptr); playPause = lv_btn_create(lv_scr_act(), nullptr);
playPause->user_data = this; playPause->user_data = this;
lv_obj_set_event_cb(playPause, eventHandler); lv_obj_set_event_cb(playPause, eventHandler);
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10); lv_obj_set_size(playPause, 115, 50);
lv_obj_set_height(playPause, 39); lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
playPauseLabel = lv_label_create(playPause, nullptr); lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
lv_label_set_text(playPauseLabel, Symbols::play);
app->SetTouchMode(DisplayApp::TouchModes::Polling); app->SetTouchMode(DisplayApp::TouchModes::Polling);
} }
@ -93,28 +76,17 @@ Metronome::~Metronome() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
bool Metronome::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true;
}
bool Metronome::Refresh() { bool Metronome::Refresh() {
switch (currentState) { if (metronomeStarted) {
case States::Stopped: { if (xTaskGetTickCount() - startTime > 60 * configTICK_RATE_HZ / bpm) {
break; startTime += 60 * configTICK_RATE_HZ / bpm;
} counter--;
case States::Running: { if (counter == 0) {
if (calculateDelta(startTime, xTaskGetTickCount()) >= (60.0 / bpm)) { counter = bpb;
counter--; motorController.SetDuration(90);
startTime -= 60.0 / bpm; } else {
startTime = xTaskGetTickCount(); motorController.SetDuration(30);
if (counter == 0) {
counter = bpb;
motorController.SetDuration(90);
} else {
motorController.SetDuration(30);
}
} }
break;
} }
} }
return running; return running;
@ -128,42 +100,39 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
lv_label_set_text_fmt(bpmValue, "%03d", bpm); lv_label_set_text_fmt(bpmValue, "%03d", bpm);
} else if (obj == bpbDropdown) { } else if (obj == bpbDropdown) {
bpb = lv_dropdown_get_selected(obj) + 1; bpb = lv_dropdown_get_selected(obj) + 1;
lv_label_set_text_fmt(currentBpbText, "%d bpb", bpb);
lv_obj_realign(currentBpbText);
} }
break; break;
} }
case LV_EVENT_PRESSED: { case LV_EVENT_PRESSED: {
if (obj == bpmTap) { if (obj == bpmTap) {
float timeDelta = calculateDelta(tappedTime, xTaskGetTickCount()); TickType_t delta = xTaskGetTickCount() - tappedTime;
if (tappedTime == 0 || timeDelta > 3) { if (tappedTime != 0 && delta < configTICK_RATE_HZ * 3) {
tappedTime = xTaskGetTickCount(); bpm = configTICK_RATE_HZ * 60 / delta;
} else {
bpm = ceil(60.0 / timeDelta);
lv_arc_set_value(bpmArc, bpm); lv_arc_set_value(bpmArc, bpm);
lv_label_set_text_fmt(bpmValue, "%03d", bpm); lv_label_set_text_fmt(bpmValue, "%03d", bpm);
tappedTime = xTaskGetTickCount();
} }
tappedTime = xTaskGetTickCount();
} }
break; break;
} }
case LV_EVENT_CLICKED: { case LV_EVENT_CLICKED: {
if (obj == playPause) { if (obj == playPause) {
currentState = (currentState == States::Stopped ? States::Running : States::Stopped); metronomeStarted = !metronomeStarted;
switch (currentState) { if (metronomeStarted) {
case States::Stopped: { lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::pause);
lv_label_set_text(playPauseLabel, Symbols::play); systemTask.PushMessage(System::Messages::DisableSleeping);
systemTask.PushMessage(System::Messages::EnableSleeping); startTime = xTaskGetTickCount();
break; counter = 1;
} } else {
case States::Running: { lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
lv_label_set_text(playPauseLabel, Symbols::pause); systemTask.PushMessage(System::Messages::EnableSleeping);
systemTask.PushMessage(System::Messages::DisableSleeping);
startTime = xTaskGetTickCount();
counter = 1;
break;
}
} }
} }
break; break;
} }
default:
break;
} }
} }

View file

@ -3,32 +3,32 @@
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
#include "components/motor/MotorController.h" #include "components/motor/MotorController.h"
#include <array> namespace Pinetime {
namespace Applications {
namespace Screens {
namespace Pinetime::Applications::Screens { class Metronome : public Screen {
public:
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask);
~Metronome() override;
bool Refresh() override;
void OnEvent(lv_obj_t* obj, lv_event_t event);
class Metronome : public Screen { private:
public: TickType_t startTime = 0;
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask); TickType_t tappedTime = 0;
~Metronome() override; Controllers::MotorController& motorController;
bool Refresh() override; System::SystemTask& systemTask;
bool OnTouchEvent(TouchEvents event) override; int16_t bpm = 120;
void OnEvent(lv_obj_t* obj, lv_event_t event); uint8_t bpb = 4;
enum class States { Running, Stopped }; uint8_t counter = 1;
private: bool metronomeStarted = false;
bool running;
States currentState;
TickType_t startTime;
TickType_t tappedTime = 0;
Controllers::MotorController& motorController;
System::SystemTask& systemTask;
uint16_t bpm = 120;
uint8_t bpb = 4;
uint8_t counter = 1;
lv_obj_t *bpmArc, *bpmTap, *bpmValue, *bpmLegend; lv_obj_t *bpmArc, *bpmTap, *bpmValue;
lv_obj_t *bpbDropdown, *bpbLegend; lv_obj_t *bpbDropdown, *currentBpbText;
lv_obj_t *playPause, *playPauseLabel; lv_obj_t *playPause;
}; };
}
}
} }