dtmf_dolphin_audio.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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 = { 0 };
  23. osc->cached_freq = 0;
  24. osc->offset = 0;
  25. osc->period = 0;
  26. return osc;
  27. }
  28. DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
  29. DTMFDolphinAudio player;
  30. player.buffer_length = SAMPLE_BUFFER_LENGTH;
  31. player.half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
  32. player.buffer_buffer = malloc(sizeof(uint8_t) * player.buffer_length);
  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.playing = false;
  37. player.volume = 2.0f;
  38. player.queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
  39. dtmf_dolphin_audio_clear_samples(&player);
  40. return false;
  41. }
  42. size_t calc_waveform_period(float freq) {
  43. if (!freq) {
  44. return 0;
  45. }
  46. // DMA Rate constant, thanks to Dr_Zlo
  47. float dma_rate = CPU_CLOCK_FREQ \
  48. / 2 \
  49. / DTMF_DOLPHIN_HAL_DMA_PRESCALER \
  50. / (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
  51. return (uint16_t) (dma_rate / freq);
  52. }
  53. float sample_frame(DTMFDolphinOsc* osc, float freq) {
  54. float frame = 0.0;
  55. if (freq != osc->cached_freq || !osc->period) {
  56. osc->cached_freq = freq;
  57. osc->period = calc_waveform_period(freq);
  58. osc->offset = 0;
  59. }
  60. if (osc->period) {
  61. frame = tanhf(sin(osc->offset * PERIOD_2_PI / osc->period) + 1);
  62. osc->offset = (osc->offset + 1) % osc->period;
  63. }
  64. return frame;
  65. }
  66. void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
  67. furi_message_queue_free(player->queue);
  68. dtmf_dolphin_osc_free(player->osc1);
  69. dtmf_dolphin_osc_free(player->osc2);
  70. free(player->buffer_buffer);
  71. free(player->sample_buffer);
  72. }
  73. void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
  74. UNUSED(osc);
  75. // Nothing to free now, but keeping this here in case I reimplement caching
  76. }
  77. bool generate_waveform(DTMFDolphinAudio* player, float freq1, float freq2, uint16_t buffer_index) {
  78. uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
  79. for (size_t i = 0; i < player->half_buffer_length; i++) {
  80. float data = (sample_frame(player->osc1, freq1) / 2) + (sample_frame(player->osc2, freq2) / 2);
  81. data *= player->volume;
  82. data *= UINT8_MAX / 2; // scale -128..127
  83. data += UINT8_MAX / 2; // to unsigned
  84. if(data < 0) {
  85. data = 0;
  86. }
  87. if(data > 255) {
  88. data = 255;
  89. }
  90. player->buffer_buffer[i] = data;
  91. sample_buffer_start[i] = data;
  92. }
  93. return true;
  94. }
  95. bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) {
  96. current_player = dtmf_dolphin_audio_alloc();
  97. generate_waveform(current_player, freq1, freq2, 0);
  98. generate_waveform(current_player, freq1, freq2, current_player->half_buffer_length);
  99. dtmf_dolphin_speaker_init();
  100. dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
  101. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
  102. dtmf_dolphin_dma_start();
  103. dtmf_dolphin_speaker_start();
  104. return true;
  105. }
  106. bool dtmf_dolphin_audio_stop_tones() {
  107. current_player->playing = false;
  108. dtmf_dolphin_speaker_stop();
  109. dtmf_dolphin_dma_stop();
  110. furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
  111. dtmf_dolphin_audio_free(current_player);
  112. return true;
  113. }
  114. bool dtmf_dolphin_audio_handle_tick() {
  115. DTMFDolphinCustomEvent event;
  116. if(furi_message_queue_get(current_player->queue, &event, FuriWaitForever) == FuriStatusOk) {
  117. if(event.type == DTMFDolphinEventDMAHalfTransfer) {
  118. generate_waveform(
  119. current_player,
  120. (double) current_player->osc1->cached_freq,
  121. (double) current_player->osc2->cached_freq,
  122. 0);
  123. return true;
  124. } else if (event.type == DTMFDolphinEventDMAFullTransfer) {
  125. generate_waveform(
  126. current_player,
  127. (double) current_player->osc1->cached_freq,
  128. (double) current_player->osc2->cached_freq,
  129. current_player->half_buffer_length);
  130. return true;
  131. }
  132. }
  133. return false;
  134. }