Skip to content

Commit a443d34

Browse files
committed
Redbox functionality complete!
1 parent 5e8e559 commit a443d34

File tree

8 files changed

+161
-26
lines changed

8 files changed

+161
-26
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and future Redbox.
66

7-
Now in a release-ready state for both Dialer and Bluebox functionality. Redbox functionality awaits some changes for modulation.
7+
Now in a release-ready state for both Dialer, Bluebox, and Redbox (US/UK) functionality!
88

99
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.
1010

assets/dialer.jpg

1000 KB
Loading

dtmf_dolphin_audio.c

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
3535
return osc;
3636
}
3737

38+
DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
39+
DTMFDolphinPulseFilter *pf = malloc(sizeof(DTMFDolphinPulseFilter));
40+
pf->duration = 0;
41+
pf->period = 0;
42+
pf->offset = 0;
43+
pf->lookup_table = NULL;
44+
return pf;
45+
}
46+
3847
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
3948
DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio));
4049
player->buffer_length = SAMPLE_BUFFER_LENGTH;
@@ -44,6 +53,8 @@ DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
4453
player->osc2 = dtmf_dolphin_osc_alloc();
4554
player->volume = 1.0f;
4655
player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
56+
player->filter = dtmf_dolphin_pulse_filter_alloc();
57+
player->playing = false;
4758
dtmf_dolphin_audio_clear_samples(player);
4859

4960
return player;
@@ -82,6 +93,28 @@ void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
8293
}
8394
}
8495

96+
void filter_generate_lookup_table(DTMFDolphinPulseFilter* pf, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
97+
if (pf->lookup_table != NULL) {
98+
free(pf->lookup_table);
99+
}
100+
pf->offset = 0;
101+
102+
uint16_t gap_period = calc_waveform_period(1000 / (float) gap_ms);
103+
uint16_t pulse_period = calc_waveform_period(1000 / (float) pulse_ms);
104+
pf->period = pulse_period + gap_period;
105+
106+
if (!pf->period) {
107+
pf->lookup_table = NULL;
108+
return;
109+
}
110+
pf->duration = pf->period * pulses;
111+
pf->lookup_table = malloc(sizeof(bool) * pf->duration);
112+
113+
for (size_t i = 0; i < pf->duration; i++) {
114+
pf->lookup_table[i] = i % pf->period < pulse_period;
115+
}
116+
}
117+
85118
float sample_frame(DTMFDolphinOsc* osc) {
86119
float frame = 0.0;
87120

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

129+
bool sample_filter(DTMFDolphinPulseFilter* pf) {
130+
bool frame = true;
131+
132+
if (pf->duration) {
133+
if (pf->offset < pf->duration) {
134+
frame = pf->lookup_table[pf->offset];
135+
pf->offset = pf->offset + 1;
136+
} else {
137+
frame = false;
138+
}
139+
}
140+
141+
return frame;
142+
}
143+
144+
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
145+
if (osc->lookup_table != NULL) {
146+
free(osc->lookup_table);
147+
}
148+
free(osc);
149+
}
150+
151+
void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
152+
if (pf->lookup_table != NULL) {
153+
free(pf->lookup_table);
154+
}
155+
free(pf);
156+
}
157+
96158
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
97159
furi_message_queue_free(player->queue);
98160
dtmf_dolphin_osc_free(player->osc1);
99161
dtmf_dolphin_osc_free(player->osc2);
162+
dtmf_dolphin_filter_free(player->filter);
100163
free(player->sample_buffer);
101164
free(player);
102165
current_player = NULL;
103166
}
104167

105-
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
106-
if (osc->lookup_table != NULL) {
107-
free(osc->lookup_table);
108-
}
109-
free(osc);
110-
}
111168

112169
bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
113170
uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
@@ -121,7 +178,7 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
121178
} else {
122179
data = (sample_frame(player->osc1));
123180
}
124-
data *= player->volume;
181+
data *= sample_filter(player->filter) ? player->volume : 0.0;
125182
data *= UINT8_MAX / 2; // scale -128..127
126183
data += UINT8_MAX / 2; // to unsigned
127184

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

142-
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
199+
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
200+
if (current_player != NULL && current_player->playing) {
201+
// Cannot start playing while still playing something else
202+
return false;
203+
}
143204
current_player = dtmf_dolphin_audio_alloc();
144205

145206
osc_generate_lookup_table(current_player->osc1, freq1);
146207
osc_generate_lookup_table(current_player->osc2, freq2);
208+
filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);
147209

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

156218
dtmf_dolphin_dma_start();
157219
dtmf_dolphin_speaker_start();
220+
current_player->playing = true;
158221
return true;
159222
}
160223

161224
bool dtmf_dolphin_audio_stop_tones() {
225+
if (current_player != NULL && !current_player->playing) {
226+
// Can't stop a player that isn't playing.
227+
return false;
228+
}
229+
while(current_player->filter->offset > 0 && current_player->filter->offset < current_player->filter->duration) {
230+
// run remaining ticks if needed to complete filter sequence
231+
dtmf_dolphin_audio_handle_tick();
232+
}
162233
dtmf_dolphin_speaker_stop();
163234
dtmf_dolphin_dma_stop();
164235

dtmf_dolphin_audio.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ typedef struct {
1414
uint16_t offset;
1515
} DTMFDolphinOsc;
1616

17+
typedef struct {
18+
float duration;
19+
size_t period;
20+
bool* lookup_table;
21+
uint16_t offset;
22+
} DTMFDolphinPulseFilter;
23+
1724
typedef struct {
1825
size_t buffer_length;
1926
size_t half_buffer_length;
@@ -23,6 +30,8 @@ typedef struct {
2330
FuriMessageQueue *queue;
2431
DTMFDolphinOsc *osc1;
2532
DTMFDolphinOsc *osc2;
33+
DTMFDolphinPulseFilter *filter;
34+
bool playing;
2635
} DTMFDolphinAudio;
2736

2837
DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
@@ -33,7 +42,7 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player);
3342

3443
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc);
3544

36-
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2);
45+
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms);
3746

3847
bool dtmf_dolphin_audio_stop_tones();
3948

dtmf_dolphin_data.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
8585
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
8686
.tone_count = 2,
8787
.tones = {
88-
{"10p", 1000.0, 0.0, {0, 0, 3}, 1, 200, 0},
89-
{"50p", 1000.0, 0.0, {1, 0, 3}, 1, 350, 0},
88+
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
89+
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
9090
}
9191
};
9292

@@ -147,6 +147,19 @@ bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t
147147
return false;
148148
}
149149

150+
bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, uint8_t row, uint8_t col) {
151+
for (size_t i = 0; i < current_scene_data->tone_count; i++) {
152+
DTMFDolphinTones tones = current_scene_data->tones[i];
153+
if (tones.pos.row == row && tones.pos.col == col) {
154+
pulses[0] = tones.pulses;
155+
pulse_ms[0] = tones.pulse_ms;
156+
gap_ms[0] = tones.gap_duration;
157+
return true;
158+
}
159+
}
160+
return false;
161+
}
162+
150163
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
151164
for (size_t i = 0; i < current_scene_data->tone_count; i++) {
152165
DTMFDolphinTones tones = current_scene_data->tones[i];

dtmf_dolphin_data.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ DTMFDolphinToneSection dtmf_dolphin_data_get_current_section();
1919

2020
bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t row, uint8_t col);
2121

22+
bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, uint8_t row, uint8_t col);
23+
2224
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col);
2325

2426
const char* dtmf_dolphin_data_get_current_section_name();

scenes/dtmf_dolphin_scene_start.c

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uin
1212
cust_event = DTMFDolphinEventStartBluebox;
1313
break;
1414
case 2:
15+
cust_event = DTMFDolphinEventStartRedboxUS;
16+
break;
17+
case 3:
18+
cust_event = DTMFDolphinEventStartRedboxUK;
19+
break;
20+
case 4:
1521
cust_event = DTMFDolphinEventStartMisc;
1622
break;
1723
default:
1824
return;
1925
}
20-
26+
2127
view_dispatcher_send_custom_event(
2228
app->view_dispatcher,
2329
cust_event
@@ -34,9 +40,11 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {
3440
dtmf_dolphin_scene_start_main_menu_enter_callback,
3541
app);
3642

37-
variable_item_list_add(var_item_list, "Dialer", 0, NULL, NULL);
38-
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, NULL);
39-
variable_item_list_add(var_item_list, "Misc", 0, NULL, NULL);
43+
variable_item_list_add(var_item_list, "Dialer", 0, NULL, context);
44+
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context);
45+
variable_item_list_add(var_item_list, "Redbox (US)", 0, NULL, context);
46+
variable_item_list_add(var_item_list, "Redbox (UK)", 0, NULL, context);
47+
variable_item_list_add(var_item_list, "Misc", 0, NULL, context);
4048

4149
variable_item_list_set_selected_item(
4250
var_item_list,
@@ -53,16 +61,31 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
5361
bool consumed = false;
5462

5563
if(event.type == SceneManagerEventTypeCustom) {
56-
if (event.event == DTMFDolphinEventStartDialer) {
57-
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateDialer);
58-
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
59-
} else if (event.event == DTMFDolphinEventStartBluebox) {
60-
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateBluebox);
61-
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
62-
} else if (event.event == DTMFDolphinEventStartMisc) {
63-
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateMisc);
64-
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
64+
uint8_t sc_state;
65+
66+
switch (event.event)
67+
{
68+
case DTMFDolphinEventStartDialer:
69+
sc_state = DTMFDolphinSceneStateDialer;
70+
break;
71+
case DTMFDolphinEventStartBluebox:
72+
sc_state = DTMFDolphinSceneStateBluebox;
73+
break;
74+
case DTMFDolphinEventStartRedboxUS:
75+
sc_state = DTMFDolphinSceneStateRedboxUS;
76+
break;
77+
case DTMFDolphinEventStartRedboxUK:
78+
sc_state = DTMFDolphinSceneStateRedboxUK;
79+
break;
80+
case DTMFDolphinEventStartMisc:
81+
sc_state = DTMFDolphinSceneStateMisc;
82+
break;
83+
default:
84+
return consumed;
6585
}
86+
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, sc_state);
87+
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
88+
6689
consumed = true;
6790
}
6891
return consumed;

views/dtmf_dolphin_dialer.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ typedef struct {
1515
float freq1;
1616
float freq2;
1717
bool playing;
18+
uint16_t pulses;
19+
uint16_t pulse_ms;
20+
uint16_t gap_ms;
1821
} DTMFDolphinDialerModel;
1922

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

9295
void update_frequencies(DTMFDolphinDialerModel *model) {
9396
dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
97+
dtmf_dolphin_data_get_filter_data(&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
9498
}
9599

96100
static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
@@ -144,6 +148,19 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
144148

145149
canvas_set_font(canvas, FontSecondary);
146150
canvas_set_color(canvas, ColorBlack);
151+
if (model->pulse_ms) {
152+
furi_string_cat_printf(
153+
output,
154+
"P: %u * %u ms\n",
155+
model->pulses,
156+
model->pulse_ms);
157+
}
158+
if (model->gap_ms) {
159+
furi_string_cat_printf(
160+
output,
161+
"Gaps: %u ms\n",
162+
model->gap_ms);
163+
}
147164
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
148165

149166
furi_string_free(output);
@@ -266,7 +283,7 @@ static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_diale
266283
DTMFDolphinDialerModel * model,
267284
{
268285
if (event->type == InputTypePress) {
269-
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2);
286+
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
270287
} else if (event->type == InputTypeRelease) {
271288
model->playing = !dtmf_dolphin_audio_stop_tones();
272289
}

0 commit comments

Comments
 (0)