Mozzi  version v2.0
sound synthesis library for Arduino
MozziGuts_impl_ESP32.hpp
1 /*
2  * MozziGuts_impl_ESP32.hpp
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2020-2024 Dieter Vandoren, Thomas Friedrichsmeier and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10 */
11 
12 #if !(IS_ESP32())
13 # error "Wrong implementation included for this platform"
14 #endif
15 
16 namespace MozziPrivate {
18 #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD)
19 #error not yet implemented
20 
21 #define getADCReading() 0
22 #define channelNumToIndex(channel) channel
23 uint8_t adcPinToChannelNum(uint8_t pin) {
24  return pin;
25 }
26 void adcStartConversion(uint8_t channel) {
27 }
28 void startSecondADCReadOnCurrentChannel() {
29 }
30 void setupMozziADC(int8_t speed) {
31 }
32 void setupFastAnalogRead(int8_t speed) {
33 }
34 
35 #endif
37 
38 
40 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
41 } // namespace MozziPrivate
42 # include <driver/i2s.h> // for I2S-based output modes, including - technically - internal DAC
43 namespace MozziPrivate {
44 const i2s_port_t i2s_num = MOZZI_I2S_PORT;
45 
46 // On ESP32 we cannot test wether the DMA buffer has room. Instead, we have to use a one-sample mini buffer. In each iteration we
47 // _try_ to write that sample to the DMA buffer, and if successful, we can buffer the next sample. Somewhat cumbersome, but works.
48 // TODO: Should ESP32 gain an implemenation of i2s_available(), we should switch to using that, instead.
49 static bool _esp32_can_buffer_next = true;
50 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
51 static uint16_t _esp32_prev_sample[2];
52 # define ESP_SAMPLE_SIZE (2*sizeof(uint16_t))
53 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC)
54 static int16_t _esp32_prev_sample[2];
55 # define ESP_SAMPLE_SIZE (2*sizeof(int16_t))
56 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
57 static uint32_t _esp32_prev_sample[PDM_RESOLUTION];
58 # define ESP_SAMPLE_SIZE (PDM_RESOLUTION*sizeof(uint32_t))
59 # endif
60 
61 inline bool esp32_tryWriteSample() {
62  size_t bytes_written;
63  i2s_write(i2s_num, &_esp32_prev_sample, ESP_SAMPLE_SIZE, &bytes_written, 0);
64  return (bytes_written != 0);
65 }
66 
67 inline bool canBufferAudioOutput() {
68  if (_esp32_can_buffer_next) return true;
69  _esp32_can_buffer_next = esp32_tryWriteSample();
70  return _esp32_can_buffer_next;
71 }
72 
73 inline void audioOutput(const AudioOutput f) {
74 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
75  _esp32_prev_sample[0] = (f.l() + MOZZI_AUDIO_BIAS) << 8;
76 # if (MOZZI_AUDIO_CHANNELS > 1)
77  _esp32_prev_sample[1] = (f.r() + MOZZI_AUDIO_BIAS) << 8;
78 # else
79  // For simplicity of code, even in mono, we're writing stereo samples
80  _esp32_prev_sample[1] = _esp32_prev_sample[0];
81 # endif
82 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
83  for (uint8_t i=0; i<MOZZI_PDM_RESOLUTION; ++i) {
84  _esp32_prev_sample[i] = pdmCode32(f.l() + MOZZI_AUDIO_BIAS);
85  }
86 # else
87  // PT8211 takes signed samples
88  _esp32_prev_sample[0] = f.l();
89  _esp32_prev_sample[1] = f.r();
90 # endif
91  _esp32_can_buffer_next = esp32_tryWriteSample();
92 }
93 #endif
94 
95 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED)
96 
97 } // namespace MozziPrivate
98 # include <driver/timer.h>
99 namespace MozziPrivate {
100 
101 void CACHED_FUNCTION_ATTR timer0_audio_output_isr(void *) {
102  TIMERG0.int_clr_timers.t0 = 1;
103  TIMERG0.hw_timer[0].config.alarm_en = 1;
104  defaultAudioOutput();
105 }
106 #endif
107 
108 static void startAudio() {
109 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) // for external audio output, set up a timer running a audio rate
110  static intr_handle_t s_timer_handle;
111  const int div = 2;
112  timer_config_t config = {
113  .alarm_en = (timer_alarm_t)true,
114  .counter_en = (timer_start_t)false,
115  .intr_type = (timer_intr_mode_t) TIMER_INTR_LEVEL,
116  .counter_dir = TIMER_COUNT_UP,
117  .auto_reload = (timer_autoreload_t) true,
118  .divider = div // For max available precision: The APB_CLK clock signal is running at 80 MHz, i.e. 2/80 uS per tick
119  // Min acceptable value is 2
120  };
121  timer_init(TIMER_GROUP_0, TIMER_0, &config);
122  timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
123  timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 80000000UL / MOZZI_AUDIO_RATE / div);
124  timer_enable_intr(TIMER_GROUP_0, TIMER_0);
125  timer_isr_register(TIMER_GROUP_0, TIMER_0, &timer0_audio_output_isr, nullptr, 0, &s_timer_handle);
126  timer_start(TIMER_GROUP_0, TIMER_0);
127 
128 #elif !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM)
129  static const i2s_config_t i2s_config = {
130 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
131  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
132 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
133  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
134 # endif
135  .sample_rate = MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION,
136  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // only the top 8 bits will actually be used by the internal DAC, but using 8 bits straight away seems buggy
137  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // always use stereo output. mono seems to be buggy, and the overhead is insignifcant on the ESP32
138  .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB), // this appears to be the correct setting for internal DAC and PT8211, but not for other dacs
139  .intr_alloc_flags = 0, // default interrupt priority
140  .dma_buf_count = 8, // 8*128 bytes of buffer corresponds to 256 samples (2 channels, see above, 2 bytes per sample per channel)
141  .dma_buf_len = 128,
142  .use_apll = false
143  };
144 
145  i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
146 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
147  static const i2s_pin_config_t pin_config = {
148  .bck_io_num = MOZZI_I2S_PIN_BCK,
149  .ws_io_num = MOZZI_I2S_PIN_WS,
150  .data_out_num = MOZZI_I2S_PIN_DATA,
151  .data_in_num = -1
152  };
153  i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
154 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
155  i2s_set_pin((i2s_port_t)i2s_num, NULL);
156  i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
157 # endif
158  i2s_zero_dma_buffer((i2s_port_t)i2s_num);
159 
160 #endif
161 }
162 
163 void stopMozzi() {
164  // TODO: implement me
165 }
167 
169 void MozziRandPrivate::autoSeed() {
170  x = esp_random();
171  y = esp_random();
172  z = esp_random();
173 }
175 
176 #undef ESP_SAMPLE_SIZE // only used inside this file
177 
178 } // namespace MozziPrivate
#define MOZZI_AUDIO_RATE
Defines the audio rate, i.e.
void stopMozzi()
Stops audio and control interrupts and restores the timers to the values they had before Mozzi was st...
Definition: MozziGuts.hpp:303
Internal.
Definition: mozzi_rand_p.h:15
This struct encapsulates one frame of mono audio output.
Definition: AudioOutput.h:111