Mozzi  version v1.1.0
sound synthesis library for Arduino
MozziGuts_impl_ESP32.hpp
1 /*
2  * MozziGuts.cpp
3  *
4  * Copyright 2012 Tim Barrass.
5  *
6  * This file is part of Mozzi.
7  *
8  * Mozzi by Tim Barrass is licensed under a Creative Commons
9  * Attribution-NonCommercial-ShareAlike 4.0 International License.
10  *
11  */
12 
13 #if !(IS_ESP32())
14 # error "Wrong implementation included for this platform"
15 #endif
16 
17 ////// BEGIN analog input code ////////
18 //#define MOZZI_FAST_ANALOG_IMPLEMENTED // not yet
19 #define getADCReading() 0
20 #define channelNumToIndex(channel) channel
21 uint8_t adcPinToChannelNum(uint8_t pin) {
22  return pin;
23 }
24 void adcStartConversion(uint8_t channel) {
25 #warning Fast analog read not implemented on this platform
26 }
27 void startSecondADCReadOnCurrentChannel() {
28 #warning Fast analog read not implemented on this platform
29 }
30 void setupFastAnalogRead(int8_t speed) {
31 #warning Fast analog read not implemented on this platform
32 }
33 void setupMozziADC(int8_t speed) {
34 #warning Fast analog read not implemented on this platform
35 }
36 ////// END analog input code ////////
37 
38 
39 
40 
41 //// BEGIN AUDIO OUTPUT code ///////
42 #include <driver/i2s.h> // for I2S-based output modes
43 #include <driver/timer.h> // for EXTERNAL_AUDIO_OUTPUT
44 
45 #if (EXTERNAL_AUDIO_OUTPUT != true)
46 # include "AudioConfigESP32.h"
47 // 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
48 // _try_ to write that sample to the DMA buffer, and if successful, we can buffer the next sample. Somewhat cumbersome, but works.
49 // TODO: Should ESP32 gain an implemenation of i2s_available(), we should switch to using that, instead.
50 static bool _esp32_can_buffer_next = true;
52 static uint16_t _esp32_prev_sample[2];
53 # define ESP_SAMPLE_SIZE (2*sizeof(uint16_t))
54 # elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC)
55 static int16_t _esp32_prev_sample[2];
56 # define ESP_SAMPLE_SIZE (2*sizeof(int16_t))
57 # elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S)
58 static uint32_t _esp32_prev_sample[PDM_RESOLUTION];
59 # define ESP_SAMPLE_SIZE (PDM_RESOLUTION*sizeof(uint32_t))
60 # endif
61 
62 inline bool esp32_tryWriteSample() {
63  size_t bytes_written;
64  i2s_write(i2s_num, &_esp32_prev_sample, ESP_SAMPLE_SIZE, &bytes_written, 0);
65  return (bytes_written != 0);
66 }
67 
68 inline bool canBufferAudioOutput() {
69  if (_esp32_can_buffer_next) return true;
70  _esp32_can_buffer_next = esp32_tryWriteSample();
71  return _esp32_can_buffer_next;
72 }
73 
74 inline void audioOutput(const AudioOutput f) {
76  _esp32_prev_sample[0] = (f.l() + AUDIO_BIAS) << 8;
77 # if (AUDIO_CHANNELS > 1)
78  _esp32_prev_sample[1] = (f.r() + AUDIO_BIAS) << 8;
79 # else
80  // For simplicity of code, even in mono, we're writing stereo samples
81  _esp32_prev_sample[1] = _esp32_prev_sample[0];
82 # endif
83 # elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S)
84  for (uint8_t i=0; i<PDM_RESOLUTION; ++i) {
85  _esp32_prev_sample[i] = pdmCode32(f.l() + AUDIO_BIAS);
86  }
87 # else
88  // PT8211 takes signed samples
89  _esp32_prev_sample[0] = f.l();
90  _esp32_prev_sample[1] = f.r();
91 # endif
92  _esp32_can_buffer_next = esp32_tryWriteSample();
93 }
94 #endif
95 
96 #if (BYPASS_MOZZI_OUTPUT_BUFFER != true)
97 void CACHED_FUNCTION_ATTR timer0_audio_output_isr(void *) {
98  TIMERG0.int_clr_timers.t0 = 1;
99  TIMERG0.hw_timer[0].config.alarm_en = 1;
100  defaultAudioOutput();
101 }
102 #endif
103 
104 static void startAudio() {
105 #if (BYPASS_MOZZI_OUTPUT_BUFFER != true) // for external audio output, set up a timer running a audio rate
106  static intr_handle_t s_timer_handle;
107  const int div = 2;
108  timer_config_t config = {
109  .alarm_en = (timer_alarm_t)true,
110  .counter_en = (timer_start_t)false,
111  .intr_type = (timer_intr_mode_t) TIMER_INTR_LEVEL,
112  .counter_dir = TIMER_COUNT_UP,
113  .auto_reload = (timer_autoreload_t) true,
114  .divider = div // For max available precision: The APB_CLK clock signal is running at 80 MHz, i.e. 2/80 uS per tick
115  // Min acceptable value is 2
116  };
117  timer_init(TIMER_GROUP_0, TIMER_0, &config);
118  timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
119  timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 80000000UL / AUDIO_RATE / div);
120  timer_enable_intr(TIMER_GROUP_0, TIMER_0);
121  timer_isr_register(TIMER_GROUP_0, TIMER_0, &timer0_audio_output_isr, nullptr, 0, &s_timer_handle);
122  timer_start(TIMER_GROUP_0, TIMER_0);
123 
124 #else
125  static const i2s_config_t i2s_config = {
127  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
129  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
130 # endif
131  .sample_rate = AUDIO_RATE * PDM_RESOLUTION,
132  .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
133  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // always use stereo output. mono seems to be buggy, and the overhead is insignifcant on the ESP32
134  .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
135  .intr_alloc_flags = 0, // default interrupt priority
136  .dma_buf_count = 8, // 8*128 bytes of buffer corresponds to 256 samples (2 channels, see above, 2 bytes per sample per channel)
137  .dma_buf_len = 128,
138  .use_apll = false
139  };
140 
141  i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
143  static const i2s_pin_config_t pin_config = {
144  .bck_io_num = ESP32_I2S_BCK_PIN,
145  .ws_io_num = ESP32_I2S_WS_PIN,
146  .data_out_num = ESP32_I2S_DATA_PIN,
147  .data_in_num = -1
148  };
149  i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
151  i2s_set_pin((i2s_port_t)i2s_num, NULL);
152  i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
153 # endif
154  i2s_zero_dma_buffer((i2s_port_t)i2s_num);
155 
156 #endif
157 }
158 
159 void stopMozzi() {
160  // TODO: implement me
161 }
162 //// END AUDIO OUTPUT code ///////
#define BYPASS_MOZZI_OUTPUT_BUFFER
void stopMozzi()
Stops audio and control interrupts and restores the timers to the values they had before Mozzi was st...
#define PT8211_DAC
#define INTERNAL_DAC
void setupFastAnalogRead(int8_t speed)
NOTE: Code needed to set up faster than usual analog reads, e.g.
#define PDM_VIA_I2S
Definition: AudioConfigESP.h:9
#define ESP32_AUDIO_OUT_MODE
#define PDM_RESOLUTION
#define AUDIO_BIAS
Definition: MozziGuts.h:229