diff --git a/README.md b/README.md index 9f26d36ab..4ad36f7f5 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ## DTMF Dolphin -DTMF (Dual-Tone Multi-Frequency) dialer, and future Bluebox and Redbox for the Flipper Zero. +DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and future Redbox. -Documentation and code completion pending. This is a work in progress. +Now in a release-ready state for both Dialer and Bluebox functionality. Redbox functionality awaits some changes for modulation of pitch. -Warning: may induce feelings of nostalgia and/or actual timetravel to the '80s/'90s. \ No newline at end of file +Please note that using the current tone output method, the 2600 tone is scaled about 33 Hz higher than it should be. This is a limitation of the current sample rate. \ No newline at end of file diff --git a/application.fam b/application.fam index 977b37349..ea40be908 100644 --- a/application.fam +++ b/application.fam @@ -8,6 +8,6 @@ App( "gui", "dialogs", ], - stack_size=4 * 1024, + stack_size=8 * 1024, order=20, ) diff --git a/dtmf_dolphin.c b/dtmf_dolphin.c index 0833ba6f7..9643566d6 100644 --- a/dtmf_dolphin.c +++ b/dtmf_dolphin.c @@ -19,29 +19,11 @@ static void dtmf_dolphin_app_tick_event_callback(void* context) { furi_assert(context); DTMFDolphinApp* app = context; - // Needed to handle queueing to ISR and prioritization of audio - if (app->player.playing) { - dtmf_dolphin_player_handle_tick(); - } else { - scene_manager_handle_tick_event(app->scene_manager); - } + scene_manager_handle_tick_event(app->scene_manager); } static DTMFDolphinApp* app_alloc() { DTMFDolphinApp* app = malloc(sizeof(DTMFDolphinApp)); - app->player.half_samples = 4 * 1024; - app->player.sample_count = 8 * 1024; - app->player.sample_buffer = malloc(sizeof(uint16_t) * app->player.sample_count); - app->player.buffer_buffer = malloc(sizeof(uint8_t) * app->player.sample_count); - app->player.wf1_period = 0; - app->player.wf2_period = 0; - app->player.wf1_freq = 0; - app->player.wf2_freq = 0; - app->player.wf1_pos = 0; - app->player.wf2_pos = 0; - app->player.queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinEvent)); - app->player.volume = 2.0f; - app->player.playing = false; app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); @@ -70,22 +52,6 @@ static DTMFDolphinApp* app_alloc() { DTMFDolphinViewDialer, dtmf_dolphin_dialer_get_view(app->dtmf_dolphin_dialer)); - app->dtmf_dolphin_bluebox = dtmf_dolphin_bluebox_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DTMFDolphinViewBluebox, - dtmf_dolphin_bluebox_get_view(app->dtmf_dolphin_bluebox)); - - app->dtmf_dolphin_play = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DTMFDolphinViewPlay, - widget_get_view(app->dtmf_dolphin_play)); - - // app->dialer_button_panel = button_panel_alloc(); - // app->bluebox_button_panel = button_panel_alloc(); - // app->redbox_button_panel = button_panel_alloc(); - app->notification = furi_record_open(RECORD_NOTIFICATION); notification_message(app->notification, &sequence_display_backlight_enforce_on); @@ -97,21 +63,15 @@ static DTMFDolphinApp* app_alloc() { static void app_free(DTMFDolphinApp* app) { furi_assert(app); view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewMainMenu); - view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewBluebox); view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewDialer); - view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewPlay); variable_item_list_free(app->main_menu_list); - dtmf_dolphin_bluebox_free(app->dtmf_dolphin_bluebox); dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer); - widget_free(app->dtmf_dolphin_play); + // widget_free(app->dtmf_dolphin_play); view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - furi_message_queue_free(app->player.queue); - free(app->player.sample_buffer); - // button_panel_free(app->dialer_button_panel); // button_panel_free(app->bluebox_button_panel); // button_panel_free(app->redbox_button_panel); @@ -126,8 +86,6 @@ static void app_free(DTMFDolphinApp* app) { int32_t dtmf_dolphin_app(void *p) { UNUSED(p); DTMFDolphinApp* app = app_alloc(); - - dtmf_dolphin_player_init(&(app->player)); view_dispatcher_run(app->view_dispatcher); diff --git a/dtmf_dolphin_audio.c b/dtmf_dolphin_audio.c new file mode 100644 index 000000000..6d6341bf1 --- /dev/null +++ b/dtmf_dolphin_audio.c @@ -0,0 +1,188 @@ +#include "dtmf_dolphin_audio.h" + +DTMFDolphinAudio *current_player; + +static void dtmf_dolphin_audio_dma_isr(void* ctx) { + FuriMessageQueue *event_queue = ctx; + + if (LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + + DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer}; + furi_message_queue_put(event_queue, &event, 0); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + + DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer}; + furi_message_queue_put(event_queue, &event, 0); + } +} + +void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) { + for (size_t i = 0; i < player->buffer_length; i++) { + player->sample_buffer[i] = 0; + } +} + +DTMFDolphinOsc* dtmf_dolphin_osc_alloc() { + DTMFDolphinOsc *osc = malloc(sizeof(DTMFDolphinOsc)); + osc->cached_freq = 0; + osc->offset = 0; + osc->period = 0; + osc->lookup_table = NULL; + return osc; +} + +DTMFDolphinAudio* dtmf_dolphin_audio_alloc() { + DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio)); + player->buffer_length = SAMPLE_BUFFER_LENGTH; + player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2; + player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length); + player->osc1 = dtmf_dolphin_osc_alloc(); + player->osc2 = dtmf_dolphin_osc_alloc(); + player->volume = 1.0f; + player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent)); + dtmf_dolphin_audio_clear_samples(player); + + return player; +} + +size_t calc_waveform_period(float freq) { + if (!freq) { + return 0; + } + // DMA Rate calculation, thanks to Dr_Zlo + float dma_rate = CPU_CLOCK_FREQ \ + / 2 \ + / DTMF_DOLPHIN_HAL_DMA_PRESCALER \ + / (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1); + + // Using a constant scaling modifier, which likely represents + // the combined system overhead and isr latency. + return (uint16_t) dma_rate * 2 / freq * 0.801923; +} + +void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) { + if (osc->lookup_table != NULL) { + free(osc->lookup_table); + } + osc->offset = 0; + osc->cached_freq = freq; + osc->period = calc_waveform_period(freq); + if (!osc->period) { + osc->lookup_table = NULL; + return; + } + osc->lookup_table = malloc(sizeof(float) * osc->period); + + for (size_t i = 0; i < osc->period; i++) { + osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1; + } +} + +float sample_frame(DTMFDolphinOsc* osc) { + float frame = 0.0; + + if (osc->period) { + frame = osc->lookup_table[osc->offset]; + osc->offset = (osc->offset + 1) % osc->period; + } + + return frame; +} + +void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) { + furi_message_queue_free(player->queue); + dtmf_dolphin_osc_free(player->osc1); + dtmf_dolphin_osc_free(player->osc2); + free(player->sample_buffer); + free(player); + current_player = NULL; +} + +void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) { + if (osc->lookup_table != NULL) { + free(osc->lookup_table); + } + free(osc); +} + +bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { + uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index]; + + for (size_t i = 0; i < player->half_buffer_length; i++) { + float data = 0; + if (player->osc2->period) { + data = \ + (sample_frame(player->osc1) / 2) + \ + (sample_frame(player->osc2) / 2); + } else { + data = (sample_frame(player->osc1)); + } + data *= player->volume; + data *= UINT8_MAX / 2; // scale -128..127 + data += UINT8_MAX / 2; // to unsigned + + if(data < 0) { + data = 0; + } + + if(data > 255) { + data = 255; + } + + sample_buffer_start[i] = data; + } + + return true; +} + +bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) { + current_player = dtmf_dolphin_audio_alloc(); + + osc_generate_lookup_table(current_player->osc1, freq1); + osc_generate_lookup_table(current_player->osc2, freq2); + + generate_waveform(current_player, 0); + generate_waveform(current_player, current_player->half_buffer_length); + + dtmf_dolphin_speaker_init(); + dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue); + + dtmf_dolphin_dma_start(); + dtmf_dolphin_speaker_start(); + return true; +} + +bool dtmf_dolphin_audio_stop_tones() { + dtmf_dolphin_speaker_stop(); + dtmf_dolphin_dma_stop(); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + + dtmf_dolphin_audio_free(current_player); + + return true; +} + +bool dtmf_dolphin_audio_handle_tick() { + bool handled = false; + + if (current_player) { + DTMFDolphinCustomEvent event; + if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) { + if(event.type == DTMFDolphinEventDMAHalfTransfer) { + generate_waveform(current_player, 0); + handled = true; + } else if (event.type == DTMFDolphinEventDMAFullTransfer) { + generate_waveform(current_player, current_player->half_buffer_length); + handled = true; + } + } + } + return handled; +} \ No newline at end of file diff --git a/dtmf_dolphin_audio.h b/dtmf_dolphin_audio.h new file mode 100644 index 000000000..071323a6c --- /dev/null +++ b/dtmf_dolphin_audio.h @@ -0,0 +1,40 @@ +#pragma once +// #include "dtmf_dolphin_i.h" +#include "dtmf_dolphin_event.h" +#include "dtmf_dolphin_hal.h" + +#define SAMPLE_BUFFER_LENGTH 8192 +#define PERIOD_2_PI 6.2832 +#define CPU_CLOCK_FREQ 64000000 + +typedef struct { + float cached_freq; + size_t period; + float* lookup_table; + uint16_t offset; +} DTMFDolphinOsc; + +typedef struct { + size_t buffer_length; + size_t half_buffer_length; + uint8_t *buffer_buffer; + uint16_t *sample_buffer; + float volume; + FuriMessageQueue *queue; + DTMFDolphinOsc *osc1; + DTMFDolphinOsc *osc2; +} DTMFDolphinAudio; + +DTMFDolphinOsc* dtmf_dolphin_osc_alloc(); + +DTMFDolphinAudio* dtmf_dolphin_audio_alloc(); + +void dtmf_dolphin_audio_free(DTMFDolphinAudio* player); + +void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc); + +bool dtmf_dolphin_audio_play_tones(float freq1, float freq2); + +bool dtmf_dolphin_audio_stop_tones(); + +bool dtmf_dolphin_audio_handle_tick(); diff --git a/dtmf_dolphin_data.c b/dtmf_dolphin_data.c new file mode 100644 index 000000000..d0a85800c --- /dev/null +++ b/dtmf_dolphin_data.c @@ -0,0 +1,196 @@ +#include "dtmf_dolphin_data.h" + +typedef struct { + const uint8_t row; + const uint8_t col; + const uint8_t span; +} DTMFDolphinTonePos; + +typedef struct { + const char *name; + const float frequency_1; + const float frequency_2; + const DTMFDolphinTonePos pos; + const uint16_t pulses; // for Redbox + const uint16_t pulse_ms; // for Redbox + const uint16_t gap_duration; // for Redbox +} DTMFDolphinTones; + +typedef struct { + const char* name; + DTMFDolphinToneSection block; + uint8_t tone_count; + DTMFDolphinTones tones[DTMF_DOLPHIN_MAX_TONE_COUNT]; +} DTMFDolphinSceneData; + +DTMFDolphinSceneData DTMFDolphinSceneDataDialer = { + .name = "Dialer", + .block = DTMF_DOLPHIN_TONE_BLOCK_DIALER, + .tone_count = 16, + .tones = { + {"1", 697.0, 1209.0, {0, 0, 1}, 0, 0, 0}, + {"2", 697.0, 1336.0, {0, 1, 1}, 0, 0, 0}, + {"3", 697.0, 1477.0, {0, 2, 1}, 0, 0, 0}, + {"A", 697.0, 1633.0, {0, 3, 1}, 0, 0, 0}, + {"4", 770.0, 1209.0, {1, 0, 1}, 0, 0, 0}, + {"5", 770.0, 1336.0, {1, 1, 1}, 0, 0, 0}, + {"6", 770.0, 1477.0, {1, 2, 1}, 0, 0, 0}, + {"B", 770.0, 1633.0, {1, 3, 1}, 0, 0, 0}, + {"7", 852.0, 1209.0, {2, 0, 1}, 0, 0, 0}, + {"8", 852.0, 1336.0, {2, 1, 1}, 0, 0, 0}, + {"9", 852.0, 1477.0, {2, 2, 1}, 0, 0, 0}, + {"C", 852.0, 1633.0, {2, 3, 1}, 0, 0, 0}, + {"*", 941.0, 1209.0, {3, 0, 1}, 0, 0, 0}, + {"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0}, + {"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0}, + {"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0}, + } +}; + +DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = { + .name = "Bluebox", + .block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, + .tone_count = 13, + .tones = { + {"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0}, + {"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0}, + {"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0}, + {"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0}, + {"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0}, + {"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0}, + {"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0}, + {"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0}, + {"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0}, + {"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0}, + {"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0}, + {"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0}, + {"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0}, + } +}; + +DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = { + .name = "Redbox (US)", + .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, + .tone_count = 4, + .tones = { + {"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0}, + {"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66}, + {"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33}, + {"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0}, + } +}; + +DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = { + .name = "Redbox (UK)", + .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, + .tone_count = 2, + .tones = { + {"10p", 1000.0, 0.0, {0, 0, 3}, 1, 200, 0}, + {"50p", 1000.0, 0.0, {1, 0, 3}, 1, 350, 0}, + } +}; + +DTMFDolphinSceneData DTMFDolphinSceneDataMisc = { + .name = "Misc", + .block = DTMF_DOLPHIN_TONE_BLOCK_MISC, + .tone_count = 3, + .tones = { + {"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0}, + {"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0}, + {"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0}, + } +}; + +DTMFDolphinToneSection current_section; +DTMFDolphinSceneData *current_scene_data; + +void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) { + current_section = section; + + switch (current_section) + { + case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX: + current_scene_data = &DTMFDolphinSceneDataBluebox; + break; + case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US: + current_scene_data = &DTMFDolphinSceneDataRedboxUS; + break; + case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK: + current_scene_data = &DTMFDolphinSceneDataRedboxUK; + break; + case DTMF_DOLPHIN_TONE_BLOCK_MISC: + current_scene_data = &DTMFDolphinSceneDataMisc; + break; + default: // DTMF_DOLPHIN_TONE_BLOCK_DIALER: + current_scene_data = &DTMFDolphinSceneDataDialer; + break; + } +} + +DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() { + return current_section; +} + +DTMFDolphinSceneData *dtmf_dolphin_data_get_current_scene_data() { + return current_scene_data; +} + +bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t row, uint8_t col) { + for (size_t i = 0; i < current_scene_data->tone_count; i++) { + DTMFDolphinTones tones = current_scene_data->tones[i]; + if (tones.pos.row == row && tones.pos.col == col) { + freq1[0] = tones.frequency_1; + freq2[0] = tones.frequency_2; + return true; + } + } + return false; +} + +const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) { + for (size_t i = 0; i < current_scene_data->tone_count; i++) { + DTMFDolphinTones tones = current_scene_data->tones[i]; + if (tones.pos.row == row && tones.pos.col == col) { + return tones.name; + } + } + return NULL; +} + +const char* dtmf_dolphin_data_get_current_section_name() { + if (current_scene_data) { + return current_scene_data->name; + } + return NULL; +} + +void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span) { + max_rows[0] = 0; + max_cols[0] = 0; + max_span[0] = 0; + uint8_t tmp_rowspan[5] = { 0, 0, 0, 0, 0 }; + for (size_t i = 0; i < current_scene_data->tone_count; i++) { + DTMFDolphinTones tones = current_scene_data->tones[i]; + if (tones.pos.row > max_rows[0]) { + max_rows[0] = tones.pos.row; + } + if (tones.pos.col > max_cols[0]) { + max_cols[0] = tones.pos.col; + } + tmp_rowspan[tones.pos.row] += tones.pos.span; + if (tmp_rowspan[tones.pos.row] > max_span[0]) + max_span[0] = tmp_rowspan[tones.pos.row]; + } + max_rows[0]++; + max_cols[0]++; +} + +uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) { + for (size_t i = 0; i < current_scene_data->tone_count; i++) { + DTMFDolphinTones tones = current_scene_data->tones[i]; + if (tones.pos.row == row && tones.pos.col == col) { + return tones.pos.span; + } + } + return 0; +} \ No newline at end of file diff --git a/dtmf_dolphin_data.h b/dtmf_dolphin_data.h new file mode 100644 index 000000000..65fb9c75e --- /dev/null +++ b/dtmf_dolphin_data.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +#define DTMF_DOLPHIN_MAX_TONE_COUNT 16 + +typedef enum { + DTMF_DOLPHIN_TONE_BLOCK_DIALER, + DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, + DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, + DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, + DTMF_DOLPHIN_TONE_BLOCK_MISC, +} DTMFDolphinToneSection; + +void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section); + +DTMFDolphinToneSection dtmf_dolphin_data_get_current_section(); + +bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t row, uint8_t col); + +const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col); + +const char* dtmf_dolphin_data_get_current_section_name(); + +void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span); + +uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col); \ No newline at end of file diff --git a/dtmf_dolphin_event.h b/dtmf_dolphin_event.h index 26412da79..75f5bb274 100644 --- a/dtmf_dolphin_event.h +++ b/dtmf_dolphin_event.h @@ -3,12 +3,18 @@ typedef enum { DTMFDolphinEventVolumeUp = 0, DTMFDolphinEventVolumeDown, - DTMFDolphinBlueboxOkCB, + DTMFDolphinDialerOkCB, DTMFDolphinEventStartDialer, DTMFDolphinEventStartBluebox, - DTMFDolphinEventStartRedbox, + DTMFDolphinEventStartRedboxUS, + DTMFDolphinEventStartRedboxUK, + DTMFDolphinEventStartMisc, DTMFDolphinEventPlayTones, DTMFDolphinEventStopTones, - DTMFDolphinPlayerEventHalfTransfer, - DTMFDolphinPlayerEventFullTransfer, -} DTMFDolphinEvent; \ No newline at end of file + DTMFDolphinEventDMAHalfTransfer, + DTMFDolphinEventDMAFullTransfer, +} DTMFDolphinEvent; + +typedef struct { + DTMFDolphinEvent type; +} DTMFDolphinCustomEvent; \ No newline at end of file diff --git a/dtmf_dolphin_hal.c b/dtmf_dolphin_hal.c index 4de9860cf..b556c7145 100644 --- a/dtmf_dolphin_hal.c +++ b/dtmf_dolphin_hal.c @@ -2,8 +2,8 @@ void dtmf_dolphin_speaker_init() { LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 4; - TIM_InitStruct.Autoreload = 255; + TIM_InitStruct.Prescaler = DTMF_DOLPHIN_HAL_DMA_PRESCALER; + TIM_InitStruct.Autoreload = DTMF_DOLPHIN_HAL_DMA_AUTORELOAD; LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; diff --git a/dtmf_dolphin_hal.h b/dtmf_dolphin_hal.h index a5dab1a6e..5b426f6a0 100644 --- a/dtmf_dolphin_hal.h +++ b/dtmf_dolphin_hal.h @@ -1,8 +1,6 @@ #pragma once #include #include -#include -#include #include #include #include @@ -11,6 +9,9 @@ #define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 #define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 +#define DTMF_DOLPHIN_HAL_DMA_PRESCALER 4 +#define DTMF_DOLPHIN_HAL_DMA_AUTORELOAD 255 + #ifdef __cplusplus extern "C" { #endif diff --git a/dtmf_dolphin_i.h b/dtmf_dolphin_i.h index c07b8b63e..2a0de1b41 100644 --- a/dtmf_dolphin_i.h +++ b/dtmf_dolphin_i.h @@ -5,26 +5,25 @@ #include #include #include -#include -#include +// #include +// #include #include #include #include #include "dtmf_dolphin_event.h" -#include "dtmf_dolphin_player.h" #include "views/dtmf_dolphin_dialer.h" -#include "views/dtmf_dolphin_bluebox.h" #define TAG "DTMFDolphin" -enum DTMFDolphinItem { - DTMFDolphinItemDialer, - DTMFDolphinItemBluebox, - DTMFDolphinItemRedbox, - DTMFDolphinItemPlay +enum DTMFDolphinSceneState { + DTMFDolphinSceneStateDialer, + DTMFDolphinSceneStateBluebox, + DTMFDolphinSceneStateRedboxUS, + DTMFDolphinSceneStateRedboxUK, + DTMFDolphinSceneStateMisc, }; typedef struct { @@ -32,9 +31,6 @@ typedef struct { SceneManager* scene_manager; VariableItemList* main_menu_list; DTMFDolphinDialer* dtmf_dolphin_dialer; - DTMFDolphinBluebox* dtmf_dolphin_bluebox; - DTMFDolphinPlayer player; - Widget* dtmf_dolphin_play; Gui* gui; // ButtonPanel* dialer_button_panel; @@ -45,8 +41,5 @@ typedef struct { typedef enum { DTMFDolphinViewMainMenu, - DTMFDolphinViewDialer, - DTMFDolphinViewBluebox, - DTMFDolphinViewRedbox, - DTMFDolphinViewPlay, + DTMFDolphinViewDialer } DTMFDolphinView; diff --git a/dtmf_dolphin_player.c b/dtmf_dolphin_player.c deleted file mode 100644 index ead58af0e..000000000 --- a/dtmf_dolphin_player.c +++ /dev/null @@ -1,156 +0,0 @@ -#include "dtmf_dolphin_player.h" - -#define DTMF_DOLPHIN_SAMPLE_RATE (8000) - -typedef struct { - DTMFDolphinEvent type; -} DTMFDolphinPlayerEvent; - -// Keep this here for accessibility in local scope event without context -DTMFDolphinPlayer* player; - -void dtmf_dolphin_dma_isr(void* ctx) { - FuriMessageQueue *event_queue = ctx; - - if (LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); - - DTMFDolphinPlayerEvent event = {.type = DTMFDolphinPlayerEventHalfTransfer}; - furi_message_queue_put(event_queue, &event, 0); - } - - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); - - DTMFDolphinPlayerEvent event = {.type = DTMFDolphinPlayerEventFullTransfer}; - furi_message_queue_put(event_queue, &event, 0); - } -} - -bool dtmf_dolphin_player_init(void* context) { - player = context; - - return false; -} - -void dtmf_dolphin_player_clear_samples() { - for (size_t i = 0; i < player->sample_count; i++) { - player->sample_buffer[i] = 0; - } -} - -bool dtmf_dolphin_player_generate_waveform(size_t index) { - uint16_t* sample_buffer_start = &player->sample_buffer[index]; - if (!player->wf1_freq) - return false; - - // Generate basic sine wave sample to fill sample_count - for (size_t i = 0; i < player->half_samples; i++) { - // float data = sin(i * PERIOD_2_PI / player->wf1_period) + 1; - float data = sin(player->wf1_pos * PERIOD_2_PI / player->wf1_period) + 1; - player->wf1_pos = (player->wf1_pos + 1) % player->wf1_period; - - data *= player->volume; - - // Downmix second tone with the first - if (player->wf2_freq) { - data /= 2; - - float data_2 = sin(player->wf2_pos * PERIOD_2_PI / player->wf2_period) + 1; - player->wf2_pos = (player->wf2_pos + 1) % player->wf2_period; - - data_2 *= player->volume / 2; - - data += data_2; - } - - data = tanhf(data); - - data *= UINT8_MAX / 2; // scale -128..127 - data += UINT8_MAX / 2; // to unsigned - - if(data < 0) { - data = 0; - } - - if(data > 255) { - data = 255; - } - - player->buffer_buffer[i] = data; - sample_buffer_start[i] = data; - } - - return true; -} - -bool dtmf_dolphin_player_play_tones(float *freq) { - player->wf1_pos = 0; - player->wf2_pos = 0; - player->wf1_freq = 0; - player->wf2_freq = 0; - player->wf1_period = 0; - player->wf2_period = 0; - if (freq[0]) { - player->wf1_freq = freq[0]; - player->wf1_period = player->sample_count / freq[0] * 4; - } - if (freq[1]) { - player->wf2_freq = freq[1]; - player->wf2_period = player->sample_count / freq[1] * 4; - } - dtmf_dolphin_player_clear_samples(); - - dtmf_dolphin_player_generate_waveform(0); - dtmf_dolphin_player_generate_waveform(player->half_samples); - - dtmf_dolphin_speaker_init(); - dtmf_dolphin_dma_init((uint32_t)player->sample_buffer, player->sample_count); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_dma_isr, player->queue); - - dtmf_dolphin_dma_start(); - dtmf_dolphin_speaker_start(); - - player->playing = true; - - return true; -} - -bool dtmf_dolphin_player_stop_tones() { - player->playing = false; - - dtmf_dolphin_speaker_stop(); - dtmf_dolphin_dma_stop(); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - - return true; -} - -bool dtmf_dolphin_player_handle_tick() { - DTMFDolphinPlayerEvent event; - - if(furi_message_queue_get(player->queue, &event, FuriWaitForever) == FuriStatusOk) { - if (player->playing) { - if(event.type == DTMFDolphinPlayerEventHalfTransfer) { - dtmf_dolphin_player_generate_waveform(0); - // uint16_t* sample_buffer_start = &player->sample_buffer[0]; - // for (size_t i = 0; i < player->half_samples; i++) { - // sample_buffer_start[i] = player->buffer_buffer[i]; - // } - return true; - } else if (event.type == DTMFDolphinPlayerEventFullTransfer) { - dtmf_dolphin_player_generate_waveform(player->half_samples); - // uint16_t* sample_buffer_start = &player->sample_buffer[player->half_samples]; - // for (size_t i = 0; i < player->half_samples; i++) { - // sample_buffer_start[i] = player->buffer_buffer[i]; - // } - return true; - } - } else { - return true; - } - } - return false; -} \ No newline at end of file diff --git a/dtmf_dolphin_player.h b/dtmf_dolphin_player.h deleted file mode 100644 index f33fb182f..000000000 --- a/dtmf_dolphin_player.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "dtmf_dolphin_event.h" -#include "dtmf_dolphin_hal.h" -#include "dtmf_dolphin_tone.h" - -#define PERIOD_2_PI 6.2832 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - size_t half_samples; - size_t sample_count; - float wf1_freq; - float wf2_freq; - uint16_t wf1_period; - uint16_t wf2_period; - uint16_t wf1_pos; - uint16_t wf2_pos; - uint8_t *buffer_buffer; - uint16_t *sample_buffer; - float volume; - bool playing; - - FuriMessageQueue* queue; -} DTMFDolphinPlayer; - -void dtmf_dolphin_dma_isr(void* ctx); - -bool dtmf_dolphin_player_init(void* context); - -void dtmf_dolphin_player_clear_samples(); - -bool dtmf_dolphin_player_generate_waveform(size_t index); - -bool dtmf_dolphin_player_play_tones(float *freq); - -bool dtmf_dolphin_player_stop_tones(); - -bool dtmf_dolphin_player_handle_tick(); - -#ifdef __cplusplus -} -#endif - - diff --git a/dtmf_dolphin_tone.c b/dtmf_dolphin_tone.c deleted file mode 100644 index 45491df68..000000000 --- a/dtmf_dolphin_tone.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "dtmf_dolphin_tone.h" - -const char* dtmf_dolphin_get_tone_name(uint8_t row, uint8_t col, DTMFDolphinToneSection block) { - if (block == DTMF_DOLPHIN_TONE_BLOCK_DIALER) { - for (int i = 0; i < DTMF_DOLPHIN_DIALER_TONE_COUNT; i++) { - if (dtmf_dolphin_dialer_tone[i].pos.row == row && dtmf_dolphin_dialer_tone[i].pos.col == col) { - return dtmf_dolphin_dialer_tone[i].name; - } - } - } else if (block == DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX) { - // if (index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // return dtmf_dolphin_bluebox_tone[index].name; - // } - } - return "N"; -} - -uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col, DTMFDolphinToneSection block) { - if (block == DTMF_DOLPHIN_TONE_BLOCK_DIALER) { - for (int i = 0; i < DTMF_DOLPHIN_DIALER_TONE_COUNT; i++) { - if (dtmf_dolphin_dialer_tone[i].pos.row == row && dtmf_dolphin_dialer_tone[i].pos.col == col) { - return dtmf_dolphin_dialer_tone[i].pos.span; - } - } - } else if (block == DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX) { - // if (index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // return dtmf_dolphin_bluebox_tone[index].name; - // } - } - return 1; // Default to 1 -} - -void dtmf_dolphin_get_tone_frequencies(float *freq, uint8_t row, uint8_t col, DTMFDolphinToneSection block) { - freq[0] = 0; - freq[1] = 0; - - if (block == DTMF_DOLPHIN_TONE_BLOCK_DIALER) { - for (int i = 0; i < DTMF_DOLPHIN_DIALER_TONE_COUNT; i++) { - if (dtmf_dolphin_dialer_tone[i].pos.row == row && dtmf_dolphin_dialer_tone[i].pos.col == col) { - freq[0] = dtmf_dolphin_dialer_tone[i].frequency_1; - freq[1] = dtmf_dolphin_dialer_tone[i].frequency_2; - } - } - } else if (block == DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX) { - // if (index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // return dtmf_dolphin_bluebox_tone[index].name; - // } - } -} - -void dtmf_dolphin_tone_get_max_pos(uint8_t *max_rows, uint8_t *max_cols, uint8_t *max_span, DTMFDolphinToneSection block) { - max_rows[0] = 0; - max_cols[0] = 0; - max_span[0] = 0; - uint8_t span[8] = { 0 }; - if (block == DTMF_DOLPHIN_TONE_BLOCK_DIALER) { - for (int i = 0; i < DTMF_DOLPHIN_DIALER_TONE_COUNT; i++) { - if (dtmf_dolphin_dialer_tone[i].pos.row > max_rows[0]) - max_rows[0] = dtmf_dolphin_dialer_tone[i].pos.row; - if (dtmf_dolphin_dialer_tone[i].pos.col > max_cols[0]) - max_cols[0] = dtmf_dolphin_dialer_tone[i].pos.col; - span[dtmf_dolphin_dialer_tone[i].pos.row] += dtmf_dolphin_dialer_tone[i].pos.span; - } - max_rows[0]++; - max_cols[0]++; - for (int i = 0; i < max_rows[0]; i++) { - if (span[i] > max_span[0]) - max_span[0] = span[i]; - } - } else if (block == DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX) { - // if (index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // for (int i; i < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT; i++) { - // if (dtmf_dolphin_bluebox_tone[i].pos.row > max_rows) - // max_rows = dtmf_dolphin_bluebox_tone[i].pos.row; - // if (dtmf_dolphin_bluebox_tone[i].pos.col > max_cols) - // max_cols = dtmf_dolphin_bluebox_tone[i].pos.col; - // } - } -} - -// void dtmf_dolphin_bluebox_generate(uint8_t index, uint8_t *buffer) { - -// // TODO: Generate the waveform -// } diff --git a/dtmf_dolphin_tone.h b/dtmf_dolphin_tone.h deleted file mode 100644 index 9c0befe55..000000000 --- a/dtmf_dolphin_tone.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "tones/dtmf_dolphin_dialer_tones.h" -#include "tones/dtmf_dolphin_bluebox_tones.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - DTMF_DOLPHIN_TONE_BLOCK_DIALER, - DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, - DTMF_DOLPHIN_TONE_BLOCK_REDBOX, -} DTMFDolphinToneSection; - -const char* dtmf_dolphin_get_tone_name(uint8_t row, uint8_t col, DTMFDolphinToneSection block); - -uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col, DTMFDolphinToneSection block); - -void dtmf_dolphin_get_tone_frequencies(float *freq, uint8_t row, uint8_t col, DTMFDolphinToneSection block); - -void dtmf_dolphin_tone_get_max_pos(uint8_t *max_rows, uint8_t *max_cols, uint8_t *max_span, DTMFDolphinToneSection block); - -#ifdef __cplusplus -} -#endif diff --git a/scenes/dtmf_dolphin_scene_bluebox.c b/scenes/dtmf_dolphin_scene_bluebox.c deleted file mode 100644 index 3af221280..000000000 --- a/scenes/dtmf_dolphin_scene_bluebox.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "../dtmf_dolphin_i.h" - -void dtmf_dolphin_scene_bluebox_on_enter(void *context) { - DTMFDolphinApp* app = context; - view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewBluebox); -} - -bool dtmf_dolphin_scene_bluebox_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void dtmf_dolphin_scene_bluebox_on_exit(void* context) { - UNUSED(context); -} diff --git a/scenes/dtmf_dolphin_scene_config.h b/scenes/dtmf_dolphin_scene_config.h index 644525cd0..b6dab07dc 100644 --- a/scenes/dtmf_dolphin_scene_config.h +++ b/scenes/dtmf_dolphin_scene_config.h @@ -1,3 +1,2 @@ ADD_SCENE(dtmf_dolphin, start, Start) -ADD_SCENE(dtmf_dolphin, dialer, Dialer) -ADD_SCENE(dtmf_dolphin, bluebox, Bluebox) \ No newline at end of file +ADD_SCENE(dtmf_dolphin, dialer, Dialer) \ No newline at end of file diff --git a/scenes/dtmf_dolphin_scene_dialer.c b/scenes/dtmf_dolphin_scene_dialer.c index faf3d617f..2c24918ba 100644 --- a/scenes/dtmf_dolphin_scene_dialer.c +++ b/scenes/dtmf_dolphin_scene_dialer.c @@ -1,7 +1,31 @@ #include "../dtmf_dolphin_i.h" +// #include "../dtmf_dolphin_data.h" +// #include "../dtmf_dolphin_audio.h" + void dtmf_dolphin_scene_dialer_on_enter(void *context) { DTMFDolphinApp* app = context; + DTMFDolphinScene scene_id = DTMFDolphinSceneDialer; + enum DTMFDolphinSceneState state = scene_manager_get_scene_state(app->scene_manager, scene_id); + + switch (state) + { + case DTMFDolphinSceneStateBluebox: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX); + break; + case DTMFDolphinSceneStateRedboxUS: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US); + break; + case DTMFDolphinSceneStateRedboxUK: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK); + break; + case DTMFDolphinSceneStateMisc: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_MISC); + break; + default: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_DIALER); + break; + } view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewDialer); } @@ -9,14 +33,14 @@ void dtmf_dolphin_scene_dialer_on_enter(void *context) { bool dtmf_dolphin_scene_dialer_on_event(void* context, SceneManagerEvent event) { DTMFDolphinApp* app = context; UNUSED(app); + UNUSED(event); bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - } + // if(event.type == SceneManagerEventTypeTick) { + // consumed = true; + // } return consumed; - return false; } void dtmf_dolphin_scene_dialer_on_exit(void* context) { diff --git a/scenes/dtmf_dolphin_scene_start.c b/scenes/dtmf_dolphin_scene_start.c index d61ceda6e..d789c36ce 100644 --- a/scenes/dtmf_dolphin_scene_start.c +++ b/scenes/dtmf_dolphin_scene_start.c @@ -2,12 +2,12 @@ static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) { DTMFDolphinApp* app = context; - if (index == DTMFDolphinItemDialer) { + if (index == DTMFDolphinSceneStateDialer) { view_dispatcher_send_custom_event( app->view_dispatcher, DTMFDolphinEventStartDialer ); - } else if (index == DTMFDolphinItemBluebox) { + } else if (index == DTMFDolphinSceneStateBluebox) { view_dispatcher_send_custom_event( app->view_dispatcher, DTMFDolphinEventStartBluebox @@ -26,7 +26,8 @@ void dtmf_dolphin_scene_start_on_enter(void* context) { app); variable_item_list_add(var_item_list, "Dialer", 0, NULL, NULL); - // variable_item_list_add(var_item_list, "Bluebox", 0, NULL, NULL); + variable_item_list_add(var_item_list, "Bluebox", 0, NULL, NULL); + variable_item_list_add(var_item_list, "Misc", 0, NULL, NULL); variable_item_list_set_selected_item( var_item_list, @@ -44,11 +45,14 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if (event.event == DTMFDolphinEventStartDialer) { - scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinItemDialer); + scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateDialer); scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); } else if (event.event == DTMFDolphinEventStartBluebox) { - scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneBluebox, DTMFDolphinItemBluebox); - scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneBluebox); + scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateBluebox); + scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); + } else if (event.event == DTMFDolphinEventStartMisc) { + scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateMisc); + scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); } consumed = true; } diff --git a/tones/dtmf_dolphin_bluebox_tones.h b/tones/dtmf_dolphin_bluebox_tones.h deleted file mode 100644 index 4ac053ea8..000000000 --- a/tones/dtmf_dolphin_bluebox_tones.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include - -#define DTMF_DOLPHIN_BLUEBOX_TONE_COUNT 16 - -typedef struct { - const char *name; - const float frequency_1; - const float frequency_2; -} DTMFDolphinBlueboxTones; - -static const DTMFDolphinBlueboxTones dtmf_dolphin_bluebox_tone[DTMF_DOLPHIN_BLUEBOX_TONE_COUNT] = { - {"2600 Hz", 2600.0, 0.0}, - {"1", 700.0, 900.0}, - {"2", 700.0, 1100.0}, - {"3", 900.0, 1100.0}, - {"4", 700.0, 1300.0}, - {"5", 900.0, 1300.0}, - {"6", 1100.0, 1300.0}, - {"7", 700.0, 1500.0}, - {"8", 900.0, 1500.0}, - {"9", 1100.0, 1500.0}, - {"0", 1300.0, 1500.0}, - {"Key Pulse (KP)", 1100.0, 1700.0}, - {"Start (ST)", 1500.0, 1700.0}, - {"CCITT 11", 700.0, 1700.0}, - {"CCITT 12", 900.0, 1700.0}, - {"CCITT KP2", 1300.0, 1700.0}, -}; diff --git a/tones/dtmf_dolphin_dialer_tones.h b/tones/dtmf_dolphin_dialer_tones.h deleted file mode 100644 index e9e462aaf..000000000 --- a/tones/dtmf_dolphin_dialer_tones.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include - -#define DTMF_DOLPHIN_DIALER_TONE_COUNT 16 - -typedef struct DTMFDolphinDialerTonePos { - const uint8_t row; - const uint8_t col; - const uint8_t span; -} DTMFDolphinDialerTonePos; - -typedef struct { - const char *name; - const float frequency_1; - const float frequency_2; - const struct DTMFDolphinDialerTonePos pos; -} DTMFDolphinDialerTones; - -/* Via https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling - 1209 Hz 1336 Hz 1477 Hz 1633 Hz -697 Hz 1 2 3 A -770 Hz 4 5 6 B -852 Hz 7 8 9 C -941 Hz * 0 # D */ - -static const DTMFDolphinDialerTones dtmf_dolphin_dialer_tone[DTMF_DOLPHIN_DIALER_TONE_COUNT] = { - {"1", 697.0, 1209.0, {0, 0, 1}}, - {"2", 697.0, 1336.0, {0, 1, 1}}, - {"3", 697.0, 1477.0, {0, 2, 1}}, - {"A", 697.0, 1633.0, {0, 3, 1}}, - {"4", 770.0, 1209.0, {1, 0, 1}}, - {"5", 770.0, 1336.0, {1, 1, 1}}, - {"6", 770.0, 1477.0, {1, 2, 1}}, - {"B", 770.0, 1633.0, {1, 3, 1}}, - {"7", 852.0, 1209.0, {2, 0, 1}}, - {"8", 852.0, 1336.0, {2, 1, 1}}, - {"9", 852.0, 1477.0, {2, 2, 1}}, - {"C", 852.0, 1633.0, {2, 3, 1}}, - {"*", 941.0, 1209.0, {3, 0, 1}}, - {"0", 941.0, 1336.0, {3, 1, 1}}, - {"#", 941.0, 1477.0, {3, 2, 1}}, - {"D", 941.0, 1633.0, {3, 3, 1}}, -}; diff --git a/views/dtmf_dolphin_bluebox.c b/views/dtmf_dolphin_bluebox.c deleted file mode 100644 index ff2943ae2..000000000 --- a/views/dtmf_dolphin_bluebox.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "dtmf_dolphin_bluebox.h" - -#include - -typedef struct DTMFDolphinBluebox { - View* view; - DTMFDolphinBlueboxOkCallback callback; - void* context; -} DTMFDolphinBluebox; - -typedef struct { - uint8_t index; -} DTMFDolphinBlueboxModel; - -static bool dtmf_dolphin_bluebox_process_left(DTMFDolphinBluebox* dtmf_dolphin_bluebox); -static bool dtmf_dolphin_bluebox_process_right(DTMFDolphinBluebox* dtmf_dolphin_bluebox); -static bool dtmf_dolphin_bluebox_process_ok(DTMFDolphinBluebox* dtmf_dolphin_bluebox, InputEvent* event); - -static void dtmf_dolphin_bluebox_draw_callback(Canvas* canvas, void* _model) { - DTMFDolphinBlueboxModel* model = _model; - UNUSED(model); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Bluebox Mode"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to select"); - // elements_multiline_text_aligned( - // canvas, 64, 32, AlignCenter, AlignTop, dtmf_dolphin_get_tone_name(model->index, DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX)); -} - -static bool dtmf_dolphin_bluebox_input_callback(InputEvent* event, void* context) { - furi_assert(context); - DTMFDolphinBluebox* dtmf_dolphin_bluebox = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyRight) { - consumed = dtmf_dolphin_bluebox_process_right(dtmf_dolphin_bluebox); - } else if(event->key == InputKeyLeft) { - consumed = dtmf_dolphin_bluebox_process_left(dtmf_dolphin_bluebox); - } - } else if(event->key == InputKeyOk) { - consumed = dtmf_dolphin_bluebox_process_ok(dtmf_dolphin_bluebox, event); - } - - return consumed; -} - -static bool dtmf_dolphin_bluebox_process_left(DTMFDolphinBluebox* dtmf_dolphin_bluebox) { - with_view_model( - dtmf_dolphin_bluebox->view, (DTMFDolphinBlueboxModel * model) { - if(model->index) { - model->index--; - } - return true; - }); - return true; -} - -static bool dtmf_dolphin_bluebox_process_right(DTMFDolphinBluebox* dtmf_dolphin_bluebox) { - with_view_model( - dtmf_dolphin_bluebox->view, (DTMFDolphinBlueboxModel * model) { - if(model->index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - model->index++; - } - return true; - }); - return true; -} - -static bool dtmf_dolphin_bluebox_process_ok(DTMFDolphinBluebox* dtmf_dolphin_bluebox, InputEvent* event) { - bool consumed = false; - - with_view_model( - dtmf_dolphin_bluebox->view, (DTMFDolphinBlueboxModel* model) { - if(event->type == InputTypePress) { - if(model->index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // TODO: Do the thing - } else { - // TODO: Do the thing - } - consumed = true; - } else if(event->type == InputTypeRelease) { - if(model->index < DTMF_DOLPHIN_BLUEBOX_TONE_COUNT) { - // gpio_item_set_pin(Model->pin_idx, false); - } else { - // gpio_item_set_all_pins(false); - } - consumed = true; - } - dtmf_dolphin_bluebox->callback(event->type, dtmf_dolphin_bluebox->context); - return true; - }); - - return consumed; -} - -DTMFDolphinBluebox* dtmf_dolphin_bluebox_alloc() { - DTMFDolphinBluebox* dtmf_dolphin_bluebox = malloc(sizeof(DTMFDolphinBluebox)); - - dtmf_dolphin_bluebox->view = view_alloc(); - view_allocate_model(dtmf_dolphin_bluebox->view, ViewModelTypeLocking, sizeof(DTMFDolphinBlueboxModel)); - view_set_context(dtmf_dolphin_bluebox->view, dtmf_dolphin_bluebox); - view_set_draw_callback(dtmf_dolphin_bluebox->view, dtmf_dolphin_bluebox_draw_callback); - view_set_input_callback(dtmf_dolphin_bluebox->view, dtmf_dolphin_bluebox_input_callback); - - return dtmf_dolphin_bluebox; -} - -void dtmf_dolphin_bluebox_free(DTMFDolphinBluebox* dtmf_dolphin_bluebox) { - furi_assert(dtmf_dolphin_bluebox); - view_free(dtmf_dolphin_bluebox->view); - free(dtmf_dolphin_bluebox); -} - -View* dtmf_dolphin_bluebox_get_view(DTMFDolphinBluebox* dtmf_dolphin_bluebox) { - furi_assert(dtmf_dolphin_bluebox); - return dtmf_dolphin_bluebox->view; -} - -void dtmf_dolphin_bluebox_set_ok_callback(DTMFDolphinBluebox* dtmf_dolphin_bluebox, DTMFDolphinBlueboxOkCallback callback, void* context) { - furi_assert(dtmf_dolphin_bluebox); - furi_assert(callback); - with_view_model( - dtmf_dolphin_bluebox->view, (DTMFDolphinBlueboxModel * model) { - UNUSED(model); - dtmf_dolphin_bluebox->callback = callback; - dtmf_dolphin_bluebox->context = context; - return false; - }); -} diff --git a/views/dtmf_dolphin_bluebox.h b/views/dtmf_dolphin_bluebox.h deleted file mode 100644 index 2f9975958..000000000 --- a/views/dtmf_dolphin_bluebox.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include "../dtmf_dolphin_tone.h" - -typedef struct DTMFDolphinBluebox DTMFDolphinBluebox; -typedef void (*DTMFDolphinBlueboxOkCallback)(InputType type, void* context); - -DTMFDolphinBluebox* dtmf_dolphin_bluebox_alloc(); - -void dtmf_dolphin_bluebox_free(DTMFDolphinBluebox* dtmf_dolphin_bluebox); - -View* dtmf_dolphin_bluebox_get_view(DTMFDolphinBluebox* dtmf_dolphin_bluebox); - -void dtmf_dolphin_bluebox_set_ok_callback(DTMFDolphinBluebox* dtmf_dolphin_bluebox, DTMFDolphinBlueboxOkCallback callback, void* context); diff --git a/views/dtmf_dolphin_common.h b/views/dtmf_dolphin_common.h index e1ffa01b0..855978d2c 100644 --- a/views/dtmf_dolphin_common.h +++ b/views/dtmf_dolphin_common.h @@ -1,5 +1,7 @@ #pragma once -#include "../dtmf_dolphin_player.h" +#include "../dtmf_dolphin_event.h" +#include "../dtmf_dolphin_data.h" +#include "../dtmf_dolphin_audio.h" #define DTMF_DOLPHIN_NUMPAD_X 1 #define DTMF_DOLPHIN_NUMPAD_Y 14 diff --git a/views/dtmf_dolphin_dialer.c b/views/dtmf_dolphin_dialer.c index eddfa0dde..f33cd642a 100644 --- a/views/dtmf_dolphin_dialer.c +++ b/views/dtmf_dolphin_dialer.c @@ -9,9 +9,12 @@ typedef struct DTMFDolphinDialer { } DTMFDolphinDialer; typedef struct { + DTMFDolphinToneSection section; uint8_t row; uint8_t col; - float *freq; + float freq1; + float freq2; + bool playing; } DTMFDolphinDialerModel; static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer); @@ -31,7 +34,11 @@ void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) { (row * DTMF_DOLPHIN_BUTTON_HEIGHT); // (row * DTMF_DOLPHIN_BUTTON_PADDING); - uint8_t span = dtmf_dolphin_get_tone_span(row, col, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + uint8_t span = dtmf_dolphin_get_tone_span(row, col); + + if (span == 0) { + return; + } canvas_set_color(canvas, ColorBlack); @@ -57,7 +64,7 @@ void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) { top + (int) (DTMF_DOLPHIN_BUTTON_HEIGHT / 2), AlignCenter, AlignCenter, - dtmf_dolphin_get_tone_name(row, col, DTMF_DOLPHIN_TONE_BLOCK_DIALER)); + dtmf_dolphin_data_get_tone_name(row, col)); if (invert) canvas_invert_color(canvas); @@ -68,7 +75,7 @@ void draw_dialer(Canvas* canvas, void* _model) { uint8_t max_rows; uint8_t max_cols; uint8_t max_span; - dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span); canvas_set_font(canvas, FontSecondary); @@ -83,18 +90,34 @@ void draw_dialer(Canvas* canvas, void* _model) { } void update_frequencies(DTMFDolphinDialerModel *model) { - dtmf_dolphin_get_tone_frequencies(model->freq, model->row, model->col, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col); } static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { DTMFDolphinDialerModel* model = _model; - uint8_t max_rows; - uint8_t max_cols; - uint8_t max_span; - dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + if (model->playing) { + // Leverage the prioritized draw callback to handle + // the DMA so that it doesn't skip. + dtmf_dolphin_audio_handle_tick(); + // Don't do any drawing if audio is playing. + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, + canvas_width(canvas) / 2, + canvas_height(canvas) / 2, + AlignCenter, + AlignCenter, + "Playing Tones"); + return; + } + update_frequencies(model); + uint8_t max_rows = 0; + uint8_t max_cols = 0; + uint8_t max_span = 0; + dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span); canvas_set_font(canvas, FontPrimary); - elements_multiline_text(canvas, 2, 10, "Dialer"); + elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name()); canvas_draw_line(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, 0, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, canvas_height(canvas)); @@ -107,11 +130,18 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { string_t output; string_init(output); - string_cat_printf( - output, - "F1: %u Hz\nF2: %u Hz", - model->freq[0] ? (unsigned int) model->freq[0] : 0, - model->freq[1] ? (unsigned int) model->freq[1] : 0); + if (model->freq1 && model->freq2) { + string_cat_printf( + output, + "Dual Tone\nF1: %u Hz\nF2: %u Hz\n", + (unsigned int) model->freq1, + (unsigned int) model->freq2); + } else if (model->freq1) { + string_cat_printf( + output, + "Single Tone\nF: %u Hz\n", + (unsigned int) model->freq1); + } canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorBlack); @@ -146,9 +176,14 @@ static bool dtmf_dolphin_dialer_input_callback(InputEvent* event, void* context) static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer) { with_view_model( dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { - if(model->row > 0) { - model->row--; - update_frequencies(model); + uint8_t span = 0; + uint8_t cursor = model->row; + while (span == 0 && cursor > 0) { + cursor--; + span = dtmf_dolphin_get_tone_span(cursor, model->col); + } + if (span != 0) { + model->row = cursor; } return true; }); @@ -159,13 +194,18 @@ static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dia uint8_t max_rows = 0; uint8_t max_cols = 0; uint8_t max_span = 0; - dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span); with_view_model( dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { - if(model->row < max_rows - 1) { - model->row++; - update_frequencies(model); + uint8_t span = 0; + uint8_t cursor = model->row; + while(span == 0 && cursor < max_rows - 1) { + cursor++; + span = dtmf_dolphin_get_tone_span(cursor, model->col); + } + if (span != 0) { + model->row = cursor; } return true; }); @@ -175,9 +215,14 @@ static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dia static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer) { with_view_model( dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { - if(model->col > 0) { - model->col--; - update_frequencies(model); + uint8_t span = 0; + uint8_t cursor = model->col; + while (span == 0 && cursor > 0) { + cursor--; + span = dtmf_dolphin_get_tone_span(model->row, cursor); + } + if (span != 0) { + model->col = cursor; } return true; }); @@ -188,13 +233,18 @@ static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_di uint8_t max_rows = 0; uint8_t max_cols = 0; uint8_t max_span = 0; - dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span, DTMF_DOLPHIN_TONE_BLOCK_DIALER); + dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span); with_view_model( dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { - if(model->col < max_cols - 1) { - model->col++; - update_frequencies(model); + uint8_t span = 0; + uint8_t cursor = model->col; + while(span == 0 && cursor < max_cols - 1) { + cursor++; + span = dtmf_dolphin_get_tone_span(model->row, cursor); + } + if (span != 0) { + model->col = cursor; } return true; }); @@ -207,9 +257,9 @@ static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_diale with_view_model( dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { if (event->type == InputTypePress) { - dtmf_dolphin_player_play_tones(model->freq); + model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2); } else if (event->type == InputTypeRelease) { - dtmf_dolphin_player_stop_tones(); + model->playing = !dtmf_dolphin_audio_stop_tones(); } return true; @@ -218,6 +268,23 @@ static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_diale return consumed; } +static void dtmf_dolphin_dialer_enter_callback(void* context) { + furi_assert(context); + DTMFDolphinDialer* dtmf_dolphin_dialer = context; + + with_view_model( + dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { + model->col = 0; + model->row = 0; + model->section = 0; + model->freq1 = 0.0; + model->freq2 = 0.0; + model->playing = false; + return true; + } + ); +} + DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() { DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer)); @@ -228,8 +295,10 @@ DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() { dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { model->col = 0; model->row = 0; - model->freq = malloc(sizeof(float) * 2); - update_frequencies(model); + model->section = 0; + model->freq1 = 0.0; + model->freq2 = 0.0; + model->playing = false; return true; } ); @@ -237,18 +306,12 @@ DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() { view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer); view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback); view_set_input_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_input_callback); - + view_set_enter_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_enter_callback); return dtmf_dolphin_dialer; } void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer) { furi_assert(dtmf_dolphin_dialer); - with_view_model( - dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { - free(model->freq); - return true; - } - ); view_free(dtmf_dolphin_dialer->view); free(dtmf_dolphin_dialer); } @@ -258,14 +321,3 @@ View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) { return dtmf_dolphin_dialer->view; } -// void dtmf_dolphin_dialer_set_ok_callback(DTMFDolphinDialer* dtmf_dolphin_dialer, DTMFDolphinDialerOkCallback callback, void* context) { -// furi_assert(dtmf_dolphin_dialer); -// furi_assert(callback); -// with_view_model( -// dtmf_dolphin_dialer->view, (DTMFDolphinDialerModel * model) { -// UNUSED(model); -// dtmf_dolphin_dialer->callback = callback; -// dtmf_dolphin_dialer->context = context; -// return false; -// }); -// } diff --git a/views/dtmf_dolphin_dialer.h b/views/dtmf_dolphin_dialer.h index 80c0aef81..c52343ff0 100644 --- a/views/dtmf_dolphin_dialer.h +++ b/views/dtmf_dolphin_dialer.h @@ -1,8 +1,6 @@ #pragma once #include -#include "../dtmf_dolphin_event.h" -#include "../dtmf_dolphin_tone.h" #include "dtmf_dolphin_common.h" typedef struct DTMFDolphinDialer DTMFDolphinDialer;