Explorar o código

refactor: codebase improvements

Ihar Biaspalau hai 10 meses
pai
achega
9f0ba6d2c4
Modificáronse 6 ficheiros con 590 adicións e 533 borrados
  1. 8 0
      .gitignore
  2. 48 4
      README.md
  3. 6 2
      application.fam
  4. 63 65
      notes.h
  5. 345 306
      tuning_fork.c
  6. 120 156
      tunings.h

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+dist/*
+.vscode
+.clang-format
+.clangd
+.editorconfig
+.env
+.ufbt
+.nova

+ 48 - 4
README.md

@@ -1,7 +1,5 @@
 # Tuning Fork
 
-Inspired by [Metronome](https://github.com/panki27/Metronome)
-
 A tuning fork for the [Flipper Zero](https://flipperzero.one/) device.
 Allows to play different notes in different pitches.
 
@@ -23,8 +21,54 @@ Allows to play different notes in different pitches.
 - Bass D (4 strings)
 - Bass Drop A (5 strings)
 
-## Compiling
+## Development
+
+### Install ufbt
+> Linux & macOS: `python3 -m pip install --upgrade ufbt`
+> Windows: `py -m pip install --upgrade ufbt`
 
+### Clone repo
+```
+git clone https://github.com/besya/flipperzero-tuning-fork.git
+```
+### Navigate to project folder
+```
+cd flipperzero-tuning-fork
 ```
-./fbt firmware_tuning_fork
+### Prepare VSCode environment
 ```
+ufbt vscode_dist
+```
+### Build app
+```
+ufbt
+```
+> This command creates dist/tuning_fork.fap
+
+### Launch app
+```
+ufbt launch
+```
+> This command deploys app to Flipper Zero and launches an application
+
+## Contibuting
+
+1. Fork repo
+1. Clone
+1. Create branch
+1. Make changes
+1. Commit changes and push to branch
+1. Open fork page and click Contribute
+1. Create a pull-request from your branch to this repo's main branch
+
+## Acknowledgements
+
+Special thanks to:
+- [@xMasterX] for distributing and support
+- [@Willy-JL] for fixes
+- [@panki27] for inspiration
+- The Flipper Zero community for support and feedback
+
+## License
+
+This project is licensed under the GNU GPL v3 - see the [LICENSE](LICENSE) file for details.

+ 6 - 2
application.fam

@@ -1,14 +1,18 @@
 App(
     appid="tuning_fork",
     name="Tuning Fork",
-    apptype=FlipperAppType.PLUGIN,
+    apptype=FlipperAppType.EXTERNAL,
     entry_point="tuning_fork_app",
     cdefines=["APP_TUNING_FORM"],
     requires=[
         "gui",
     ],
     fap_icon="tuning_fork_icon.png",
-    fap_category="Music",
+    fap_category="Media",
     stack_size=2 * 1024,
     order=20,
+    fap_author="@besya & (Fixes by @Willy-JL)",
+    fap_weburl="https://github.com/besya/flipperzero-tuning-fork",
+    fap_version="1.3",
+    fap_description="Tuning fork for tuning musical instruments",
 )

+ 63 - 65
notes.h

@@ -1,160 +1,158 @@
 #ifndef NOTES
 #define NOTES
 
-
-#define C0 16.35f
+#define C0  16.35f
 #define Cs0 17.32f
 #define Db0 17.32f
-#define D0 18.35f
+#define D0  18.35f
 #define Ds0 19.45f
 #define Eb0 19.45f
-#define E0 20.60f
-#define F0 21.83f
+#define E0  20.60f
+#define F0  21.83f
 #define Fs0 23.12f
 #define Gb0 23.12f
-#define G0 24.50f
+#define G0  24.50f
 #define Gs0 25.96f
 #define Ab0 25.96f
-#define A0 27.50f
+#define A0  27.50f
 #define As0 29.14f
 #define Bb0 29.14f
-#define B0 30.868f
-#define C1 32.70f
+#define B0  30.868f
+#define C1  32.70f
 #define Cs1 34.65f
 #define Db1 34.65f
-#define D1 36.71f
+#define D1  36.71f
 #define Ds1 38.89f
 #define Eb1 38.89f
-#define E1 41.203f
-#define F1 43.65f
+#define E1  41.203f
+#define F1  43.65f
 #define Fs1 46.25f
 #define Gb1 46.25f
-#define G1 49.00f
+#define G1  49.00f
 #define Gs1 51.91f
 #define Ab1 51.91f
-#define A1 55.00f
+#define A1  55.00f
 #define As1 58.27f
 #define Bb1 58.27f
-#define B1 61.74f
-#define C2 65.41f
+#define B1  61.74f
+#define C2  65.41f
 #define Cs2 69.30f
 #define Db2 69.30f
-#define D2 73.416f
+#define D2  73.416f
 #define Ds2 77.78f
 #define Eb2 77.78f
-#define E2 82.41f
-#define F2 87.31f
+#define E2  82.41f
+#define F2  87.31f
 #define Fs2 92.50f
 #define Gb2 92.50f
-#define G2 97.999f
+#define G2  97.999f
 #define Gs2 103.83f
 #define Ab2 103.83f
-#define A2 110.00f
+#define A2  110.00f
 #define As2 116.54f
 #define Bb2 116.54f
-#define B2 123.47f
-#define C3 130.813f
+#define B2  123.47f
+#define C3  130.813f
 #define Cs3 138.59f
 #define Db3 138.59f
-#define D3 146.83f
+#define D3  146.83f
 #define Ds3 155.56f
 #define Eb3 155.56f
-#define E3 164.81f
-#define F3 174.61f
+#define E3  164.81f
+#define F3  174.61f
 #define Fs3 185.00f
 #define Gb3 185.00f
-#define G3 196.00f
+#define G3  196.00f
 #define Gs3 207.65f
 #define Ab3 207.65f
-#define A3 220.00f
+#define A3  220.00f
 #define As3 233.08f
 #define Bb3 233.08f
-#define B3 246.94f
-#define C4 261.63f
+#define B3  246.94f
+#define C4  261.63f
 #define Cs4 277.18f
 #define Db4 277.18f
-#define D4 293.66f
+#define D4  293.66f
 #define Ds4 311.13f
 #define Eb4 311.13f
-#define E4 329.63f
-#define F4 349.23f
+#define E4  329.63f
+#define F4  349.23f
 #define Fs4 369.99f
 #define Gb4 369.99f
-#define G4 392.00f
+#define G4  392.00f
 #define Gs4 415.30f
 #define Ab4 415.30f
-#define A4 440.00f
+#define A4  440.00f
 #define As4 466.16f
 #define Bb4 466.16f
-#define B4 493.88f
-#define C5 523.25f
+#define B4  493.88f
+#define C5  523.25f
 #define Cs5 554.37f
 #define Db5 554.37f
-#define D5 587.33f
+#define D5  587.33f
 #define Ds5 622.25f
 #define Eb5 622.25f
-#define E5 659.25f
-#define F5 698.46f
+#define E5  659.25f
+#define F5  698.46f
 #define Fs5 739.99f
 #define Gb5 739.99f
-#define G5 783.99f
+#define G5  783.99f
 #define Gs5 830.61f
 #define Ab5 830.61f
-#define A5 880.00f
+#define A5  880.00f
 #define As5 932.33f
 #define Bb5 932.33f
-#define B5 987.77f
-#define C6 1046.50f
+#define B5  987.77f
+#define C6  1046.50f
 #define Cs6 1108.73f
 #define Db6 1108.73f
-#define D6 1174.66f
+#define D6  1174.66f
 #define Ds6 1244.51f
 #define Eb6 1244.51f
-#define E6 1318.51f
-#define F6 1396.91f
+#define E6  1318.51f
+#define F6  1396.91f
 #define Fs6 1479.98f
 #define Gb6 1479.98f
-#define G6 1567.98f
+#define G6  1567.98f
 #define Gs6 1661.22f
 #define Ab6 1661.22f
-#define A6 1760.00f
+#define A6  1760.00f
 #define As6 1864.66f
 #define Bb6 1864.66f
-#define B6 1975.53f
-#define C7 2093.00f
+#define B6  1975.53f
+#define C7  2093.00f
 #define Cs7 2217.46f
 #define Db7 2217.46f
-#define D7 2349.32f
+#define D7  2349.32f
 #define Ds7 2489.02f
 #define Eb7 2489.02f
-#define E7 2637.02f
-#define F7 2793.83f
+#define E7  2637.02f
+#define F7  2793.83f
 #define Fs7 2959.96f
 #define Gb7 2959.96f
-#define G7 3135.96f
+#define G7  3135.96f
 #define Gs7 3322.44f
 #define Ab7 3322.44f
-#define A7 3520.00f
+#define A7  3520.00f
 #define As7 3729.31f
 #define Bb7 3729.31f
-#define B7 3951.07f
-#define C8 4186.01f
+#define B7  3951.07f
+#define C8  4186.01f
 #define Cs8 4434.92f
 #define Db8 4434.92f
-#define D8 4698.63f
+#define D8  4698.63f
 #define Ds8 4978.03f
 #define Eb8 4978.03f
-#define E8 5274.04f
-#define F8 5587.65f
+#define E8  5274.04f
+#define F8  5587.65f
 #define Fs8 5919.91f
 #define Gb8 5919.91f
-#define G8 6271.93f
+#define G8  6271.93f
 #define Gs8 6644.88f
 #define Ab8 6644.88f
-#define A8 7040.00f
+#define A8  7040.00f
 #define As8 7458.62f
 #define Bb8 7458.62f
-#define B8 7902.13f
+#define B8  7902.13f
 
 #endif //NOTES
-

+ 345 - 306
tuning_fork.c

@@ -1,395 +1,434 @@
 #include <furi.h>
 #include <furi_hal.h>
 #include <input/input.h>
-#include <m-string.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 
-#include <gui/gui.h>
-#include <gui/elements.h>
 #include <gui/canvas.h>
+#include <gui/elements.h>
+#include <gui/gui.h>
 
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 
-#include "notes.h"
 #include "tunings.h"
 
+#define VOLUME_STEP             0.1f
+#define DEFAULT_VOLUME          1.0f
+#define QUEUE_SIZE              8
+#define SPEAKER_ACQUIRE_TIMEOUT 1000
+
 typedef enum {
-  EventTypeTick,
-  EventTypeKey,
+    EventTypeTick,
+    EventTypeKey,
 } EventType;
 
 typedef struct {
-  EventType type;
-  InputEvent input;
+    EventType type;
+    InputEvent input;
 } PluginEvent;
 
 enum Page {
-  Tunings,
-  Notes
+    Tunings,
+    Notes
 };
 
 typedef struct {
-  bool playing;
-  enum Page page;
-  int current_tuning_note_index;
-  int current_tuning_index;
-  float volume;
-  TUNING tuning;
+    FuriMutex* mutex;
+    bool playing;
+    enum Page page;
+    int current_tuning_note_index;
+    int current_tuning_index;
+    float volume;
+    TUNING tuning;
 } TuningForkState;
 
 static TUNING current_tuning(TuningForkState* tuningForkState) {
-  return tuningForkState->tuning;
+    return tuningForkState->tuning;
 }
 
 static NOTE current_tuning_note(TuningForkState* tuningForkState) {
-  return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index];
+    return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index];
 }
 
 static float current_tuning_note_freq(TuningForkState* tuningForkState) {
-  return current_tuning_note(tuningForkState).frequency;
+    return current_tuning_note(tuningForkState).frequency;
+}
+
+static void safe_string_copy(char* dest, const char* src, size_t size) {
+    if(dest && src) {
+        strncpy(dest, src, size - 1);
+        dest[size - 1] = '\0';
+    }
 }
 
 static void current_tuning_note_label(TuningForkState* tuningForkState, char* outNoteLabel) {
-  for(int i=0; i < 20; ++i){
-    outNoteLabel[i] = current_tuning_note(tuningForkState).label[i];
-  }
+    if(outNoteLabel) {
+        safe_string_copy(outNoteLabel, current_tuning_note(tuningForkState).label, 20);
+    }
 }
 
 static void current_tuning_label(TuningForkState* tuningForkState, char* outTuningLabel) {
-  for(int i=0; i < 20; ++i){
-    outTuningLabel[i] = current_tuning(tuningForkState).label[i];
-  }
+    if(outTuningLabel) {
+        safe_string_copy(outTuningLabel, current_tuning(tuningForkState).label, 20);
+    }
 }
 
 static void updateTuning(TuningForkState* tuning_fork_state) {
-  tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index];
-  tuning_fork_state->current_tuning_note_index = 0;
+    tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index];
+    tuning_fork_state->current_tuning_note_index = 0;
 }
 
 static void next_tuning(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) {
-    tuning_fork_state->current_tuning_index = 0;
-  } else {
-    tuning_fork_state->current_tuning_index += 1;
-  }
-  updateTuning(tuning_fork_state);
+    if(tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) {
+        tuning_fork_state->current_tuning_index = 0;
+    } else {
+        tuning_fork_state->current_tuning_index += 1;
+    }
+    updateTuning(tuning_fork_state);
 }
 
 static void prev_tuning(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->current_tuning_index - 1 < 0) {
-    tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1;
-  } else {
-    tuning_fork_state->current_tuning_index -= 1;
-  }
-  updateTuning(tuning_fork_state);
+    if(tuning_fork_state->current_tuning_index - 1 < 0) {
+        tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1;
+    } else {
+        tuning_fork_state->current_tuning_index -= 1;
+    }
+    updateTuning(tuning_fork_state);
 }
 
 static void next_note(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->current_tuning_note_index == current_tuning(tuning_fork_state).notes_length - 1) {
-    tuning_fork_state->current_tuning_note_index = 0;
-  } else {
-    tuning_fork_state->current_tuning_note_index += 1;
-  }
+    if(tuning_fork_state->current_tuning_note_index ==
+       current_tuning(tuning_fork_state).notes_length - 1) {
+        tuning_fork_state->current_tuning_note_index = 0;
+    } else {
+        tuning_fork_state->current_tuning_note_index += 1;
+    }
 }
 
 static void prev_note(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->current_tuning_note_index == 0) {
-    tuning_fork_state->current_tuning_note_index = current_tuning(tuning_fork_state).notes_length - 1;
-  } else {
-    tuning_fork_state->current_tuning_note_index -= 1;
-  }
+    if(tuning_fork_state->current_tuning_note_index == 0) {
+        tuning_fork_state->current_tuning_note_index =
+            current_tuning(tuning_fork_state).notes_length - 1;
+    } else {
+        tuning_fork_state->current_tuning_note_index -= 1;
+    }
 }
 
 static void increase_volume(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->volume < 1.0f) {
-    tuning_fork_state->volume += 0.1f;
-  }
+    if(tuning_fork_state->volume <= (1.0f - VOLUME_STEP)) {
+        tuning_fork_state->volume += VOLUME_STEP;
+    }
 }
 
 static void decrease_volume(TuningForkState* tuning_fork_state) {
-  if (tuning_fork_state->volume > 0.0f) {
-    tuning_fork_state->volume -= 0.1f;
-  }
+    if(tuning_fork_state->volume >= VOLUME_STEP) {
+        tuning_fork_state->volume -= VOLUME_STEP;
+    }
 }
 
 static void play(TuningForkState* tuning_fork_state) {
-  furi_hal_speaker_start(current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume);
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) {
+        furi_hal_speaker_start(
+            current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume);
+    }
 }
 
 static void stop() {
-  furi_hal_speaker_stop();
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_stop();
+        furi_hal_speaker_release();
+    }
 }
 
 static void replay(TuningForkState* tuning_fork_state) {
-  stop();
-  play(tuning_fork_state);
+    stop();
+    play(tuning_fork_state);
 }
 
 static void render_callback(Canvas* const canvas, void* ctx) {
-  TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25);
-  if(tuning_fork_state == NULL) {
-    return;
-  }
-
-  string_t tempStr;
-  string_init(tempStr);
-
-  canvas_draw_frame(canvas, 0, 0, 128, 64);
-
-  canvas_set_font(canvas, FontPrimary);
-
-  if (tuning_fork_state->page == Tunings) {
-    char tuningLabel[20];
-    current_tuning_label(tuning_fork_state, tuningLabel);
-    string_printf(tempStr, "< %s >", tuningLabel);
-    canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr));
-    string_reset(tempStr);
-  } else {
-    char tuningLabel[20];
-    current_tuning_label(tuning_fork_state, tuningLabel);
-    string_printf(tempStr, "%s", tuningLabel);
-    canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr));
-    string_reset(tempStr);
-
-    char tuningNoteLabel[20];
-    current_tuning_note_label(tuning_fork_state, tuningNoteLabel);
-    string_printf(tempStr, "< %s >", tuningNoteLabel);
-    canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr));
-    string_reset(tempStr);
-  }
-
-  canvas_set_font(canvas, FontSecondary);
-  elements_button_left(canvas, "Prev");
-  elements_button_right(canvas, "Next");
-
-  if (tuning_fork_state->page == Notes) {
-    if (tuning_fork_state->playing) {
-      elements_button_center(canvas, "Stop ");
+    furi_assert(ctx);
+    TuningForkState* tuning_fork_state = ctx;
+    furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever);
+
+    FuriString* tempStr = furi_string_alloc();
+
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+
+    canvas_set_font(canvas, FontPrimary);
+
+    if(tuning_fork_state->page == Tunings) {
+        char tuningLabel[20];
+        current_tuning_label(tuning_fork_state, tuningLabel);
+        furi_string_printf(tempStr, "< %s >", tuningLabel);
+        canvas_draw_str_aligned(
+            canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+        furi_string_reset(tempStr);
+    } else {
+        char tuningLabel[20];
+        current_tuning_label(tuning_fork_state, tuningLabel);
+        furi_string_printf(tempStr, "%s", tuningLabel);
+        canvas_draw_str_aligned(
+            canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+        furi_string_reset(tempStr);
+
+        char tuningNoteLabel[20];
+        current_tuning_note_label(tuning_fork_state, tuningNoteLabel);
+        furi_string_printf(tempStr, "< %s >", tuningNoteLabel);
+        canvas_draw_str_aligned(
+            canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+        furi_string_reset(tempStr);
+    }
+
+    canvas_set_font(canvas, FontSecondary);
+    elements_button_left(canvas, "Prev");
+    elements_button_right(canvas, "Next");
+
+    if(tuning_fork_state->page == Notes) {
+        if(tuning_fork_state->playing) {
+            elements_button_center(canvas, "Stop ");
+        } else {
+            elements_button_center(canvas, "Play");
+        }
     } else {
-      elements_button_center(canvas, "Play");
+        elements_button_center(canvas, "Select");
     }
-  } else {
-    elements_button_center(canvas, "Select");
-  }
-  if (tuning_fork_state->page == Notes) {
-    elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume);
-  }
-
-  string_clear(tempStr);
-  release_mutex((ValueMutex*)ctx, tuning_fork_state);
+    if(tuning_fork_state->page == Notes) {
+        elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume);
+    }
+
+    furi_string_free(tempStr);
+    furi_mutex_release(tuning_fork_state->mutex);
 }
 
-static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
-  furi_assert(event_queue);
+static void input_callback(InputEvent* input_event, void* ctx) {
+    furi_assert(ctx);
+    FuriMessageQueue* event_queue = ctx;
 
-  PluginEvent event = {.type = EventTypeKey, .input = *input_event};
-  furi_message_queue_put(event_queue, &event, FuriWaitForever);
+    PluginEvent event = {.type = EventTypeKey, .input = *input_event};
+    furi_message_queue_put(event_queue, &event, FuriWaitForever);
 }
 
 static void tuning_fork_state_init(TuningForkState* const tuning_fork_state) {
-  tuning_fork_state->playing = false;
-  tuning_fork_state->page = Tunings;
-  tuning_fork_state->volume = 1.0f;
-  tuning_fork_state->tuning = GuitarStandard6;
-  tuning_fork_state->current_tuning_index = 2;
-  tuning_fork_state->current_tuning_note_index = 0;
+    if(tuning_fork_state) {
+        tuning_fork_state->playing = false;
+        tuning_fork_state->page = Tunings;
+        tuning_fork_state->volume = DEFAULT_VOLUME;
+        tuning_fork_state->tuning = GuitarStandard6;
+        tuning_fork_state->current_tuning_index = 2;
+        tuning_fork_state->current_tuning_note_index = 0;
+        updateTuning(tuning_fork_state);
+    }
 }
 
 int32_t tuning_fork_app() {
-  FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+    FuriMessageQueue* event_queue = furi_message_queue_alloc(QUEUE_SIZE, sizeof(PluginEvent));
+    if(!event_queue) {
+        FURI_LOG_E("TuningFork", "Cannot create message queue\r\n");
+        return 255;
+    }
 
-  TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState));
-  tuning_fork_state_init(tuning_fork_state);
+    TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState));
+    if(!tuning_fork_state) {
+        FURI_LOG_E("TuningFork", "Cannot allocate state\r\n");
+        furi_message_queue_free(event_queue);
+        return 255;
+    }
 
-  ValueMutex state_mutex;
-  if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) {
-    FURI_LOG_E("TuningFork", "cannot create mutex\r\n");
-    free(tuning_fork_state);
-    return 255;
-  }
-
-  // Set system callbacks
-  ViewPort* view_port = view_port_alloc();
-  view_port_draw_callback_set(view_port, render_callback, &state_mutex);
-  view_port_input_callback_set(view_port, input_callback, event_queue);
-
-  Gui* gui = furi_record_open("gui");
-  gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-  PluginEvent event;
-  for(bool processing = true; processing;) {
-    FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
-
-    TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex);
-
-    if(event_status == FuriStatusOk) {
-      if(event.type == EventTypeKey) {
-        if(event.input.type == InputTypeShort) {
-          // push events
-          switch(event.input.key) {
-            case InputKeyUp:
-              if (tuning_fork_state->page == Notes) {
-                increase_volume(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-              break;
-            case InputKeyDown:
-              if (tuning_fork_state->page == Notes) {
-                decrease_volume(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-              break;
-            case InputKeyRight:
-              if (tuning_fork_state->page == Tunings) {
-                next_tuning(tuning_fork_state);
-              } else {
-                next_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-              break;
-            case InputKeyLeft:
-              if (tuning_fork_state->page == Tunings) {
-                prev_tuning(tuning_fork_state);
-              } else {
-                prev_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-              break;
-            case InputKeyOk:
-              if (tuning_fork_state->page == Tunings) {
-                tuning_fork_state->page = Notes;
-              } else {
-                tuning_fork_state->playing = !tuning_fork_state->playing;
-                if (tuning_fork_state->playing) {
-                  play(tuning_fork_state);
-                } else {
-                  stop();
-                }
-              }
-              break;
-            case InputKeyBack:
-              if (tuning_fork_state->page == Tunings) {
-                processing = false;
-              } else {
-                tuning_fork_state->playing = false;
-                tuning_fork_state->current_tuning_note_index = 0;
-                stop();
-                tuning_fork_state->page = Tunings;
-              }
-              break;
-          }
-        } else if (event.input.type == InputTypeLong) {
-          // hold events
-          switch(event.input.key) {
-            case InputKeyUp:
-              break;
-            case InputKeyDown:
-              break;
-            case InputKeyRight:
-              if (tuning_fork_state->page == Tunings) {
-                next_tuning(tuning_fork_state);
-              } else {
-                next_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-
-              break;
-            case InputKeyLeft:
-              if (tuning_fork_state->page == Tunings) {
-                prev_tuning(tuning_fork_state);
-              } else {
-                prev_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-
-              break;
-            case InputKeyOk:
-              break;
-            case InputKeyBack:
-              if (tuning_fork_state->page == Tunings) {
-                processing = false;
-              } else {
-                tuning_fork_state->playing = false;
-                stop();
-                tuning_fork_state->page = Tunings;
-                tuning_fork_state->current_tuning_note_index = 0;
-              }
-              break;
-          }
-        } else if (event.input.type == InputTypeRepeat) {
-          // repeat events
-          switch(event.input.key) {
-            case InputKeyUp:
-              break;
-            case InputKeyDown:
-              break;
-            case InputKeyRight:
-              if (tuning_fork_state->page == Tunings) {
-                next_tuning(tuning_fork_state);
-              } else {
-                next_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
-                }
-              }
-
-              break;
-            case InputKeyLeft:
-              if (tuning_fork_state->page == Tunings) {
-                prev_tuning(tuning_fork_state);
-              } else {
-                prev_note(tuning_fork_state);
-                if (tuning_fork_state->playing) {
-                  replay(tuning_fork_state);
+    tuning_fork_state_init(tuning_fork_state);
+
+    tuning_fork_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+    if(!tuning_fork_state->mutex) {
+        FURI_LOG_E("TuningFork", "cannot create mutex\r\n");
+        free(tuning_fork_state);
+        furi_message_queue_free(event_queue);
+        return 255;
+    }
+
+    // Set system callbacks
+    ViewPort* view_port = view_port_alloc();
+    view_port_draw_callback_set(view_port, render_callback, tuning_fork_state);
+    view_port_input_callback_set(view_port, input_callback, event_queue);
+
+    Gui* gui = furi_record_open("gui");
+    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+    PluginEvent event;
+    for(bool processing = true; processing;) {
+        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+
+        furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever);
+
+        if(event_status == FuriStatusOk) {
+            if(event.type == EventTypeKey) {
+                if(event.input.type == InputTypeShort) {
+                    // push events
+                    switch(event.input.key) {
+                    case InputKeyUp:
+                        if(tuning_fork_state->page == Notes) {
+                            increase_volume(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+                        break;
+                    case InputKeyDown:
+                        if(tuning_fork_state->page == Notes) {
+                            decrease_volume(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+                        break;
+                    case InputKeyRight:
+                        if(tuning_fork_state->page == Tunings) {
+                            next_tuning(tuning_fork_state);
+                        } else {
+                            next_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+                        break;
+                    case InputKeyLeft:
+                        if(tuning_fork_state->page == Tunings) {
+                            prev_tuning(tuning_fork_state);
+                        } else {
+                            prev_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+                        break;
+                    case InputKeyOk:
+                        if(tuning_fork_state->page == Tunings) {
+                            tuning_fork_state->page = Notes;
+                        } else {
+                            tuning_fork_state->playing = !tuning_fork_state->playing;
+                            if(tuning_fork_state->playing) {
+                                play(tuning_fork_state);
+                            } else {
+                                stop();
+                            }
+                        }
+                        break;
+                    case InputKeyBack:
+                        if(tuning_fork_state->page == Tunings) {
+                            processing = false;
+                        } else {
+                            tuning_fork_state->playing = false;
+                            tuning_fork_state->current_tuning_note_index = 0;
+                            stop();
+                            tuning_fork_state->page = Tunings;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } else if(event.input.type == InputTypeLong) {
+                    // hold events
+                    switch(event.input.key) {
+                    case InputKeyUp:
+                        break;
+                    case InputKeyDown:
+                        break;
+                    case InputKeyRight:
+                        if(tuning_fork_state->page == Tunings) {
+                            next_tuning(tuning_fork_state);
+                        } else {
+                            next_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+
+                        break;
+                    case InputKeyLeft:
+                        if(tuning_fork_state->page == Tunings) {
+                            prev_tuning(tuning_fork_state);
+                        } else {
+                            prev_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+
+                        break;
+                    case InputKeyOk:
+                        break;
+                    case InputKeyBack:
+                        if(tuning_fork_state->page == Tunings) {
+                            processing = false;
+                        } else {
+                            tuning_fork_state->playing = false;
+                            stop();
+                            tuning_fork_state->page = Tunings;
+                            tuning_fork_state->current_tuning_note_index = 0;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } else if(event.input.type == InputTypeRepeat) {
+                    // repeat events
+                    switch(event.input.key) {
+                    case InputKeyUp:
+                        break;
+                    case InputKeyDown:
+                        break;
+                    case InputKeyRight:
+                        if(tuning_fork_state->page == Tunings) {
+                            next_tuning(tuning_fork_state);
+                        } else {
+                            next_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+
+                        break;
+                    case InputKeyLeft:
+                        if(tuning_fork_state->page == Tunings) {
+                            prev_tuning(tuning_fork_state);
+                        } else {
+                            prev_note(tuning_fork_state);
+                            if(tuning_fork_state->playing) {
+                                replay(tuning_fork_state);
+                            }
+                        }
+
+                        break;
+                    case InputKeyOk:
+                        break;
+                    case InputKeyBack:
+                        if(tuning_fork_state->page == Tunings) {
+                            processing = false;
+                        } else {
+                            tuning_fork_state->playing = false;
+                            stop();
+                            tuning_fork_state->page = Tunings;
+                            tuning_fork_state->current_tuning_note_index = 0;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
                 }
-              }
-
-              break;
-            case InputKeyOk:
-              break;
-            case InputKeyBack:
-              if (tuning_fork_state->page == Tunings) {
-                processing = false;
-              } else {
-                tuning_fork_state->playing = false;
-                stop();
-                tuning_fork_state->page = Tunings;
-                tuning_fork_state->current_tuning_note_index = 0;
-              }
-              break;
-          }
+            }
         }
-      }
-    } else {
-      FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout");
-    }
 
-    view_port_update(view_port);
-    release_mutex(&state_mutex, tuning_fork_state);
-  }
+        furi_mutex_release(tuning_fork_state->mutex);
+        view_port_update(view_port);
+    }
 
-  view_port_enabled_set(view_port, false);
-  gui_remove_view_port(gui, view_port);
-  furi_record_close("gui");
-  view_port_free(view_port);
-  furi_message_queue_free(event_queue);
-  delete_mutex(&state_mutex);
-  furi_record_close(RECORD_NOTIFICATION);
-  free(tuning_fork_state);
+    view_port_enabled_set(view_port, false);
+    gui_remove_view_port(gui, view_port);
+    furi_record_close("gui");
+    view_port_free(view_port);
+    furi_message_queue_free(event_queue);
+    furi_mutex_free(tuning_fork_state->mutex);
+    furi_record_close(RECORD_NOTIFICATION);
+    free(tuning_fork_state);
 
-  return 0;
+    return 0;
 }

+ 120 - 156
tunings.h

@@ -1,198 +1,162 @@
+#ifndef TUNINGS_H
+#define TUNINGS_H
+
 #include "notes.h"
+#include <stdint.h>
 
-#ifndef TUNINGS
-#define TUNINGS
+#define MAX_LABEL_LENGTH     20
+#define MAX_NOTES_PER_TUNING 20
 
 typedef struct {
-  char label[20];
-  float frequency;
+    char label[MAX_LABEL_LENGTH];
+    float frequency;
 } NOTE;
 
 typedef struct {
-  char label[20];
-  int notes_length;
-  NOTE notes[20];
+    char label[MAX_LABEL_LENGTH];
+    uint8_t notes_length;
+    NOTE notes[MAX_NOTES_PER_TUNING];
 } TUNING;
 
 const TUNING TuningForks = {
-  "Tuning forks", 6, {
-    { "Common A4 (440)", 440.00f},
-    { "Sarti's A4 (436)", 436.00f},
-    { "1858 A4 (435)", 435.00f},
-    { "Verdi's A4 (432)", 432.00f},
-    { "1750-1820 A4 (423.5)", 423.50f},
-    { "Verdi's C4 (256.00)", 256.00f},
-  }
-};
+    "Tuning forks",
+    6,
+    {
+        {"Common A4 (440)", 440.00f},
+        {"Sarti's A4 (436)", 436.00f},
+        {"1858 A4 (435)", 435.00f},
+        {"Verdi's A4 (432)", 432.00f},
+        {"1750-1820 A4 (423.5)", 423.50f},
+        {"Verdi's C4 (256.00)", 256.00f},
+    }};
 
 const TUNING ScientificPitch = {
-  "Scientific pitch", 12, {
-    { "C0 (16Hz)", 16.0f},
-    { "C1 (32Hz)", 32.0f},
-    { "C2 (64Hz)", 64.0f},
-    { "C3 (128Hz)", 128.0f},
-    { "C4 (256Hz)", 256.0f},
-    { "C5 (512Hz)", 512.0f},
-    { "C6 (1024Hz)", 1024.0f},
-    { "C7 (2048Hz)", 2048.0f},
-    { "C8 (4096Hz)", 4096.0f},
-    { "C9 (8192Hz)", 8192.0f},
-    { "C10 (16384Hz)", 16384.0f},
-    { "C11 (32768Hz)", 32768.0f}
-  }
-};
+    "Scientific pitch",
+    12,
+    {{"C0 (16Hz)", 16.0f},
+     {"C1 (32Hz)", 32.0f},
+     {"C2 (64Hz)", 64.0f},
+     {"C3 (128Hz)", 128.0f},
+     {"C4 (256Hz)", 256.0f},
+     {"C5 (512Hz)", 512.0f},
+     {"C6 (1024Hz)", 1024.0f},
+     {"C7 (2048Hz)", 2048.0f},
+     {"C8 (4096Hz)", 4096.0f},
+     {"C9 (8192Hz)", 8192.0f},
+     {"C10 (16384Hz)", 16384.0f},
+     {"C11 (32768Hz)", 32768.0f}}};
 
 const TUNING GuitarStandard6 = {
-  "Guitar Standard 6", 6, {
-    {"String 1", E4},
-    {"String 2", B3},
-    {"String 3", G3},
-    {"String 4", D3},
-    {"String 5", A2},
-    {"String 6", E2}
-  }
-};
+    "Guitar Standard 6",
+    6,
+    {{"String 1", E4},
+     {"String 2", B3},
+     {"String 3", G3},
+     {"String 4", D3},
+     {"String 5", A2},
+     {"String 6", E2}}};
 
 const TUNING GuitarDropD6 = {
-  "Guitar Drop D 6", 6, {
-    {"String 1", E4},
-    {"String 2", B3},
-    {"String 3", G3},
-    {"String 4", D3},
-    {"String 5", A2},
-    {"String 6", D2}
-  }
-};
+    "Guitar Drop D 6",
+    6,
+    {{"String 1", E4},
+     {"String 2", B3},
+     {"String 3", G3},
+     {"String 4", D3},
+     {"String 5", A2},
+     {"String 6", D2}}};
 
 const TUNING GuitarD6 = {
-  "Guitar D 6", 6, {
-    {"String 1", D4},
-    {"String 2", A3},
-    {"String 3", F3},
-    {"String 4", C3},
-    {"String 5", G2},
-    {"String 6", D2}
-  }
-};
+    "Guitar D 6",
+    6,
+    {{"String 1", D4},
+     {"String 2", A3},
+     {"String 3", F3},
+     {"String 4", C3},
+     {"String 5", G2},
+     {"String 6", D2}}};
 
 const TUNING GuitarDropC6 = {
-  "Guitar Drop C 6", 6, {
-    {"String 1", D4},
-    {"String 2", A3},
-    {"String 3", F3},
-    {"String 4", C3},
-    {"String 5", G2},
-    {"String 6", C2}
-  }
-};
+    "Guitar Drop C 6",
+    6,
+    {{"String 1", D4},
+     {"String 2", A3},
+     {"String 3", F3},
+     {"String 4", C3},
+     {"String 5", G2},
+     {"String 6", C2}}};
 
 const TUNING GuitarStandard7 = {
-  "Guitar Standard 7", 7, {
-    {"String 1", E4},
-    {"String 2", B3},
-    {"String 3", G3},
-    {"String 4", D3},
-    {"String 5", A2},
-    {"String 6", E2},
-    {"String 7", B1}
-  }
-};
+    "Guitar Standard 7",
+    7,
+    {{"String 1", E4},
+     {"String 2", B3},
+     {"String 3", G3},
+     {"String 4", D3},
+     {"String 5", A2},
+     {"String 6", E2},
+     {"String 7", B1}}};
 
 const TUNING BassStandard4 = {
-  "Bass Standard 4", 4, {
-    {"String 1", G2},
-    {"String 2", D2},
-    {"String 3", A1},
-    {"String 4", E1}
-  }
-};
+    "Bass Standard 4",
+    4,
+    {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}}};
 
 const TUNING BassStandardTenor4 = {
-  "Bass Stand Tenor 4", 4, {
-    {"String 1", C3},
-    {"String 2", G2},
-    {"String 3", D2},
-    {"String 4", A1}
-  }
-};
+    "Bass Stand Tenor 4",
+    4,
+    {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}}};
 
 const TUNING BassStandard5 = {
-  "Bass Standard 5", 5, {
-    {"String 1", G2},
-    {"String 2", D2},
-    {"String 3", A1},
-    {"String 4", E1},
-    {"String 5", B0}
-  }
-};
+    "Bass Standard 5",
+    5,
+    {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", B0}}};
 
 const TUNING BassStandardTenor5 = {
-  "Bass Stand Tenor 5", 5, {
-    {"String 1", C3},
-    {"String 2", G2},
-    {"String 3", D2},
-    {"String 4", A1},
-    {"String 5", E1}
-  }
-};
+    "Bass Stand Tenor 5",
+    5,
+    {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}, {"String 5", E1}}};
 
 const TUNING BassDropD4 = {
-  "Bass Drop D 4", 4, {
-    {"String 1", G2},
-    {"String 2", D2},
-    {"String 3", A1},
-    {"String 4", D1}
-  }
-};
+    "Bass Drop D 4",
+    4,
+    {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", D1}}};
 
 const TUNING BassD4 = {
-  "Bass D 4", 4, {
-    {"String 1", F2},
-    {"String 2", C2},
-    {"String 3", G1},
-    {"String 4", D1}
-  }
-};
+    "Bass D 4",
+    4,
+    {{"String 1", F2}, {"String 2", C2}, {"String 3", G1}, {"String 4", D1}}};
 
 const TUNING BassDropA5 = {
-  "Bass Drop A 5", 5, {
-    {"String 1", G2},
-    {"String 2", D2},
-    {"String 3", A1},
-    {"String 4", E1},
-    {"String 5", A0}
-  }
-};
+    "Bass Drop A 5",
+    5,
+    {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", A0}}};
 
 const TUNING UkuleleStandard4 = {
-    "Ukulele Standard 4", 4, {
-    {"String 1", A4},
-    {"String 2", E4},
-    {"String 3", C4},
-    {"String 4", G4}
-  }
-};
+    "Ukulele Standard 4",
+    4,
+    {{"String 1", A4}, {"String 2", E4}, {"String 3", C4}, {"String 4", G4}}};
 
 #define TUNINGS_COUNT 15
 
 TUNING TuningList[TUNINGS_COUNT] = {
-  ScientificPitch,
-  TuningForks,
-
-  GuitarStandard6,
-  GuitarDropD6,
-  GuitarD6,
-  GuitarDropC6,
-  GuitarStandard7,
-
-  BassStandard4,
-  BassStandardTenor4,
-  BassStandard5,
-  BassStandardTenor5,
-  BassDropD4,
-  BassD4,
-  BassDropA5,
-  UkuleleStandard4
-};
-
-#endif //TUNINGS
+    ScientificPitch,
+    TuningForks,
+
+    GuitarStandard6,
+    GuitarDropD6,
+    GuitarD6,
+    GuitarDropC6,
+    GuitarStandard7,
+
+    BassStandard4,
+    BassStandardTenor4,
+    BassStandard5,
+    BassStandardTenor5,
+    BassDropD4,
+    BassD4,
+    BassDropA5,
+
+    UkuleleStandard4};
+
+#endif //TUNINGS_H