logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

audio_dac_additive.c (20130B)


  1. /* Copyright 2016-2019 Jack Humbert
  2. * Copyright 2020 JohSchneider
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "audio.h"
  18. #include "gpio.h"
  19. #include <math.h>
  20. #include "util.h"
  21. // Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file.
  22. #pragma GCC diagnostic push
  23. #pragma GCC diagnostic ignored "-Wtautological-compare"
  24. /*
  25. Audio Driver: DAC
  26. which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA
  27. it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
  28. this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
  29. */
  30. #if !defined(AUDIO_PIN)
  31. # error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
  32. #endif
  33. #if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
  34. # pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though."
  35. #endif
  36. #if !defined(AUDIO_PIN_ALT)
  37. // no ALT pin defined is valid, but the c-ifs below need some value set
  38. # define AUDIO_PIN_ALT PAL_NOLINE
  39. #endif
  40. #if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
  41. # define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
  42. #endif
  43. #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
  44. /* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
  45. */
  46. static const dacsample_t dac_buffer_sine[] = {
  47. // 256 values, max 4095
  48. 0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, 0x27, 0x32, 0x3d, 0x4a, 0x58, 0x67, 0x78, 0x89, 0x9c, 0xb0, 0xc5, 0xdb, 0xf2, 0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe,
  49. 0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2, 0xdb, 0xc5, 0xb0, 0x9c, 0x89, 0x78, 0x67, 0x58, 0x4a, 0x3d, 0x32, 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1,
  50. };
  51. #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
  52. #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
  53. static const dacsample_t dac_buffer_triangle[] = {
  54. // 256 values, max 4095
  55. 0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf,
  56. 0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0, 0xc0, 0xa0, 0x80, 0x60, 0x40, 0x20,
  57. };
  58. #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
  59. #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
  60. static const dacsample_t dac_buffer_square[] = {
  61. AUDIO_DAC_OFF_VALUE, // first and
  62. AUDIO_DAC_SAMPLE_MAX, // second steps
  63. };
  64. #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
  65. /*
  66. // four steps: 0, 1/3, 2/3 and 1
  67. static const dacsample_t dac_buffer_staircase[] = {
  68. 0,
  69. AUDIO_DAC_SAMPLE_MAX / 3,
  70. 2 * AUDIO_DAC_SAMPLE_MAX / 3,
  71. AUDIO_DAC_SAMPLE_MAX,
  72. }
  73. */
  74. #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
  75. static const dacsample_t dac_buffer_trapezoid[] = {
  76. 0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
  77. 0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf, 0x7f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  78. };
  79. #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
  80. static dacsample_t dac_buffer[AUDIO_DAC_BUFFER_SIZE];
  81. /* keep track of the sample position for for each frequency */
  82. static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
  83. static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0};
  84. static uint8_t active_tones_snapshot_length = 0;
  85. typedef enum {
  86. OUTPUT_SHOULD_START,
  87. OUTPUT_RUN_NORMALLY,
  88. // path 1: wait for zero, then change/update active tones
  89. OUTPUT_TONES_CHANGED,
  90. OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
  91. // path 2: hardware should stop, wait for zero then turn output off = stop the timer
  92. OUTPUT_SHOULD_STOP,
  93. OUTPUT_REACHED_ZERO_BEFORE_OFF,
  94. OUTPUT_OFF,
  95. OUTPUT_OFF_1,
  96. OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level
  97. number_of_output_states
  98. } output_states_t;
  99. output_states_t state = OUTPUT_OFF_2;
  100. /**
  101. * Generation of the waveform being passed to the callback. Declared weak so users
  102. * can override it with their own wave-forms/noises.
  103. */
  104. __attribute__((weak)) uint16_t dac_value_generate(void) {
  105. // DAC is running/asking for values but snapshot length is zero -> must be playing a pause
  106. if (active_tones_snapshot_length == 0) {
  107. return AUDIO_DAC_OFF_VALUE;
  108. }
  109. /* doing additive wave synthesis over all currently playing tones = adding up
  110. * sine-wave-samples for each frequency, scaled by the number of active tones
  111. */
  112. uint_fast16_t value = 0;
  113. float frequency = 0.0f;
  114. #if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
  115. const size_t wavetable_length = ARRAY_SIZE(dac_buffer_sine);
  116. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
  117. const size_t wavetable_length = ARRAY_SIZE(dac_buffer_triangle);
  118. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
  119. const size_t wavetable_length = ARRAY_SIZE(dac_buffer_trapezoid);
  120. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
  121. const size_t wavetable_length = ARRAY_SIZE(dac_buffer_square);
  122. #endif
  123. for (size_t i = 0; i < active_tones_snapshot_length; i++) {
  124. /* Note: a user implementation does not have to rely on the active_tones_snapshot, but
  125. * could directly query the active frequencies through audio_get_processed_frequency */
  126. frequency = active_tones_snapshot[i];
  127. float new_dac_if = dac_if[i];
  128. new_dac_if += frequency * ((float)wavetable_length / AUDIO_DAC_SAMPLE_RATE * 2.0f / 3.0f);
  129. /*Note: the 2/3 are necessary to get the correct frequencies on the
  130. * DAC output (as measured with an oscilloscope), since the gpt
  131. * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
  132. * is called twice per conversion.*/
  133. while (new_dac_if >= wavetable_length)
  134. new_dac_if -= wavetable_length;
  135. dac_if[i] = new_dac_if;
  136. // Wavetable generation/lookup
  137. size_t dac_i = (size_t)new_dac_if;
  138. #if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
  139. value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
  140. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
  141. value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
  142. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
  143. value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
  144. #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
  145. value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
  146. #endif
  147. /*
  148. // SINE
  149. value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
  150. // TRIANGLE
  151. value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
  152. // SQUARE
  153. value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
  154. //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
  155. */
  156. // STAIRS (mostly usefully as test-pattern)
  157. // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
  158. }
  159. return value;
  160. }
  161. /**
  162. * DAC streaming callback. Does all of the main computing for playing songs.
  163. *
  164. * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
  165. */
  166. static void dac_end(DACDriver *dacp) {
  167. dacsample_t *sample_p = (dacp)->samples;
  168. // work on the other half of the buffer
  169. if (dacIsBufferComplete(dacp)) {
  170. sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'
  171. }
  172. for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
  173. if (OUTPUT_OFF <= state) {
  174. sample_p[s] = AUDIO_DAC_OFF_VALUE;
  175. continue;
  176. } else {
  177. sample_p[s] = dac_value_generate();
  178. }
  179. /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
  180. * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
  181. * * *
  182. * * *
  183. * ---------------------------------------------------------
  184. * * * } AUDIO_DAC_SAMPLE_MAX/100
  185. * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
  186. * * * } AUDIO_DAC_SAMPLE_MAX/100
  187. * ---------------------------------------------------------
  188. * *
  189. * * *
  190. * * *
  191. * =====*=*================================================= 0x0
  192. */
  193. if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below
  194. (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above
  195. ) {
  196. if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
  197. state = OUTPUT_RUN_NORMALLY;
  198. } else if (OUTPUT_TONES_CHANGED == state) {
  199. state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
  200. } else if (OUTPUT_SHOULD_STOP == state) {
  201. state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
  202. }
  203. }
  204. // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
  205. if (OUTPUT_SHOULD_START == state) {
  206. sample_p[s] = AUDIO_DAC_OFF_VALUE;
  207. }
  208. if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
  209. uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
  210. active_tones_snapshot_length = 0;
  211. // update the snapshot - once, and only on occasion that something changed;
  212. // -> saves cpu cycles (?)
  213. for (uint8_t i = 0; i < active_tones; i++) {
  214. float freq = audio_get_processed_frequency(i);
  215. if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
  216. active_tones_snapshot[active_tones_snapshot_length++] = freq;
  217. }
  218. }
  219. if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
  220. state = OUTPUT_OFF;
  221. }
  222. if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
  223. state = OUTPUT_RUN_NORMALLY;
  224. }
  225. }
  226. }
  227. // update audio internal state (note position, current_note, ...)
  228. if (audio_update_state()) {
  229. if (OUTPUT_SHOULD_STOP != state) {
  230. state = OUTPUT_TONES_CHANGED;
  231. }
  232. }
  233. if (OUTPUT_OFF <= state) {
  234. if (OUTPUT_OFF_2 == state) {
  235. // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
  236. gptStopTimer(&GPTD6);
  237. } else {
  238. state++;
  239. }
  240. }
  241. }
  242. static void dac_error(DACDriver *dacp, dacerror_t err) {
  243. (void)dacp;
  244. (void)err;
  245. chSysHalt("DAC failure. halp");
  246. }
  247. static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
  248. .callback = NULL,
  249. .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
  250. .dier = 0U};
  251. static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
  252. /**
  253. * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
  254. * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
  255. * to be a third of what we expect.
  256. *
  257. * Here are all the values for DAC_TRG (TSEL in the ref manual)
  258. * TIM15_TRGO 0b011
  259. * TIM2_TRGO 0b100
  260. * TIM3_TRGO 0b001
  261. * TIM6_TRGO 0b000
  262. * TIM7_TRGO 0b010
  263. * EXTI9 0b110
  264. * SWTRIG 0b111
  265. */
  266. static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
  267. void audio_driver_initialize_impl(void) {
  268. if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
  269. palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
  270. dacStart(&DACD1, &dac_conf);
  271. }
  272. if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
  273. palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
  274. dacStart(&DACD2, &dac_conf);
  275. }
  276. /* enable the output buffer, to directly drive external loads with no additional circuitry
  277. *
  278. * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
  279. * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
  280. * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
  281. *
  282. * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
  283. * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
  284. */
  285. DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
  286. DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
  287. /* Start the DAC output with all off values. This buffer will then get fed
  288. * with samples from dac_end, which will play notes.
  289. */
  290. for (size_t i = 0; i < AUDIO_DAC_BUFFER_SIZE; i++) {
  291. dac_buffer[i] = AUDIO_DAC_OFF_VALUE;
  292. }
  293. if (AUDIO_PIN == A4) {
  294. dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE);
  295. } else if (AUDIO_PIN == A5) {
  296. dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE);
  297. }
  298. // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
  299. #if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
  300. if (AUDIO_PIN_ALT == A4) {
  301. dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
  302. } else if (AUDIO_PIN_ALT == A5) {
  303. dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
  304. }
  305. #endif
  306. gptStart(&GPTD6, &gpt6cfg1);
  307. }
  308. void audio_driver_stop_impl(void) {
  309. state = OUTPUT_SHOULD_STOP;
  310. }
  311. void audio_driver_start_impl(void) {
  312. gptStartContinuous(&GPTD6, 2U);
  313. for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
  314. dac_if[i] = 0.0f;
  315. active_tones_snapshot[i] = 0.0f;
  316. }
  317. active_tones_snapshot_length = 0;
  318. state = OUTPUT_SHOULD_START;
  319. }
  320. #pragma GCC diagnostic pop