dtmf_dolphin_audio.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
  30. DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio));
  31. player->buffer_length = SAMPLE_BUFFER_LENGTH;
  32. player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
  33. player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
  34. player->osc1 = dtmf_dolphin_osc_alloc();
  35. player->osc2 = dtmf_dolphin_osc_alloc();
  36. player->volume = 1.0f;
  37. player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
  38. dtmf_dolphin_audio_clear_samples(player);
  39. return player;
  40. }
  41. size_t calc_waveform_period(float freq) {
  42. if (!freq) {
  43. return 0;
  44. }
  45. // DMA Rate calculation, thanks to Dr_Zlo
  46. float dma_rate = CPU_CLOCK_FREQ \
  47. / 2 \
  48. / DTMF_DOLPHIN_HAL_DMA_PRESCALER \
  49. / (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
  50. // Using a constant scaling modifier, which likely represents
  51. // the combined system overhead and isr latency.
  52. return (uint16_t) dma_rate * 2 / freq * 0.801923;
  53. }
  54. void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
  55. if (osc->lookup_table != NULL) {
  56. free(osc->lookup_table);
  57. }
  58. osc->offset = 0;
  59. osc->cached_freq = freq;
  60. osc->period = calc_waveform_period(freq);
  61. if (!osc->period) {
  62. osc->lookup_table = NULL;
  63. return;
  64. }
  65. osc->lookup_table = malloc(sizeof(float) * osc->period);
  66. for (size_t i = 0; i < osc->period; i++) {
  67. osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
  68. }
  69. }
  70. float sample_frame(DTMFDolphinOsc* osc) {
  71. float frame = 0.0;
  72. if (osc->period) {
  73. frame = osc->lookup_table[osc->offset];
  74. osc->offset = (osc->offset + 1) % osc->period;
  75. }
  76. return frame;
  77. }
  78. void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
  79. furi_message_queue_free(player->queue);
  80. dtmf_dolphin_osc_free(player->osc1);
  81. dtmf_dolphin_osc_free(player->osc2);
  82. free(player->sample_buffer);
  83. free(player);
  84. current_player = NULL;
  85. }
  86. void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
  87. if (osc->lookup_table != NULL) {
  88. free(osc->lookup_table);
  89. }
  90. free(osc);
  91. }
  92. bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
  93. uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
  94. for (size_t i = 0; i < player->half_buffer_length; i++) {
  95. float data = 0;
  96. if (player->osc2->period) {
  97. data = \
  98. (sample_frame(player->osc1) / 2) + \
  99. (sample_frame(player->osc2) / 2);
  100. } else {
  101. data = (sample_frame(player->osc1));
  102. }
  103. data *= player->volume;
  104. data *= UINT8_MAX / 2; // scale -128..127
  105. data += UINT8_MAX / 2; // to unsigned
  106. if(data < 0) {
  107. data = 0;
  108. }
  109. if(data > 255) {
  110. data = 255;
  111. }
  112. sample_buffer_start[i] = data;
  113. }
  114. return true;
  115. }
  116. bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
  117. current_player = dtmf_dolphin_audio_alloc();
  118. osc_generate_lookup_table(current_player->osc1, freq1);
  119. osc_generate_lookup_table(current_player->osc2, freq2);
  120. generate_waveform(current_player, 0);
  121. generate_waveform(current_player, current_player->half_buffer_length);
  122. dtmf_dolphin_speaker_init();
  123. dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
  124. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
  125. dtmf_dolphin_dma_start();
  126. dtmf_dolphin_speaker_start();
  127. return true;
  128. }
  129. bool dtmf_dolphin_audio_stop_tones() {
  130. dtmf_dolphin_speaker_stop();
  131. dtmf_dolphin_dma_stop();
  132. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
  133. dtmf_dolphin_audio_free(current_player);
  134. return true;
  135. }
  136. bool dtmf_dolphin_audio_handle_tick() {
  137. bool handled = false;
  138. if (current_player) {
  139. DTMFDolphinCustomEvent event;
  140. if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
  141. if(event.type == DTMFDolphinEventDMAHalfTransfer) {
  142. generate_waveform(current_player, 0);
  143. handled = true;
  144. } else if (event.type == DTMFDolphinEventDMAFullTransfer) {
  145. generate_waveform(current_player, current_player->half_buffer_length);
  146. handled = true;
  147. }
  148. }
  149. }
  150. return handled;
  151. }