Skip to content

Commit

Permalink
Redbox functionality complete!
Browse files Browse the repository at this point in the history
  • Loading branch information
litui committed Oct 16, 2022
1 parent 5e8e559 commit a443d34
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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.
Now in a release-ready state for both Dialer, Bluebox, and Redbox (US/UK) functionality!

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.

Expand Down
Binary file modified assets/dialer.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 79 additions & 8 deletions dtmf_dolphin_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
return osc;
}

DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
DTMFDolphinPulseFilter *pf = malloc(sizeof(DTMFDolphinPulseFilter));
pf->duration = 0;
pf->period = 0;
pf->offset = 0;
pf->lookup_table = NULL;
return pf;
}

DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio));
player->buffer_length = SAMPLE_BUFFER_LENGTH;
Expand All @@ -44,6 +53,8 @@ DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
player->osc2 = dtmf_dolphin_osc_alloc();
player->volume = 1.0f;
player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
player->filter = dtmf_dolphin_pulse_filter_alloc();
player->playing = false;
dtmf_dolphin_audio_clear_samples(player);

return player;
Expand Down Expand Up @@ -82,6 +93,28 @@ void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
}
}

void filter_generate_lookup_table(DTMFDolphinPulseFilter* pf, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
if (pf->lookup_table != NULL) {
free(pf->lookup_table);
}
pf->offset = 0;

uint16_t gap_period = calc_waveform_period(1000 / (float) gap_ms);
uint16_t pulse_period = calc_waveform_period(1000 / (float) pulse_ms);
pf->period = pulse_period + gap_period;

if (!pf->period) {
pf->lookup_table = NULL;
return;
}
pf->duration = pf->period * pulses;
pf->lookup_table = malloc(sizeof(bool) * pf->duration);

for (size_t i = 0; i < pf->duration; i++) {
pf->lookup_table[i] = i % pf->period < pulse_period;
}
}

float sample_frame(DTMFDolphinOsc* osc) {
float frame = 0.0;

Expand All @@ -93,21 +126,45 @@ float sample_frame(DTMFDolphinOsc* osc) {
return frame;
}

bool sample_filter(DTMFDolphinPulseFilter* pf) {
bool frame = true;

if (pf->duration) {
if (pf->offset < pf->duration) {
frame = pf->lookup_table[pf->offset];
pf->offset = pf->offset + 1;
} else {
frame = false;
}
}

return frame;
}

void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
if (osc->lookup_table != NULL) {
free(osc->lookup_table);
}
free(osc);
}

void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
if (pf->lookup_table != NULL) {
free(pf->lookup_table);
}
free(pf);
}

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);
dtmf_dolphin_filter_free(player->filter);
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];
Expand All @@ -121,7 +178,7 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
} else {
data = (sample_frame(player->osc1));
}
data *= player->volume;
data *= sample_filter(player->filter) ? player->volume : 0.0;
data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned

Expand All @@ -139,11 +196,16 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
return true;
}

bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
if (current_player != NULL && current_player->playing) {
// Cannot start playing while still playing something else
return false;
}
current_player = dtmf_dolphin_audio_alloc();

osc_generate_lookup_table(current_player->osc1, freq1);
osc_generate_lookup_table(current_player->osc2, freq2);
filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);

generate_waveform(current_player, 0);
generate_waveform(current_player, current_player->half_buffer_length);
Expand All @@ -155,10 +217,19 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {

dtmf_dolphin_dma_start();
dtmf_dolphin_speaker_start();
current_player->playing = true;
return true;
}

bool dtmf_dolphin_audio_stop_tones() {
if (current_player != NULL && !current_player->playing) {
// Can't stop a player that isn't playing.
return false;
}
while(current_player->filter->offset > 0 && current_player->filter->offset < current_player->filter->duration) {
// run remaining ticks if needed to complete filter sequence
dtmf_dolphin_audio_handle_tick();
}
dtmf_dolphin_speaker_stop();
dtmf_dolphin_dma_stop();

Expand Down
11 changes: 10 additions & 1 deletion dtmf_dolphin_audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ typedef struct {
uint16_t offset;
} DTMFDolphinOsc;

typedef struct {
float duration;
size_t period;
bool* lookup_table;
uint16_t offset;
} DTMFDolphinPulseFilter;

typedef struct {
size_t buffer_length;
size_t half_buffer_length;
Expand All @@ -23,6 +30,8 @@ typedef struct {
FuriMessageQueue *queue;
DTMFDolphinOsc *osc1;
DTMFDolphinOsc *osc2;
DTMFDolphinPulseFilter *filter;
bool playing;
} DTMFDolphinAudio;

DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
Expand All @@ -33,7 +42,7 @@ 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_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms);

bool dtmf_dolphin_audio_stop_tones();

Expand Down
17 changes: 15 additions & 2 deletions dtmf_dolphin_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
.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},
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
}
};

Expand Down Expand Up @@ -147,6 +147,19 @@ bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t
return false;
}

bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, 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) {
pulses[0] = tones.pulses;
pulse_ms[0] = tones.pulse_ms;
gap_ms[0] = tones.gap_duration;
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];
Expand Down
2 changes: 2 additions & 0 deletions dtmf_dolphin_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ 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_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, 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();
Expand Down
49 changes: 36 additions & 13 deletions scenes/dtmf_dolphin_scene_start.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uin
cust_event = DTMFDolphinEventStartBluebox;
break;
case 2:
cust_event = DTMFDolphinEventStartRedboxUS;
break;
case 3:
cust_event = DTMFDolphinEventStartRedboxUK;
break;
case 4:
cust_event = DTMFDolphinEventStartMisc;
break;
default:
return;
}

view_dispatcher_send_custom_event(
app->view_dispatcher,
cust_event
Expand All @@ -34,9 +40,11 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {
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_add(var_item_list, "Dialer", 0, NULL, context);
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context);
variable_item_list_add(var_item_list, "Redbox (US)", 0, NULL, context);
variable_item_list_add(var_item_list, "Redbox (UK)", 0, NULL, context);
variable_item_list_add(var_item_list, "Misc", 0, NULL, context);

variable_item_list_set_selected_item(
var_item_list,
Expand All @@ -53,16 +61,31 @@ 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);
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
} 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);
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
uint8_t sc_state;

switch (event.event)
{
case DTMFDolphinEventStartDialer:
sc_state = DTMFDolphinSceneStateDialer;
break;
case DTMFDolphinEventStartBluebox:
sc_state = DTMFDolphinSceneStateBluebox;
break;
case DTMFDolphinEventStartRedboxUS:
sc_state = DTMFDolphinSceneStateRedboxUS;
break;
case DTMFDolphinEventStartRedboxUK:
sc_state = DTMFDolphinSceneStateRedboxUK;
break;
case DTMFDolphinEventStartMisc:
sc_state = DTMFDolphinSceneStateMisc;
break;
default:
return consumed;
}
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, sc_state);
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);

consumed = true;
}
return consumed;
Expand Down
19 changes: 18 additions & 1 deletion views/dtmf_dolphin_dialer.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ typedef struct {
float freq1;
float freq2;
bool playing;
uint16_t pulses;
uint16_t pulse_ms;
uint16_t gap_ms;
} DTMFDolphinDialerModel;

static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer);
Expand Down Expand Up @@ -91,6 +94,7 @@ void draw_dialer(Canvas* canvas, void* _model) {

void update_frequencies(DTMFDolphinDialerModel *model) {
dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
dtmf_dolphin_data_get_filter_data(&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
}

static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
Expand Down Expand Up @@ -144,6 +148,19 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {

canvas_set_font(canvas, FontSecondary);
canvas_set_color(canvas, ColorBlack);
if (model->pulse_ms) {
furi_string_cat_printf(
output,
"P: %u * %u ms\n",
model->pulses,
model->pulse_ms);
}
if (model->gap_ms) {
furi_string_cat_printf(
output,
"Gaps: %u ms\n",
model->gap_ms);
}
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));

furi_string_free(output);
Expand Down Expand Up @@ -266,7 +283,7 @@ static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_diale
DTMFDolphinDialerModel * model,
{
if (event->type == InputTypePress) {
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2);
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
} else if (event->type == InputTypeRelease) {
model->playing = !dtmf_dolphin_audio_stop_tones();
}
Expand Down

0 comments on commit a443d34

Please sign in to comment.