dtmf_dolphin_dialer.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #include "dtmf_dolphin_dialer.h"
  2. #include <gui/elements.h>
  3. typedef struct DTMFDolphinDialer {
  4. View* view;
  5. DTMFDolphinDialerOkCallback callback;
  6. void* context;
  7. } DTMFDolphinDialer;
  8. typedef struct {
  9. DTMFDolphinToneSection section;
  10. uint8_t row;
  11. uint8_t col;
  12. float freq1;
  13. float freq2;
  14. bool playing;
  15. uint16_t pulses;
  16. uint16_t pulse_ms;
  17. uint16_t gap_ms;
  18. } DTMFDolphinDialerModel;
  19. static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer);
  20. static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer);
  21. static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer);
  22. static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer);
  23. static bool
  24. dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
  25. void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) {
  26. uint8_t left = DTMF_DOLPHIN_NUMPAD_X + // ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
  27. (col * DTMF_DOLPHIN_BUTTON_WIDTH);
  28. // (col * DTMF_DOLPHIN_BUTTON_PADDING);
  29. uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + // ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
  30. (row * DTMF_DOLPHIN_BUTTON_HEIGHT);
  31. // (row * DTMF_DOLPHIN_BUTTON_PADDING);
  32. uint8_t span = dtmf_dolphin_get_tone_span(row, col);
  33. if(span == 0) {
  34. return;
  35. }
  36. canvas_set_color(canvas, ColorBlack);
  37. if(invert)
  38. canvas_draw_rbox(
  39. canvas,
  40. left,
  41. top,
  42. (DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
  43. DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
  44. 2);
  45. else
  46. canvas_draw_rframe(
  47. canvas,
  48. left,
  49. top,
  50. (DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
  51. DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
  52. 2);
  53. if(invert) canvas_invert_color(canvas);
  54. canvas_set_font(canvas, FontSecondary);
  55. // canvas_set_color(canvas, invert ? ColorWhite : ColorBlack);
  56. canvas_draw_str_aligned(
  57. canvas,
  58. left - 1 + (int)((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
  59. top + (int)(DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
  60. AlignCenter,
  61. AlignCenter,
  62. dtmf_dolphin_data_get_tone_name(row, col));
  63. if(invert) canvas_invert_color(canvas);
  64. }
  65. void draw_dialer(Canvas* canvas, void* _model) {
  66. DTMFDolphinDialerModel* model = _model;
  67. uint8_t max_rows;
  68. uint8_t max_cols;
  69. uint8_t max_span;
  70. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  71. canvas_set_font(canvas, FontSecondary);
  72. for(int r = 0; r < max_rows; r++) {
  73. for(int c = 0; c < max_cols; c++) {
  74. if(model->row == r && model->col == c)
  75. draw_button(canvas, r, c, true);
  76. else
  77. draw_button(canvas, r, c, false);
  78. }
  79. }
  80. }
  81. void update_frequencies(DTMFDolphinDialerModel* model) {
  82. dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
  83. dtmf_dolphin_data_get_filter_data(
  84. &model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
  85. }
  86. static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
  87. DTMFDolphinDialerModel* model = _model;
  88. if(model->playing) {
  89. // Leverage the prioritized draw callback to handle
  90. // the DMA so that it doesn't skip.
  91. dtmf_dolphin_audio_handle_tick();
  92. // Don't do any drawing if audio is playing.
  93. canvas_set_font(canvas, FontPrimary);
  94. elements_multiline_text_aligned(
  95. canvas,
  96. canvas_width(canvas) / 2,
  97. canvas_height(canvas) / 2,
  98. AlignCenter,
  99. AlignCenter,
  100. "Playing Tones");
  101. return;
  102. }
  103. update_frequencies(model);
  104. uint8_t max_rows = 0;
  105. uint8_t max_cols = 0;
  106. uint8_t max_span = 0;
  107. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  108. canvas_set_font(canvas, FontPrimary);
  109. elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name());
  110. canvas_draw_line(
  111. canvas,
  112. (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
  113. 0,
  114. (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
  115. canvas_height(canvas));
  116. elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail");
  117. canvas_draw_line(
  118. canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
  119. // elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode");
  120. draw_dialer(canvas, model);
  121. FuriString* output = furi_string_alloc();
  122. if(model->freq1 && model->freq2) {
  123. furi_string_cat_printf(
  124. output,
  125. "Dual Tone\nF1: %u Hz\nF2: %u Hz\n",
  126. (unsigned int)model->freq1,
  127. (unsigned int)model->freq2);
  128. } else if(model->freq1) {
  129. furi_string_cat_printf(output, "Single Tone\nF: %u Hz\n", (unsigned int)model->freq1);
  130. }
  131. canvas_set_font(canvas, FontSecondary);
  132. canvas_set_color(canvas, ColorBlack);
  133. if(model->pulse_ms) {
  134. furi_string_cat_printf(output, "P: %u * %u ms\n", model->pulses, model->pulse_ms);
  135. }
  136. if(model->gap_ms) {
  137. furi_string_cat_printf(output, "Gaps: %u ms\n", model->gap_ms);
  138. }
  139. furi_string_cat_printf(output, "GPIO: PA6");
  140. elements_multiline_text(
  141. canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
  142. furi_string_free(output);
  143. }
  144. static bool dtmf_dolphin_dialer_input_callback(InputEvent* event, void* context) {
  145. furi_assert(context);
  146. DTMFDolphinDialer* dtmf_dolphin_dialer = context;
  147. bool consumed = false;
  148. if(event->type == InputTypeShort) {
  149. if(event->key == InputKeyRight) {
  150. consumed = dtmf_dolphin_dialer_process_right(dtmf_dolphin_dialer);
  151. } else if(event->key == InputKeyLeft) {
  152. consumed = dtmf_dolphin_dialer_process_left(dtmf_dolphin_dialer);
  153. } else if(event->key == InputKeyUp) {
  154. consumed = dtmf_dolphin_dialer_process_up(dtmf_dolphin_dialer);
  155. } else if(event->key == InputKeyDown) {
  156. consumed = dtmf_dolphin_dialer_process_down(dtmf_dolphin_dialer);
  157. }
  158. } else if(event->key == InputKeyOk) {
  159. consumed = dtmf_dolphin_dialer_process_ok(dtmf_dolphin_dialer, event);
  160. }
  161. return consumed;
  162. }
  163. static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  164. with_view_model(
  165. dtmf_dolphin_dialer->view,
  166. DTMFDolphinDialerModel * model,
  167. {
  168. uint8_t span = 0;
  169. uint8_t cursor = model->row;
  170. while(span == 0 && cursor > 0) {
  171. cursor--;
  172. span = dtmf_dolphin_get_tone_span(cursor, model->col);
  173. }
  174. if(span != 0) {
  175. model->row = cursor;
  176. }
  177. },
  178. true);
  179. return true;
  180. }
  181. static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  182. uint8_t max_rows = 0;
  183. uint8_t max_cols = 0;
  184. uint8_t max_span = 0;
  185. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  186. with_view_model(
  187. dtmf_dolphin_dialer->view,
  188. DTMFDolphinDialerModel * model,
  189. {
  190. uint8_t span = 0;
  191. uint8_t cursor = model->row;
  192. while(span == 0 && cursor < max_rows - 1) {
  193. cursor++;
  194. span = dtmf_dolphin_get_tone_span(cursor, model->col);
  195. }
  196. if(span != 0) {
  197. model->row = cursor;
  198. }
  199. },
  200. true);
  201. return true;
  202. }
  203. static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  204. with_view_model(
  205. dtmf_dolphin_dialer->view,
  206. DTMFDolphinDialerModel * model,
  207. {
  208. uint8_t span = 0;
  209. uint8_t cursor = model->col;
  210. while(span == 0 && cursor > 0) {
  211. cursor--;
  212. span = dtmf_dolphin_get_tone_span(model->row, cursor);
  213. }
  214. if(span != 0) {
  215. model->col = cursor;
  216. }
  217. },
  218. true);
  219. return true;
  220. }
  221. static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  222. uint8_t max_rows = 0;
  223. uint8_t max_cols = 0;
  224. uint8_t max_span = 0;
  225. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  226. with_view_model(
  227. dtmf_dolphin_dialer->view,
  228. DTMFDolphinDialerModel * model,
  229. {
  230. uint8_t span = 0;
  231. uint8_t cursor = model->col;
  232. while(span == 0 && cursor < max_cols - 1) {
  233. cursor++;
  234. span = dtmf_dolphin_get_tone_span(model->row, cursor);
  235. }
  236. if(span != 0) {
  237. model->col = cursor;
  238. }
  239. },
  240. true);
  241. return true;
  242. }
  243. static bool
  244. dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
  245. bool consumed = false;
  246. with_view_model(
  247. dtmf_dolphin_dialer->view,
  248. DTMFDolphinDialerModel * model,
  249. {
  250. if(event->type == InputTypePress) {
  251. model->playing = dtmf_dolphin_audio_play_tones(
  252. model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
  253. } else if(event->type == InputTypeRelease) {
  254. model->playing = !dtmf_dolphin_audio_stop_tones();
  255. }
  256. },
  257. true);
  258. return consumed;
  259. }
  260. static void dtmf_dolphin_dialer_enter_callback(void* context) {
  261. furi_assert(context);
  262. DTMFDolphinDialer* dtmf_dolphin_dialer = context;
  263. with_view_model(
  264. dtmf_dolphin_dialer->view,
  265. DTMFDolphinDialerModel * model,
  266. {
  267. model->col = 0;
  268. model->row = 0;
  269. model->section = 0;
  270. model->freq1 = 0.0;
  271. model->freq2 = 0.0;
  272. model->playing = false;
  273. },
  274. true);
  275. }
  276. DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
  277. DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
  278. dtmf_dolphin_dialer->view = view_alloc();
  279. view_allocate_model(
  280. dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
  281. with_view_model(
  282. dtmf_dolphin_dialer->view,
  283. DTMFDolphinDialerModel * model,
  284. {
  285. model->col = 0;
  286. model->row = 0;
  287. model->section = 0;
  288. model->freq1 = 0.0;
  289. model->freq2 = 0.0;
  290. model->playing = false;
  291. },
  292. true);
  293. view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
  294. view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
  295. view_set_input_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_input_callback);
  296. view_set_enter_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_enter_callback);
  297. return dtmf_dolphin_dialer;
  298. }
  299. void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  300. furi_assert(dtmf_dolphin_dialer);
  301. view_free(dtmf_dolphin_dialer->view);
  302. free(dtmf_dolphin_dialer);
  303. }
  304. View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  305. furi_assert(dtmf_dolphin_dialer);
  306. return dtmf_dolphin_dialer->view;
  307. }