dtmf_dolphin_audio.c 7.9 KB

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