@@ -27,52 +27,66 @@ void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
27
27
}
28
28
29
29
DTMFDolphinOsc * dtmf_dolphin_osc_alloc () {
30
- DTMFDolphinOsc * osc = { 0 } ;
30
+ DTMFDolphinOsc * osc = malloc ( sizeof ( DTMFDolphinOsc )) ;
31
31
osc -> cached_freq = 0 ;
32
32
osc -> offset = 0 ;
33
33
osc -> period = 0 ;
34
+ osc -> lookup_table = NULL ;
34
35
return osc ;
35
36
}
36
37
37
38
DTMFDolphinAudio * dtmf_dolphin_audio_alloc () {
38
- DTMFDolphinAudio player ;
39
- player .buffer_length = SAMPLE_BUFFER_LENGTH ;
40
- player .half_buffer_length = SAMPLE_BUFFER_LENGTH / 2 ;
41
- player .buffer_buffer = malloc (sizeof (uint8_t ) * player .buffer_length );
42
- player .sample_buffer = malloc (sizeof (uint16_t ) * player .buffer_length );
43
- player .osc1 = dtmf_dolphin_osc_alloc ();
44
- player .osc2 = dtmf_dolphin_osc_alloc ();
45
- player .playing = false;
46
- player .volume = 2.0f ;
47
- player .queue = furi_message_queue_alloc (10 , sizeof (DTMFDolphinCustomEvent ));
48
- dtmf_dolphin_audio_clear_samples (& player );
49
-
50
- return false;
39
+ DTMFDolphinAudio * player = malloc (sizeof (DTMFDolphinAudio ));
40
+ player -> buffer_length = SAMPLE_BUFFER_LENGTH ;
41
+ player -> half_buffer_length = SAMPLE_BUFFER_LENGTH / 2 ;
42
+ player -> sample_buffer = malloc (sizeof (uint16_t ) * player -> buffer_length );
43
+ player -> osc1 = dtmf_dolphin_osc_alloc ();
44
+ player -> osc2 = dtmf_dolphin_osc_alloc ();
45
+ player -> volume = 1.0f ;
46
+ player -> queue = furi_message_queue_alloc (10 , sizeof (DTMFDolphinCustomEvent ));
47
+ dtmf_dolphin_audio_clear_samples (player );
48
+
49
+ return player ;
51
50
}
52
51
53
52
size_t calc_waveform_period (float freq ) {
54
53
if (!freq ) {
55
54
return 0 ;
56
55
}
57
- // DMA Rate constant , thanks to Dr_Zlo
56
+ // DMA Rate calculation , thanks to Dr_Zlo
58
57
float dma_rate = CPU_CLOCK_FREQ \
59
58
/ 2 \
60
59
/ DTMF_DOLPHIN_HAL_DMA_PRESCALER \
61
60
/ (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1 );
62
61
63
- return (uint16_t ) (dma_rate / freq );
62
+ // Using a constant scaling modifier, which likely represents
63
+ // the combined system overhead and isr latency.
64
+ return (uint16_t ) dma_rate * 2 / freq * 0.801923 ;
64
65
}
65
66
66
- float sample_frame (DTMFDolphinOsc * osc , float freq ) {
67
- float frame = 0.0 ;
67
+ void osc_generate_lookup_table (DTMFDolphinOsc * osc , float freq ) {
68
+ if (osc -> lookup_table != NULL ) {
69
+ free (osc -> lookup_table );
70
+ }
71
+ osc -> offset = 0 ;
72
+ osc -> cached_freq = freq ;
73
+ osc -> period = calc_waveform_period (freq );
74
+ if (!osc -> period ) {
75
+ osc -> lookup_table = NULL ;
76
+ return ;
77
+ }
78
+ osc -> lookup_table = malloc (sizeof (float ) * osc -> period );
68
79
69
- if (freq != osc -> cached_freq || !osc -> period ) {
70
- osc -> cached_freq = freq ;
71
- osc -> period = calc_waveform_period (freq );
72
- osc -> offset = 0 ;
80
+ for (size_t i = 0 ; i < osc -> period ; i ++ ) {
81
+ osc -> lookup_table [i ] = sin (i * PERIOD_2_PI / osc -> period ) + 1 ;
73
82
}
83
+ }
84
+
85
+ float sample_frame (DTMFDolphinOsc * osc ) {
86
+ float frame = 0.0 ;
87
+
74
88
if (osc -> period ) {
75
- frame = tanhf ( sin ( osc -> offset * PERIOD_2_PI / osc -> period ) + 1 ) ;
89
+ frame = osc -> lookup_table [ osc -> offset ] ;
76
90
osc -> offset = (osc -> offset + 1 ) % osc -> period ;
77
91
}
78
92
@@ -83,20 +97,30 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
83
97
furi_message_queue_free (player -> queue );
84
98
dtmf_dolphin_osc_free (player -> osc1 );
85
99
dtmf_dolphin_osc_free (player -> osc2 );
86
- free (player -> buffer_buffer );
87
100
free (player -> sample_buffer );
101
+ free (player );
102
+ current_player = NULL ;
88
103
}
89
104
90
105
void dtmf_dolphin_osc_free (DTMFDolphinOsc * osc ) {
91
- UNUSED (osc );
92
- // Nothing to free now, but keeping this here in case I reimplement caching
106
+ if (osc -> lookup_table != NULL ) {
107
+ free (osc -> lookup_table );
108
+ }
109
+ free (osc );
93
110
}
94
111
95
- bool generate_waveform (DTMFDolphinAudio * player , float freq1 , float freq2 , uint16_t buffer_index ) {
112
+ bool generate_waveform (DTMFDolphinAudio * player , uint16_t buffer_index ) {
96
113
uint16_t * sample_buffer_start = & player -> sample_buffer [buffer_index ];
97
114
98
115
for (size_t i = 0 ; i < player -> half_buffer_length ; i ++ ) {
99
- float data = (sample_frame (player -> osc1 , freq1 ) / 2 ) + (sample_frame (player -> osc2 , freq2 ) / 2 );
116
+ float data = 0 ;
117
+ if (player -> osc2 -> period ) {
118
+ data = \
119
+ (sample_frame (player -> osc1 ) / 2 ) + \
120
+ (sample_frame (player -> osc2 ) / 2 );
121
+ } else {
122
+ data = (sample_frame (player -> osc1 ));
123
+ }
100
124
data *= player -> volume ;
101
125
data *= UINT8_MAX / 2 ; // scale -128..127
102
126
data += UINT8_MAX / 2 ; // to unsigned
@@ -109,7 +133,6 @@ bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint1
109
133
data = 255 ;
110
134
}
111
135
112
- player -> buffer_buffer [i ] = data ;
113
136
sample_buffer_start [i ] = data ;
114
137
}
115
138
@@ -119,8 +142,11 @@ bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint1
119
142
bool dtmf_dolphin_audio_play_tones (float freq1 , float freq2 ) {
120
143
current_player = dtmf_dolphin_audio_alloc ();
121
144
122
- generate_waveform (current_player , freq1 , freq2 , 0 );
123
- generate_waveform (current_player , freq1 , freq2 , current_player -> half_buffer_length );
145
+ osc_generate_lookup_table (current_player -> osc1 , freq1 );
146
+ osc_generate_lookup_table (current_player -> osc2 , freq2 );
147
+
148
+ generate_waveform (current_player , 0 );
149
+ generate_waveform (current_player , current_player -> half_buffer_length );
124
150
125
151
dtmf_dolphin_speaker_init ();
126
152
dtmf_dolphin_dma_init ((uint32_t )current_player -> sample_buffer , current_player -> buffer_length );
@@ -129,13 +155,10 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
129
155
130
156
dtmf_dolphin_dma_start ();
131
157
dtmf_dolphin_speaker_start ();
132
-
133
158
return true;
134
159
}
135
160
136
161
bool dtmf_dolphin_audio_stop_tones () {
137
- current_player -> playing = false;
138
-
139
162
dtmf_dolphin_speaker_stop ();
140
163
dtmf_dolphin_dma_stop ();
141
164
@@ -147,24 +170,19 @@ bool dtmf_dolphin_audio_stop_tones() {
147
170
}
148
171
149
172
bool dtmf_dolphin_audio_handle_tick () {
150
- DTMFDolphinCustomEvent event ;
151
-
152
- if (furi_message_queue_get (current_player -> queue , & event , FuriWaitForever ) == FuriStatusOk ) {
153
- if (event .type == DTMFDolphinEventDMAHalfTransfer ) {
154
- generate_waveform (
155
- current_player ,
156
- (double ) current_player -> osc1 -> cached_freq ,
157
- (double ) current_player -> osc2 -> cached_freq ,
158
- 0 );
159
- return true;
160
- } else if (event .type == DTMFDolphinEventDMAFullTransfer ) {
161
- generate_waveform (
162
- current_player ,
163
- (double ) current_player -> osc1 -> cached_freq ,
164
- (double ) current_player -> osc2 -> cached_freq ,
165
- current_player -> half_buffer_length );
166
- return true;
173
+ bool handled = false;
174
+
175
+ if (current_player ) {
176
+ DTMFDolphinCustomEvent event ;
177
+ if (furi_message_queue_get (current_player -> queue , & event , 250 ) == FuriStatusOk ) {
178
+ if (event .type == DTMFDolphinEventDMAHalfTransfer ) {
179
+ generate_waveform (current_player , 0 );
180
+ handled = true;
181
+ } else if (event .type == DTMFDolphinEventDMAFullTransfer ) {
182
+ generate_waveform (current_player , current_player -> half_buffer_length );
183
+ handled = true;
184
+ }
167
185
}
168
186
}
169
- return false ;
187
+ return handled ;
170
188
}
0 commit comments