arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
I2SESP32.h
1#pragma once
2
3#include "AudioToolsConfig.h"
4#if defined(ESP32) && defined(USE_I2S) && \
5 ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || \
6 defined(DOXYGEN)
7
8#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h"
9#include "driver/i2s.h"
10#include "esp_system.h"
11
12#ifndef I2S_MCLK_MULTIPLE_DEFAULT
13# define I2S_MCLK_MULTIPLE_DEFAULT ((i2s_mclk_multiple_t)0)
14#endif
15
16#define IS_I2S_IMPLEMENTED
17
18namespace audio_tools {
19
28 friend class AnalogAudio;
29 friend class AudioKitStream;
30
31 public:
34 I2SConfigESP32 c(mode);
35 return c;
36 }
37
40 // nothing to do
41 if (is_started) {
42 if (info.equals(cfg)) return true;
43 if (info.equalsExSampleRate(cfg)) {
44 cfg.sample_rate = info.sample_rate;
45 LOGI("i2s_set_sample_rates: %d", info.sample_rate);
46 return i2s_set_sample_rates((i2s_port_t)cfg.port_no, cfg.sample_rate) == ESP_OK;
47 }
48 } else {
49 LOGE("not started");
50 }
51 return false;
52 }
53
55 bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); }
56
59 bool begin() { return !is_started ? begin(cfg) : true; }
60
63 TRACED();
64 this->cfg = cfg;
65 switch (cfg.rx_tx_mode) {
66 case TX_MODE:
67 return begin(cfg, cfg.pin_data, I2S_PIN_NO_CHANGE);
68 case RX_MODE:
69 // usually we expet cfg.pin_data but if the used assinged rx we might
70 // consider this one
71 return begin(
72 cfg, I2S_PIN_NO_CHANGE,
73 cfg.pin_data != I2S_PIN_NO_CHANGE ? cfg.pin_data : cfg.pin_data_rx);
74 default:
75 return begin(cfg, cfg.pin_data, cfg.pin_data_rx);
76 }
77 LOGE("Did not expect go get here");
78 }
79
81 int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; }
82
84 int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; }
85
87 void end() {
88 TRACED();
89 i2s_driver_uninstall(i2s_num);
90 is_started = false;
91 }
92
94 I2SConfigESP32 config() { return cfg; }
95
97 size_t writeBytes(const void *src, size_t size_bytes) {
98 TRACED();
99
100 size_t result = 0;
101 if (isNoChannelConversion(cfg)) {
102 if (i2s_write(i2s_num, src, size_bytes, &result, ticks_to_wait_write) !=
103 ESP_OK) {
104 TRACEE();
105 }
106 LOGD("i2s_write %d -> %d bytes", size_bytes, result);
107 } else {
108 result =
109 writeExpandChannel(i2s_num, cfg.bits_per_sample, src, size_bytes);
110 }
111 return result;
112 }
113
114 size_t readBytes(void *dest, size_t size_bytes) {
115 size_t result = 0;
116 if (isNoChannelConversion(cfg)) {
117 if (i2s_read(i2s_num, dest, size_bytes, &result, ticks_to_wait_read) !=
118 ESP_OK) {
119 TRACEE();
120 }
121 } else if (cfg.channels == 1) {
122 // I2S has always 2 channels. We support to reduce it to 1
123 uint8_t temp[size_bytes * 2];
124 if (i2s_read(i2s_num, temp, size_bytes * 2, &result,
125 ticks_to_wait_read) != ESP_OK) {
126 TRACEE();
127 }
128 // convert to 1 channel
129 switch (cfg.bits_per_sample) {
130 case 16: {
131 ChannelReducerT<int16_t> reducer16(1, 2);
132 result = reducer16.convert((uint8_t *)dest, temp, result);
133 } break;
134 case 24: {
135 ChannelReducerT<int24_t> reducer24(1, 2);
136 result = reducer24.convert((uint8_t *)dest, temp, result);
137 } break;
138 case 32: {
139 ChannelReducerT<int32_t> reducer32(1, 2);
140 result = reducer32.convert((uint8_t *)dest, temp, result);
141 } break;
142 default:
143 LOGE("invalid bits_per_sample: %d", cfg.bits_per_sample);
144 break;
145 }
146 } else {
147 LOGE("Invalid channels: %d", cfg.channels);
148 }
149 return result;
150 }
151
152 void setWaitTimeReadMs(TickType_t ms) {
153 ticks_to_wait_read = pdMS_TO_TICKS(ms);
154 }
155 void setWaitTimeWriteMs(TickType_t ms) {
156 ticks_to_wait_write = pdMS_TO_TICKS(ms);
157 }
158
159 protected:
160 I2SConfigESP32 cfg = defaultConfig(RX_MODE);
161 i2s_port_t i2s_num;
162 i2s_config_t i2s_config;
163 bool is_started = false;
164 TickType_t ticks_to_wait_read = portMAX_DELAY;
165 TickType_t ticks_to_wait_write = portMAX_DELAY;
166
167 bool isNoChannelConversion(I2SConfigESP32 cfg) {
168 if (cfg.channels == 2) return true;
169 if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ALL_RIGHT)
170 return true;
171 if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ALL_LEFT)
172 return true;
173 if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_RIGHT)
174 return true;
175 if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_LEFT)
176 return true;
177 return false;
178 }
179
181 bool begin(I2SConfigESP32 cfg, int txPin, int rxPin) {
182 TRACED();
183 cfg.logInfo();
184 this->cfg = cfg;
185 this->i2s_num = (i2s_port_t)cfg.port_no;
186 setChannels(cfg.channels);
187
188 i2s_config_t i2s_config_new = {
189 .mode = toMode(cfg),
190 .sample_rate = (eps32_i2s_sample_rate_type)cfg.sample_rate,
191 .bits_per_sample = (i2s_bits_per_sample_t)cfg.bits_per_sample,
192 .channel_format = (i2s_channel_fmt_t)cfg.channel_format,
193 .communication_format = toCommFormat(cfg.i2s_format),
194 .intr_alloc_flags = 0, // default interrupt priority
195 .dma_buf_count = cfg.buffer_count,
196 .dma_buf_len = cfg.buffer_size,
197 .use_apll = (bool)cfg.use_apll,
198 .tx_desc_auto_clear = cfg.auto_clear,
199#if ESP_IDF_VERSION_MAJOR >= 4
200 .fixed_mclk = (int)(cfg.fixed_mclk > 0 ? cfg.fixed_mclk : 0),
201 .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
202 .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
203#endif
204 };
205 i2s_config = i2s_config_new;
206
207 // We make sure that we can reconfigure
208 if (is_started) {
209 end();
210 LOGD("%s", "I2S restarting");
211 }
212
213 // setup config
214 LOGD("i2s_driver_install");
215 if (i2s_driver_install(i2s_num, &i2s_config, 0, NULL) != ESP_OK) {
216 LOGE("%s - %s", __func__, "i2s_driver_install");
217 }
218
219 // setup pin config
220 if (this->cfg.signal_type == Digital || this->cfg.signal_type == PDM) {
221 i2s_pin_config_t pin_config = {
222#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4, 4, 0)
223 .mck_io_num = cfg.pin_mck,
224#endif
225 .bck_io_num = cfg.pin_bck,
226 .ws_io_num = cfg.pin_ws,
227 .data_out_num = txPin,
228 .data_in_num = rxPin};
229
230 LOGD("i2s_set_pin");
231 if (i2s_set_pin(i2s_num, &pin_config) != ESP_OK) {
232 LOGE("%s - %s", __func__, "i2s_set_pin");
233 }
234 } else {
235 LOGD("Using built in DAC");
236 // for internal DAC, this will enable both of the internal channels
237 i2s_set_pin(i2s_num, NULL);
238 }
239
240 // clear initial buffer
241 LOGD("i2s_zero_dma_buffer");
242 i2s_zero_dma_buffer(i2s_num);
243
244 is_started = true;
245 LOGD("%s - %s", __func__, "started");
246 return true;
247 }
248
249 // update the cfg.i2s.channel_format based on the number of channels
250 void setChannels(int channels) { cfg.channels = channels; }
251
253 size_t writeExpandChannel(i2s_port_t i2s_num, const int bits_per_sample,
254 const void *src, size_t size_bytes) {
255 size_t result = 0;
256 int j;
257 switch (bits_per_sample) {
258 case 8:
259 for (j = 0; j < size_bytes; j++) {
260 int8_t frame[2];
261 int8_t *data = (int8_t *)src;
262 frame[0] = data[j];
263 frame[1] = data[j];
264 size_t result_call = 0;
265 if (i2s_write(i2s_num, frame, sizeof(int8_t) * 2, &result_call,
266 ticks_to_wait_write) != ESP_OK) {
267 TRACEE();
268 } else {
269 result += result_call;
270 }
271 }
272 break;
273
274 case 16:
275 for (j = 0; j < size_bytes / 2; j++) {
276 int16_t frame[2];
277 int16_t *data = (int16_t *)src;
278 frame[0] = data[j];
279 frame[1] = data[j];
280 size_t result_call = 0;
281 if (i2s_write(i2s_num, frame, sizeof(int16_t) * 2, &result_call,
282 ticks_to_wait_write) != ESP_OK) {
283 TRACEE();
284 } else {
285 result += result_call;
286 }
287 }
288 break;
289
290 case 24:
291 for (j = 0; j < size_bytes / 4; j++) {
292 int24_t frame[2];
293 int24_t *data = (int24_t *)src;
294 frame[0] = data[j];
295 frame[1] = data[j];
296 size_t result_call = 0;
297 if (i2s_write(i2s_num, frame, sizeof(int24_t) * 2, &result_call,
298 ticks_to_wait_write) != ESP_OK) {
299 TRACEE();
300 } else {
301 result += result_call;
302 }
303 }
304 break;
305
306 case 32:
307 for (j = 0; j < size_bytes / 4; j++) {
308 int32_t frame[2];
309 int32_t *data = (int32_t *)src;
310 frame[0] = data[j];
311 frame[1] = data[j];
312 size_t result_call = 0;
313 if (i2s_write(i2s_num, frame, sizeof(int32_t) * 2, &result_call,
314 ticks_to_wait_write) != ESP_OK) {
315 TRACEE();
316 } else {
317 result += result_call;
318 }
319 }
320 break;
321 }
322 return size_bytes;
323 }
324
325#pragma GCC diagnostic push
326#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
327
328 // determines the i2s_comm_format_t - by default we use
329 // I2S_COMM_FORMAT_STAND_I2S
330 i2s_comm_format_t toCommFormat(I2SFormat mode) {
331 switch (mode) {
332 case I2S_PHILIPS_FORMAT:
333 case I2S_STD_FORMAT:
334 return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S;
335 case I2S_LEFT_JUSTIFIED_FORMAT:
336 case I2S_MSB_FORMAT:
337 return (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S |
338 I2S_COMM_FORMAT_I2S_MSB);
339 case I2S_RIGHT_JUSTIFIED_FORMAT:
340 case I2S_LSB_FORMAT:
341 return (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S |
342 I2S_COMM_FORMAT_I2S_LSB);
343 case I2S_PCM:
344 return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_PCM_SHORT;
345
346 default:
347 LOGE("unsupported mode");
348 return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S;
349 }
350 }
351#pragma GCC diagnostic pop
352
353 int getModeDigital(I2SConfigESP32 &cfg) {
354 int i2s_format = cfg.is_master ? I2S_MODE_MASTER : I2S_MODE_SLAVE;
355 int i2s_rx_tx = 0;
356 switch (cfg.rx_tx_mode) {
357 case TX_MODE:
358 i2s_rx_tx = I2S_MODE_TX;
359 break;
360 case RX_MODE:
361 i2s_rx_tx = I2S_MODE_RX;
362 break;
363 case RXTX_MODE:
364 i2s_rx_tx = I2S_MODE_RX | I2S_MODE_TX;
365 break;
366 default:
367 LOGE("Undefined rx_tx_mode: %d", cfg.rx_tx_mode);
368 }
369 return (i2s_format | i2s_rx_tx);
370 }
371
372 // determines the i2s_format_t
373 i2s_mode_t toMode(I2SConfigESP32 &cfg) {
374 i2s_mode_t mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX);
375 switch (cfg.signal_type) {
376 case Digital:
377 mode = (i2s_mode_t)getModeDigital(cfg);
378 break;
379
380 case PDM:
381 mode = (i2s_mode_t)(getModeDigital(cfg) | I2S_MODE_PDM);
382 break;
383
384 case Analog:
385#if defined(USE_ANALOG)
386 mode = (i2s_mode_t)(cfg.rx_tx_mode ? I2S_MODE_DAC_BUILT_IN
387 : I2S_MODE_ADC_BUILT_IN);
388#else
389 LOGE("mode not supported");
390#endif
391 break;
392
393 default:
394 LOGW("signal_type undefined");
395 mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
396 break;
397 }
398 return mode;
399 }
400};
401
402using I2SDriver = I2SDriverESP32;
403
404} // namespace audio_tools
405
406#endif
AudioKit Stream which uses the https://github.com/pschatzmann/arduino-audiokit library.
Definition AudioKit.h:189
Configuration for ESP32 legacy i2s.
Definition I2SConfigESP32.h:23
RxTxMode rx_tx_mode
public settings
Definition I2SConfigESP32.h:59
Basic I2S API - for the ESP32. If we receive 1 channel, we expand the result to 2 channels.
Definition I2SESP32.h:27
bool begin(I2SConfigESP32 cfg)
starts the DAC
Definition I2SESP32.h:62
int available()
we assume the data is already available in the buffer
Definition I2SESP32.h:81
bool begin()
Definition I2SESP32.h:59
bool setAudioInfo(AudioInfo info)
Potentially updates the sample rate (if supported)
Definition I2SESP32.h:39
size_t writeExpandChannel(i2s_port_t i2s_num, const int bits_per_sample, const void *src, size_t size_bytes)
writes the data by making sure that we send 2 channels
Definition I2SESP32.h:253
int availableForWrite()
We limit the write size to the buffer size.
Definition I2SESP32.h:84
I2SConfigESP32 config()
provides the actual configuration
Definition I2SESP32.h:94
void end()
stops the I2C and unistalls the driver
Definition I2SESP32.h:87
bool begin(RxTxMode mode)
starts the DAC with the default config
Definition I2SESP32.h:55
I2SConfigESP32 defaultConfig(RxTxMode mode)
Provides the default configuration.
Definition I2SESP32.h:33
size_t writeBytes(const void *src, size_t size_bytes)
writes the data to the I2S interface
Definition I2SESP32.h:97
bool begin(I2SConfigESP32 cfg, int txPin, int rxPin)
starts the DAC
Definition I2SESP32.h:181
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition Int24_4bytes_t.h:16
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
I2SFormat
I2S Formats.
Definition AudioTypes.h:416
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
bool equals(AudioInfo alt)
Returns true if alt values are the same like the current values.
Definition AudioTypes.h:85
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
bool equalsExSampleRate(AudioInfo alt)
Checks if only the sample rate is different.
Definition AudioTypes.h:88
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59