This commit is contained in:
RogueMaster
2022-10-16 14:53:08 -04:00
parent b2c9f0cc66
commit 5a380eb2d3
16 changed files with 201 additions and 172 deletions
@@ -1,5 +1,5 @@
App(
appid="blackjack",
appid="Blackjack",
name="Blackjack",
apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app",
@@ -7,3 +7,10 @@ DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and future Redbox.
Now in a release-ready state for both Dialer and Bluebox functionality. Redbox functionality awaits some changes for modulation.
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.
### Educational Links:
* http://www.phrack.org/issues/25/7.html#article
* https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling
* https://en.wikipedia.org/wiki/Blue_box
* https://en.wikipedia.org/wiki/Red_box_(phreaking)
@@ -5,11 +5,12 @@ App(
entry_point="dtmf_dolphin_app",
cdefines=["DTMF_DOLPHIN"],
requires=[
"storage",
"gui",
"dialogs",
],
fap_icon="phone.png",
stack_size=8 * 1024,
order=20,
fap_icon="dtmf_10px.png",
fap_category="Misc",
fap_category="Tools",
)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -67,15 +67,10 @@ static void app_free(DTMFDolphinApp* app) {
variable_item_list_free(app->main_menu_list);
dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer);
// widget_free(app->dtmf_dolphin_play);
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// button_panel_free(app->dialer_button_panel);
// button_panel_free(app->bluebox_button_panel);
// button_panel_free(app->redbox_button_panel);
notification_message(app->notification, &sequence_display_backlight_enforce_auto);
furi_record_close(RECORD_GUI);
@@ -83,10 +78,10 @@ static void app_free(DTMFDolphinApp* app) {
free(app);
}
int32_t dtmf_dolphin_app(void* p) {
int32_t dtmf_dolphin_app(void *p) {
UNUSED(p);
DTMFDolphinApp* app = app_alloc();
view_dispatcher_run(app->view_dispatcher);
app_free(app);
@@ -1,11 +1,11 @@
#include "dtmf_dolphin_audio.h"
DTMFDolphinAudio* current_player;
DTMFDolphinAudio *current_player;
static void dtmf_dolphin_audio_dma_isr(void* ctx) {
FuriMessageQueue* event_queue = ctx;
FuriMessageQueue *event_queue = ctx;
if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
if (LL_DMA_IsActiveFlag_HT1(DMA1)) {
LL_DMA_ClearFlag_HT1(DMA1);
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
@@ -21,13 +21,13 @@ static void dtmf_dolphin_audio_dma_isr(void* ctx) {
}
void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
for(size_t i = 0; i < player->buffer_length; i++) {
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));
DTMFDolphinOsc *osc = malloc(sizeof(DTMFDolphinOsc));
osc->cached_freq = 0;
osc->offset = 0;
osc->period = 0;
@@ -36,7 +36,7 @@ DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
}
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio));
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);
@@ -50,32 +50,34 @@ DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
}
size_t calc_waveform_period(float freq) {
if(!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);
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;
return (uint16_t) dma_rate * 2 / freq * 0.801923;
}
void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
if(osc->lookup_table != NULL) {
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) {
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++) {
for (size_t i = 0; i < osc->period; i++) {
osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
}
}
@@ -83,7 +85,7 @@ void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
float sample_frame(DTMFDolphinOsc* osc) {
float frame = 0.0;
if(osc->period) {
if (osc->period) {
frame = osc->lookup_table[osc->offset];
osc->offset = (osc->offset + 1) % osc->period;
}
@@ -101,7 +103,7 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
}
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
if(osc->lookup_table != NULL) {
if (osc->lookup_table != NULL) {
free(osc->lookup_table);
}
free(osc);
@@ -110,16 +112,18 @@ void dtmf_dolphin_osc_free(DTMFDolphinOsc* 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++) {
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);
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
data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned
if(data < 0) {
data = 0;
@@ -147,8 +151,7 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
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);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
dtmf_dolphin_dma_start();
dtmf_dolphin_speaker_start();
@@ -162,20 +165,20 @@ bool dtmf_dolphin_audio_stop_tones() {
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) {
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) {
} else if (event.type == DTMFDolphinEventDMAFullTransfer) {
generate_waveform(current_player, current_player->half_buffer_length);
handled = true;
}
@@ -17,12 +17,12 @@ typedef struct {
typedef struct {
size_t buffer_length;
size_t half_buffer_length;
uint8_t* buffer_buffer;
uint16_t* sample_buffer;
uint8_t *buffer_buffer;
uint16_t *sample_buffer;
float volume;
FuriMessageQueue* queue;
DTMFDolphinOsc* osc1;
DTMFDolphinOsc* osc2;
FuriMessageQueue *queue;
DTMFDolphinOsc *osc1;
DTMFDolphinOsc *osc2;
} DTMFDolphinAudio;
DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
@@ -7,13 +7,13 @@ typedef struct {
} DTMFDolphinTonePos;
typedef struct {
const char* name;
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
const uint16_t pulses; // for Redbox
const uint16_t pulse_ms; // for Redbox
const uint16_t gap_duration; // for Redbox
} DTMFDolphinTones;
typedef struct {
@@ -44,38 +44,41 @@ DTMFDolphinSceneData DTMFDolphinSceneDataDialer = {
{"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},
}};
{"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},
{"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},
}};
{"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0},
}
};
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
.name = "Redbox (UK)",
@@ -84,25 +87,28 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
.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 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;
DTMFDolphinSceneData *current_scene_data;
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
current_section = section;
switch(current_section) {
switch (current_section)
{
case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX:
current_scene_data = &DTMFDolphinSceneDataBluebox;
break;
@@ -125,14 +131,14 @@ DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() {
return current_section;
}
DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() {
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++) {
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) {
if (tones.pos.row == row && tones.pos.col == col) {
freq1[0] = tones.frequency_1;
freq2[0] = tones.frequency_2;
return true;
@@ -142,9 +148,9 @@ bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t
}
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++) {
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) {
if (tones.pos.row == row && tones.pos.col == col) {
return tones.name;
}
}
@@ -152,7 +158,7 @@ const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
}
const char* dtmf_dolphin_data_get_current_section_name() {
if(current_scene_data) {
if (current_scene_data) {
return current_scene_data->name;
}
return NULL;
@@ -162,26 +168,27 @@ void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t
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++) {
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]) {
if (tones.pos.row > max_rows[0]) {
max_rows[0] = tones.pos.row;
}
if(tones.pos.col > max_cols[0]) {
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];
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++) {
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) {
if (tones.pos.row == row && tones.pos.col == col) {
return tones.pos.span;
}
}
@@ -17,7 +17,7 @@ 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);
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);
@@ -17,6 +17,7 @@
#define TAG "DTMFDolphin"
enum DTMFDolphinSceneState {
DTMFDolphinSceneStateDialer,
DTMFDolphinSceneStateBluebox,
@@ -38,4 +39,7 @@ typedef struct {
NotificationApp* notification;
} DTMFDolphinApp;
typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView;
typedef enum {
DTMFDolphinViewMainMenu,
DTMFDolphinViewDialer
} DTMFDolphinView;
Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

@@ -2,15 +2,17 @@
// #include "../dtmf_dolphin_data.h"
// #include "../dtmf_dolphin_audio.h"
void dtmf_dolphin_scene_dialer_on_enter(void* context) {
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) {
switch (state)
{
case DTMFDolphinSceneStateBluebox:
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX);
break;
break;
case DTMFDolphinSceneStateRedboxUS:
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US);
break;
@@ -2,11 +2,26 @@
static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) {
DTMFDolphinApp* app = context;
if(index == DTMFDolphinSceneStateDialer) {
view_dispatcher_send_custom_event(app->view_dispatcher, DTMFDolphinEventStartDialer);
} else if(index == DTMFDolphinSceneStateBluebox) {
view_dispatcher_send_custom_event(app->view_dispatcher, DTMFDolphinEventStartBluebox);
uint8_t cust_event = 255;
switch (index)
{
case 0:
cust_event = DTMFDolphinEventStartDialer;
break;
case 1:
cust_event = DTMFDolphinEventStartBluebox;
break;
case 2:
cust_event = DTMFDolphinEventStartMisc;
break;
default:
return;
}
view_dispatcher_send_custom_event(
app->view_dispatcher,
cust_event
);
}
void dtmf_dolphin_scene_start_on_enter(void* context) {
@@ -15,16 +30,21 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {
// VariableItem* item;
variable_item_list_set_enter_callback(
var_item_list, dtmf_dolphin_scene_start_main_menu_enter_callback, app);
var_item_list,
dtmf_dolphin_scene_start_main_menu_enter_callback,
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, "Misc", 0, NULL, NULL);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
var_item_list,
scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
view_dispatcher_switch_to_view(
app->view_dispatcher,
DTMFDolphinViewMainMenu);
}
bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
@@ -33,17 +53,14 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DTMFDolphinEventStartDialer) {
scene_manager_set_scene_state(
app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateDialer);
if (event.event == DTMFDolphinEventStartDialer) {
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, DTMFDolphinSceneDialer, DTMFDolphinSceneStateBluebox);
} else if (event.event == DTMFDolphinEventStartBluebox) {
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);
} 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;
@@ -7,4 +7,4 @@
#define DTMF_DOLPHIN_NUMPAD_Y 14
#define DTMF_DOLPHIN_BUTTON_WIDTH 13
#define DTMF_DOLPHIN_BUTTON_HEIGHT 13
#define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides
#define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides
@@ -21,55 +21,53 @@ static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_diale
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) {
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + // ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
(col * DTMF_DOLPHIN_BUTTON_WIDTH);
// (col * DTMF_DOLPHIN_BUTTON_PADDING);
uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + // ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
(row * DTMF_DOLPHIN_BUTTON_HEIGHT);
// (row * DTMF_DOLPHIN_BUTTON_PADDING);
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + \
// ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
(col * DTMF_DOLPHIN_BUTTON_WIDTH);
// (col * DTMF_DOLPHIN_BUTTON_PADDING);
uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + \
// ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
(row * DTMF_DOLPHIN_BUTTON_HEIGHT);
// (row * DTMF_DOLPHIN_BUTTON_PADDING);
uint8_t span = dtmf_dolphin_get_tone_span(row, col);
if(span == 0) {
if (span == 0) {
return;
}
canvas_set_color(canvas, ColorBlack);
if(invert)
canvas_draw_rbox(
canvas,
left,
top,
if (invert)
canvas_draw_rbox(canvas, left, top,
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
2);
else
canvas_draw_rframe(
canvas,
left,
top,
canvas_draw_rframe(canvas, left, top,
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
DTMF_DOLPHIN_BUTTON_HEIGHT- (DTMF_DOLPHIN_BUTTON_PADDING * 2),
2);
if(invert) canvas_invert_color(canvas);
if (invert)
canvas_invert_color(canvas);
canvas_set_font(canvas, FontSecondary);
// canvas_set_color(canvas, invert ? ColorWhite : ColorBlack);
canvas_draw_str_aligned(
canvas,
left - 1 + (int)((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
top + (int)(DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
canvas_draw_str_aligned(canvas,
left - 1 + (int) ((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
top + (int) (DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
AlignCenter,
AlignCenter,
dtmf_dolphin_data_get_tone_name(row, col));
if(invert) canvas_invert_color(canvas);
if (invert)
canvas_invert_color(canvas);
}
void draw_dialer(Canvas* canvas, void* _model) {
@@ -81,9 +79,9 @@ void draw_dialer(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontSecondary);
for(int r = 0; r < max_rows; r++) {
for(int c = 0; c < max_cols; c++) {
if(model->row == r && model->col == c)
for (int r = 0; r < max_rows; r++) {
for (int c = 0; c < max_cols; c++) {
if (model->row == r && model->col == c)
draw_button(canvas, r, c, true);
else
draw_button(canvas, r, c, false);
@@ -91,20 +89,20 @@ void draw_dialer(Canvas* canvas, void* _model) {
}
}
void update_frequencies(DTMFDolphinDialerModel* model) {
void update_frequencies(DTMFDolphinDialerModel *model) {
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;
if(model->playing) {
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,
canvas_width(canvas) / 2,
canvas_height(canvas) / 2,
AlignCenter,
@@ -120,36 +118,33 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontPrimary);
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));
canvas_draw_line(canvas,
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, 0,
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, canvas_height(canvas));
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail");
canvas_draw_line(
canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
canvas_draw_line(canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
// elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode");
draw_dialer(canvas, model);
FuriString* output;
output = furi_string_alloc();
FuriString *output = furi_string_alloc();
if(model->freq1 && model->freq2) {
if (model->freq1 && model->freq2) {
furi_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) {
furi_string_cat_printf(output, "Single Tone\nF: %u Hz\n", (unsigned int)model->freq1);
(unsigned int) model->freq1,
(unsigned int) model->freq2);
} else if (model->freq1) {
furi_string_cat_printf(
output,
"Single Tone\nF: %u Hz\n",
(unsigned int) model->freq1);
}
canvas_set_font(canvas, FontSecondary);
canvas_set_color(canvas, ColorBlack);
elements_multiline_text(
canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
furi_string_free(output);
}
@@ -184,11 +179,11 @@ static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_diale
{
uint8_t span = 0;
uint8_t cursor = model->row;
while(span == 0 && cursor > 0) {
while (span == 0 && cursor > 0) {
cursor--;
span = dtmf_dolphin_get_tone_span(cursor, model->col);
}
if(span != 0) {
if (span != 0) {
model->row = cursor;
}
},
@@ -212,7 +207,7 @@ static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dia
cursor++;
span = dtmf_dolphin_get_tone_span(cursor, model->col);
}
if(span != 0) {
if (span != 0) {
model->row = cursor;
}
},
@@ -227,11 +222,11 @@ static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dia
{
uint8_t span = 0;
uint8_t cursor = model->col;
while(span == 0 && cursor > 0) {
while (span == 0 && cursor > 0) {
cursor--;
span = dtmf_dolphin_get_tone_span(model->row, cursor);
}
if(span != 0) {
if (span != 0) {
model->col = cursor;
}
},
@@ -255,7 +250,7 @@ static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_di
cursor++;
span = dtmf_dolphin_get_tone_span(model->row, cursor);
}
if(span != 0) {
if (span != 0) {
model->col = cursor;
}
},
@@ -263,17 +258,16 @@ static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_di
return true;
}
static bool
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
bool consumed = false;
with_view_model(
dtmf_dolphin_dialer->view,
DTMFDolphinDialerModel * model,
{
if(event->type == InputTypePress) {
if (event->type == InputTypePress) {
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2);
} else if(event->type == InputTypeRelease) {
} else if (event->type == InputTypeRelease) {
model->playing = !dtmf_dolphin_audio_stop_tones();
}
},
@@ -297,15 +291,15 @@ static void dtmf_dolphin_dialer_enter_callback(void* context) {
model->freq2 = 0.0;
model->playing = false;
},
true);
true
);
}
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
dtmf_dolphin_dialer->view = view_alloc();
view_allocate_model(
dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
view_allocate_model(dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
with_view_model(
dtmf_dolphin_dialer->view,
@@ -318,7 +312,8 @@ DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
model->freq2 = 0.0;
model->playing = false;
},
true);
true
);
view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
@@ -337,3 +332,4 @@ View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
furi_assert(dtmf_dolphin_dialer);
return dtmf_dolphin_dialer->view;
}
@@ -12,7 +12,4 @@ void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer);
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer);
void dtmf_dolphin_dialer_set_ok_callback(
DTMFDolphinDialer* dtmf_dolphin_dialer,
DTMFDolphinDialerOkCallback callback,
void* context);
void dtmf_dolphin_dialer_set_ok_callback(DTMFDolphinDialer* dtmf_dolphin_dialer, DTMFDolphinDialerOkCallback callback, void* context);