dtmf_dolphin_audio.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include "dtmf_dolphin_audio.h"
  2. DTMFDolphinAudio *current_player;
  3. static void dtmf_dolphin_audio_dma_isr(void* ctx) {
  4. FuriMessageQueue *event_queue = ctx;
  5. if (LL_DMA_IsActiveFlag_HT1(DMA1)) {
  6. LL_DMA_ClearFlag_HT1(DMA1);
  7. DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
  8. furi_message_queue_put(event_queue, &event, 0);
  9. }
  10. if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
  11. LL_DMA_ClearFlag_TC1(DMA1);
  12. DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer};
  13. furi_message_queue_put(event_queue, &event, 0);
  14. }
  15. }
  16. void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
  17. for (size_t i = 0; i < player->buffer_length; i++) {
  18. player->sample_buffer[i] = 0;
  19. }
  20. }
  21. DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
  22. DTMFDolphinOsc *osc = malloc(sizeof(DTMFDolphinOsc));
  23. osc->cached_freq = 0;
  24. osc->offset = 0;
  25. osc->period = 0;
  26. osc->lookup_table = NULL;
  27. return osc;
  28. }
  29. DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
  30. DTMFDolphinPulseFilter *pf = malloc(sizeof(DTMFDolphinPulseFilter));
  31. pf->duration = 0;
  32. pf->period = 0;
  33. pf->offset = 0;
  34. pf->lookup_table = NULL;
  35. return pf;
  36. }
  37. DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
  38. DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio));
  39. player->buffer_length = SAMPLE_BUFFER_LENGTH;
  40. player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
  41. player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
  42. player->osc1 = dtmf_dolphin_osc_alloc();
  43. player->osc2 = dtmf_dolphin_osc_alloc();
  44. player->volume = 1.0f;
  45. player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
  46. player->filter = dtmf_dolphin_pulse_filter_alloc();
  47. player->playing = false;
  48. dtmf_dolphin_audio_clear_samples(player);
  49. return player;
  50. }
  51. size_t calc_waveform_period(float freq) {
  52. if (!freq) {
  53. return 0;
  54. }
  55. // DMA Rate calculation, thanks to Dr_Zlo
  56. float dma_rate = CPU_CLOCK_FREQ \
  57. / 2 \
  58. / DTMF_DOLPHIN_HAL_DMA_PRESCALER \
  59. / (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
  60. // Using a constant scaling modifier, which likely represents
  61. // the combined system overhead and isr latency.
  62. return (uint16_t) dma_rate * 2 / freq * 0.801923;
  63. }
  64. void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
  65. if (osc->lookup_table != NULL) {
  66. free(osc->lookup_table);
  67. }
  68. osc->offset = 0;
  69. osc->cached_freq = freq;
  70. osc->period = calc_waveform_period(freq);
  71. if (!osc->period) {
  72. osc->lookup_table = NULL;
  73. return;
  74. }
  75. osc->lookup_table = malloc(sizeof(float) * osc->period);
  76. for (size_t i = 0; i < osc->period; i++) {
  77. osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
  78. }
  79. }
  80. void filter_generate_lookup_table(DTMFDolphinPulseFilter* pf, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
  81. if (pf->lookup_table != NULL) {
  82. free(pf->lookup_table);
  83. }
  84. pf->offset = 0;
  85. uint16_t gap_period = calc_waveform_period(1000 / (float) gap_ms);
  86. uint16_t pulse_period = calc_waveform_period(1000 / (float) pulse_ms);
  87. pf->period = pulse_period + gap_period;
  88. if (!pf->period) {
  89. pf->lookup_table = NULL;
  90. return;
  91. }
  92. pf->duration = pf->period * pulses;
  93. pf->lookup_table = malloc(sizeof(bool) * pf->duration);
  94. for (size_t i = 0; i < pf->duration; i++) {
  95. pf->lookup_table[i] = i % pf->period < pulse_period;
  96. }
  97. }
  98. float sample_frame(DTMFDolphinOsc* osc) {
  99. float frame = 0.0;
  100. if (osc->period) {
  101. frame = osc->lookup_table[osc->offset];
  102. osc->offset = (osc->offset + 1) % osc->period;
  103. }
  104. return frame;
  105. }
  106. bool sample_filter(DTMFDolphinPulseFilter* pf) {
  107. bool frame = true;
  108. if (pf->duration) {
  109. if (pf->offset < pf->duration) {
  110. frame = pf->lookup_table[pf->offset];
  111. pf->offset = pf->offset + 1;
  112. } else {
  113. frame = false;
  114. }
  115. }
  116. return frame;
  117. }
  118. void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
  119. if (osc->lookup_table != NULL) {
  120. free(osc->lookup_table);
  121. }
  122. free(osc);
  123. }
  124. void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
  125. if (pf->lookup_table != NULL) {
  126. free(pf->lookup_table);
  127. }
  128. free(pf);
  129. }
  130. void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
  131. furi_message_queue_free(player->queue);
  132. dtmf_dolphin_osc_free(player->osc1);
  133. dtmf_dolphin_osc_free(player->osc2);
  134. dtmf_dolphin_filter_free(player->filter);
  135. free(player->sample_buffer);
  136. free(player);
  137. current_player = NULL;
  138. }
  139. bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
  140. uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
  141. for (size_t i = 0; i < player->half_buffer_length; i++) {
  142. float data = 0;
  143. if (player->osc2->period) {
  144. data = \
  145. (sample_frame(player->osc1) / 2) + \
  146. (sample_frame(player->osc2) / 2);
  147. } else {
  148. data = (sample_frame(player->osc1));
  149. }
  150. data *= sample_filter(player->filter) ? player->volume : 0.0;
  151. data *= UINT8_MAX / 2; // scale -128..127
  152. data += UINT8_MAX / 2; // to unsigned
  153. if(data < 0) {
  154. data = 0;
  155. }
  156. if(data > 255) {
  157. data = 255;
  158. }
  159. sample_buffer_start[i] = data;
  160. }
  161. return true;
  162. }
  163. bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) {
  164. if (current_player != NULL && current_player->playing) {
  165. // Cannot start playing while still playing something else
  166. return false;
  167. }
  168. current_player = dtmf_dolphin_audio_alloc();
  169. osc_generate_lookup_table(current_player->osc1, freq1);
  170. osc_generate_lookup_table(current_player->osc2, freq2);
  171. filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);
  172. generate_waveform(current_player, 0);
  173. generate_waveform(current_player, current_player->half_buffer_length);
  174. dtmf_dolphin_speaker_init();
  175. dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
  176. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
  177. dtmf_dolphin_dma_start();
  178. dtmf_dolphin_speaker_start();
  179. current_player->playing = true;
  180. return true;
  181. }
  182. bool dtmf_dolphin_audio_stop_tones() {
  183. if (current_player != NULL && !current_player->playing) {
  184. // Can't stop a player that isn't playing.
  185. return false;
  186. }
  187. while(current_player->filter->offset > 0 && current_player->filter->offset < current_player->filter->duration) {
  188. // run remaining ticks if needed to complete filter sequence
  189. dtmf_dolphin_audio_handle_tick();
  190. }
  191. dtmf_dolphin_speaker_stop();
  192. dtmf_dolphin_dma_stop();
  193. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
  194. dtmf_dolphin_audio_free(current_player);
  195. return true;
  196. }
  197. bool dtmf_dolphin_audio_handle_tick() {
  198. bool handled = false;
  199. if (current_player) {
  200. DTMFDolphinCustomEvent event;
  201. if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
  202. if(event.type == DTMFDolphinEventDMAHalfTransfer) {
  203. generate_waveform(current_player, 0);
  204. handled = true;
  205. } else if (event.type == DTMFDolphinEventDMAFullTransfer) {
  206. generate_waveform(current_player, current_player->half_buffer_length);
  207. handled = true;
  208. }
  209. }
  210. }
  211. return handled;
  212. }