Skip to content
This repository has been archived by the owner on Jul 28, 2024. It is now read-only.

Commit

Permalink
Now functional as dialer and bluebox.
Browse files Browse the repository at this point in the history
  • Loading branch information
litui committed Sep 19, 2022
1 parent 1f0730d commit cb02b72
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 99 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
1 change: 0 additions & 1 deletion dtmf_dolphin.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ static void dtmf_dolphin_app_tick_event_callback(void* context) {
furi_assert(context);
DTMFDolphinApp* app = context;

// dtmf_dolphin_audio_handle_tick();
scene_manager_handle_tick_event(app->scene_manager);
}

Expand Down
122 changes: 70 additions & 52 deletions dtmf_dolphin_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,66 @@ void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
}

DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
DTMFDolphinOsc *osc = { 0 };
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;
player.buffer_length = SAMPLE_BUFFER_LENGTH;
player.half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
player.buffer_buffer = malloc(sizeof(uint8_t) * player.buffer_length);
player.sample_buffer = malloc(sizeof(uint16_t) * player.buffer_length);
player.osc1 = dtmf_dolphin_osc_alloc();
player.osc2 = dtmf_dolphin_osc_alloc();
player.playing = false;
player.volume = 2.0f;
player.queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
dtmf_dolphin_audio_clear_samples(&player);

return false;
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 constant, thanks to Dr_Zlo
// 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);

return (uint16_t) (dma_rate / freq);
// Using a constant scaling modifier, which likely represents
// the combined system overhead and isr latency.
return (uint16_t) dma_rate * 2 / freq * 0.801923;
}

float sample_frame(DTMFDolphinOsc* osc, float freq) {
float frame = 0.0;
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);

if (freq != osc->cached_freq || !osc->period) {
osc->cached_freq = freq;
osc->period = calc_waveform_period(freq);
osc->offset = 0;
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 = tanhf(sin(osc->offset * PERIOD_2_PI / osc->period) + 1);
frame = osc->lookup_table[osc->offset];
osc->offset = (osc->offset + 1) % osc->period;
}

Expand All @@ -83,20 +97,30 @@ 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->buffer_buffer);
free(player->sample_buffer);
free(player);
current_player = NULL;
}

void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
UNUSED(osc);
// Nothing to free now, but keeping this here in case I reimplement caching
if (osc->lookup_table != NULL) {
free(osc->lookup_table);
}
free(osc);
}

bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint16_t buffer_index) {
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 = (sample_frame(player->osc1, freq1) / 2) + (sample_frame(player->osc2, freq2) / 2);
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
Expand All @@ -109,7 +133,6 @@ bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint1
data = 255;
}

player->buffer_buffer[i] = data;
sample_buffer_start[i] = data;
}

Expand All @@ -119,8 +142,11 @@ bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint1
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
current_player = dtmf_dolphin_audio_alloc();

generate_waveform(current_player, freq1, freq2, 0);
generate_waveform(current_player, freq1, freq2, current_player->half_buffer_length);
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);
Expand All @@ -129,13 +155,10 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {

dtmf_dolphin_dma_start();
dtmf_dolphin_speaker_start();

return true;
}

bool dtmf_dolphin_audio_stop_tones() {
current_player->playing = false;

dtmf_dolphin_speaker_stop();
dtmf_dolphin_dma_stop();

Expand All @@ -147,24 +170,19 @@ bool dtmf_dolphin_audio_stop_tones() {
}

bool dtmf_dolphin_audio_handle_tick() {
DTMFDolphinCustomEvent event;

if(furi_message_queue_get(current_player->queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == DTMFDolphinEventDMAHalfTransfer) {
generate_waveform(
current_player,
(double) current_player->osc1->cached_freq,
(double) current_player->osc2->cached_freq,
0);
return true;
} else if (event.type == DTMFDolphinEventDMAFullTransfer) {
generate_waveform(
current_player,
(double) current_player->osc1->cached_freq,
(double) current_player->osc2->cached_freq,
current_player->half_buffer_length);
return true;
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 false;
return handled;
}
4 changes: 1 addition & 3 deletions dtmf_dolphin_audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
typedef struct {
float cached_freq;
size_t period;
size_t buffer_length;
float* sample_buffer;
float* lookup_table;
uint16_t offset;
} DTMFDolphinOsc;

Expand All @@ -21,7 +20,6 @@ typedef struct {
uint8_t *buffer_buffer;
uint16_t *sample_buffer;
float volume;
bool playing;
FuriMessageQueue *queue;
DTMFDolphinOsc *osc1;
DTMFDolphinOsc *osc2;
Expand Down
18 changes: 9 additions & 9 deletions dtmf_dolphin_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = {
{"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, 3, 2}, 0, 0, 0},
{"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0},
}
};

Expand All @@ -73,10 +73,10 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = {
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
.tone_count = 4,
.tones = {
{"Nickel", 1700.0, 2200.0, {0, 0, 4}, 1, 66, 0},
{"Dime", 1700.0, 2200.0, {1, 0, 4}, 2, 66, 66},
{"Quarter", 1700.0, 2200.0, {2, 0, 4}, 5, 33, 33},
{"Dollar", 1700.0, 2200.0, {3, 0, 4}, 1, 650, 0},
{"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},
}
};

Expand All @@ -95,9 +95,9 @@ DTMFDolphinSceneData DTMFDolphinSceneDataMisc = {
.block = DTMF_DOLPHIN_TONE_BLOCK_MISC,
.tone_count = 3,
.tones = {
{"CCITT 11", 700.0, 1700.0, {0, 0, 4}, 0, 0, 0},
{"CCITT 12", 900.0, 1700.0, {1, 0, 4}, 0, 0, 0},
{"CCITT KP2", 1300.0, 1700.0, {2, 0, 4}, 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},
}
};

Expand Down Expand Up @@ -179,7 +179,7 @@ void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t
}
tmp_rowspan[tones.pos.row] += tones.pos.span;
if (tmp_rowspan[tones.pos.row] > max_span[0])
max_span[0] = max_span[tones.pos.row];
max_span[0] = tmp_rowspan[tones.pos.row];
}
max_rows[0]++;
max_cols[0]++;
Expand Down
4 changes: 4 additions & 0 deletions scenes/dtmf_dolphin_scene_start.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {

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,
Expand All @@ -49,6 +50,9 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
} 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);
}
consumed = true;
}
Expand Down
Loading

0 comments on commit cb02b72

Please sign in to comment.