metronome.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <m-string.h>
  6. #include <stdlib.h>
  7. #define BPM_STEP_SIZE 0.5d
  8. typedef enum {
  9. EventTypeTick,
  10. EventTypeKey,
  11. } EventType;
  12. typedef struct {
  13. EventType type;
  14. InputEvent input;
  15. } PluginEvent;
  16. typedef struct {
  17. double bpm;
  18. bool playing;
  19. FuriTimer* timer;
  20. } MetronomeState;
  21. static void render_callback(Canvas* const canvas, void* ctx) {
  22. const MetronomeState* metronome_state = acquire_mutex((ValueMutex*)ctx, 25);
  23. if(metronome_state == NULL) {
  24. return;
  25. }
  26. char* play_state;
  27. string_t tempStr;
  28. string_init(tempStr);
  29. // border around the edge of the screen
  30. canvas_draw_frame(canvas, 0, 0, 128, 64);
  31. canvas_set_font(canvas, FontPrimary);
  32. // draw playing state
  33. if (metronome_state->playing) {
  34. play_state = "Playing";
  35. } else {
  36. play_state = "Paused";
  37. }
  38. canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, play_state);
  39. // draw BPM value
  40. string_printf(tempStr, "%.2f", metronome_state->bpm);
  41. canvas_set_font(canvas, FontBigNumbers);
  42. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, string_get_cstr(tempStr));
  43. string_reset(tempStr);
  44. string_clear(tempStr);
  45. release_mutex((ValueMutex*)ctx, metronome_state);
  46. }
  47. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  48. furi_assert(event_queue);
  49. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  50. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  51. }
  52. static void timer_callback() {
  53. //UNUSED(metronome_state);
  54. furi_hal_speaker_start(440.0f, 1.0f);
  55. furi_delay_ms(40);
  56. furi_hal_speaker_stop();
  57. }
  58. static void metronome_state_init(MetronomeState* const metronome_state) {
  59. metronome_state->bpm = 120.0;
  60. metronome_state->playing = false;
  61. metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, metronome_state);
  62. }
  63. int32_t metronome_app() {
  64. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  65. MetronomeState* metronome_state = malloc(sizeof(MetronomeState));
  66. metronome_state_init(metronome_state);
  67. ValueMutex state_mutex;
  68. if(!init_mutex(&state_mutex, metronome_state, sizeof(MetronomeState))) {
  69. FURI_LOG_E("Metronome", "cannot create mutex\r\n");
  70. free(metronome_state);
  71. return 255;
  72. }
  73. // Set system callbacks
  74. ViewPort* view_port = view_port_alloc();
  75. view_port_draw_callback_set(view_port, render_callback, &state_mutex);
  76. view_port_input_callback_set(view_port, input_callback, event_queue);
  77. // Open GUI and register view_port
  78. Gui* gui = furi_record_open("gui");
  79. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  80. PluginEvent event;
  81. for(bool processing = true; processing;) {
  82. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  83. MetronomeState* metronome_state = (MetronomeState*)acquire_mutex_block(&state_mutex);
  84. if(event_status == FuriStatusOk) {
  85. // press events
  86. if(event.type == EventTypeKey) {
  87. if(event.input.type == InputTypePress) {
  88. switch(event.input.key) {
  89. case InputKeyUp:
  90. break;
  91. case InputKeyDown:
  92. break;
  93. case InputKeyRight:
  94. metronome_state->bpm += (double)BPM_STEP_SIZE;
  95. break;
  96. case InputKeyLeft:
  97. metronome_state->bpm -= (double)BPM_STEP_SIZE;
  98. break;
  99. case InputKeyOk:
  100. metronome_state->playing = !metronome_state->playing;
  101. if (metronome_state->playing) {
  102. // calculate time between beeps
  103. uint32_t tps = furi_kernel_get_tick_frequency();
  104. double bps = (double)metronome_state->bpm / 60;
  105. uint32_t ticks_to_sleep = (uint32_t)round(tps / bps);
  106. furi_timer_start(metronome_state->timer, ticks_to_sleep);
  107. } else {
  108. furi_timer_stop(metronome_state->timer);
  109. }
  110. break;
  111. case InputKeyBack:
  112. processing = false;
  113. break;
  114. }
  115. }
  116. }
  117. } else {
  118. FURI_LOG_D("Hello_world", "FuriMessageQueue: event timeout");
  119. // event timeout
  120. }
  121. view_port_update(view_port);
  122. release_mutex(&state_mutex, metronome_state);
  123. }
  124. view_port_enabled_set(view_port, false);
  125. gui_remove_view_port(gui, view_port);
  126. furi_record_close("gui");
  127. view_port_free(view_port);
  128. furi_message_queue_free(event_queue);
  129. delete_mutex(&state_mutex);
  130. furi_timer_free(metronome_state->timer);
  131. free(metronome_state);
  132. return 0;
  133. }