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

Commit

Permalink
Merge pull request #2 from litui/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
litui authored Sep 19, 2022
2 parents 508d20a + cb02b72 commit 78050ff
Show file tree
Hide file tree
Showing 26 changed files with 628 additions and 685 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.
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ App(
"gui",
"dialogs",
],
stack_size=4 * 1024,
stack_size=8 * 1024,
order=20,
)
46 changes: 2 additions & 44 deletions dtmf_dolphin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand All @@ -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);

Expand Down
188 changes: 188 additions & 0 deletions dtmf_dolphin_audio.c
Original file line number Diff line number Diff line change
@@ -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;
}
40 changes: 40 additions & 0 deletions dtmf_dolphin_audio.h
Original file line number Diff line number Diff line change
@@ -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();
Loading

0 comments on commit 78050ff

Please sign in to comment.