dtmf_dolphin_dialer.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. elements_multiline_text(
  140. canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
  141. furi_string_free(output);
  142. }
  143. static bool dtmf_dolphin_dialer_input_callback(InputEvent* event, void* context) {
  144. furi_assert(context);
  145. DTMFDolphinDialer* dtmf_dolphin_dialer = context;
  146. bool consumed = false;
  147. if(event->type == InputTypeShort) {
  148. if(event->key == InputKeyRight) {
  149. consumed = dtmf_dolphin_dialer_process_right(dtmf_dolphin_dialer);
  150. } else if(event->key == InputKeyLeft) {
  151. consumed = dtmf_dolphin_dialer_process_left(dtmf_dolphin_dialer);
  152. } else if(event->key == InputKeyUp) {
  153. consumed = dtmf_dolphin_dialer_process_up(dtmf_dolphin_dialer);
  154. } else if(event->key == InputKeyDown) {
  155. consumed = dtmf_dolphin_dialer_process_down(dtmf_dolphin_dialer);
  156. }
  157. } else if(event->key == InputKeyOk) {
  158. consumed = dtmf_dolphin_dialer_process_ok(dtmf_dolphin_dialer, event);
  159. }
  160. return consumed;
  161. }
  162. static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  163. with_view_model(
  164. dtmf_dolphin_dialer->view,
  165. DTMFDolphinDialerModel * model,
  166. {
  167. uint8_t span = 0;
  168. uint8_t cursor = model->row;
  169. while(span == 0 && cursor > 0) {
  170. cursor--;
  171. span = dtmf_dolphin_get_tone_span(cursor, model->col);
  172. }
  173. if(span != 0) {
  174. model->row = cursor;
  175. }
  176. },
  177. true);
  178. return true;
  179. }
  180. static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  181. uint8_t max_rows = 0;
  182. uint8_t max_cols = 0;
  183. uint8_t max_span = 0;
  184. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  185. with_view_model(
  186. dtmf_dolphin_dialer->view,
  187. DTMFDolphinDialerModel * model,
  188. {
  189. uint8_t span = 0;
  190. uint8_t cursor = model->row;
  191. while(span == 0 && cursor < max_rows - 1) {
  192. cursor++;
  193. span = dtmf_dolphin_get_tone_span(cursor, model->col);
  194. }
  195. if(span != 0) {
  196. model->row = cursor;
  197. }
  198. },
  199. true);
  200. return true;
  201. }
  202. static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  203. with_view_model(
  204. dtmf_dolphin_dialer->view,
  205. DTMFDolphinDialerModel * model,
  206. {
  207. uint8_t span = 0;
  208. uint8_t cursor = model->col;
  209. while(span == 0 && cursor > 0) {
  210. cursor--;
  211. span = dtmf_dolphin_get_tone_span(model->row, cursor);
  212. }
  213. if(span != 0) {
  214. model->col = cursor;
  215. }
  216. },
  217. true);
  218. return true;
  219. }
  220. static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  221. uint8_t max_rows = 0;
  222. uint8_t max_cols = 0;
  223. uint8_t max_span = 0;
  224. dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
  225. with_view_model(
  226. dtmf_dolphin_dialer->view,
  227. DTMFDolphinDialerModel * model,
  228. {
  229. uint8_t span = 0;
  230. uint8_t cursor = model->col;
  231. while(span == 0 && cursor < max_cols - 1) {
  232. cursor++;
  233. span = dtmf_dolphin_get_tone_span(model->row, cursor);
  234. }
  235. if(span != 0) {
  236. model->col = cursor;
  237. }
  238. },
  239. true);
  240. return true;
  241. }
  242. static bool
  243. dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
  244. bool consumed = false;
  245. with_view_model(
  246. dtmf_dolphin_dialer->view,
  247. DTMFDolphinDialerModel * model,
  248. {
  249. if(event->type == InputTypePress) {
  250. model->playing = dtmf_dolphin_audio_play_tones(
  251. model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
  252. } else if(event->type == InputTypeRelease) {
  253. model->playing = !dtmf_dolphin_audio_stop_tones();
  254. }
  255. },
  256. true);
  257. return consumed;
  258. }
  259. static void dtmf_dolphin_dialer_enter_callback(void* context) {
  260. furi_assert(context);
  261. DTMFDolphinDialer* dtmf_dolphin_dialer = context;
  262. with_view_model(
  263. dtmf_dolphin_dialer->view,
  264. DTMFDolphinDialerModel * model,
  265. {
  266. model->col = 0;
  267. model->row = 0;
  268. model->section = 0;
  269. model->freq1 = 0.0;
  270. model->freq2 = 0.0;
  271. model->playing = false;
  272. },
  273. true);
  274. }
  275. DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
  276. DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
  277. dtmf_dolphin_dialer->view = view_alloc();
  278. view_allocate_model(
  279. dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
  280. with_view_model(
  281. dtmf_dolphin_dialer->view,
  282. DTMFDolphinDialerModel * model,
  283. {
  284. model->col = 0;
  285. model->row = 0;
  286. model->section = 0;
  287. model->freq1 = 0.0;
  288. model->freq2 = 0.0;
  289. model->playing = false;
  290. },
  291. true);
  292. view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
  293. view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
  294. view_set_input_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_input_callback);
  295. view_set_enter_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_enter_callback);
  296. return dtmf_dolphin_dialer;
  297. }
  298. void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  299. furi_assert(dtmf_dolphin_dialer);
  300. view_free(dtmf_dolphin_dialer->view);
  301. free(dtmf_dolphin_dialer);
  302. }
  303. View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
  304. furi_assert(dtmf_dolphin_dialer);
  305. return dtmf_dolphin_dialer->view;
  306. }