arduino-audio-tools
I2SESP32.h
1 #pragma once
2 
3 #include "AudioConfig.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 
18 namespace audio_tools {
19 
28  friend class AnalogAudio;
29  friend class AudioKitStream;
30 
31  public:
34  I2SConfigESP32 c(mode);
35  return c;
36  }
37 
39  bool setAudioInfo(AudioInfo info) {
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 
62  bool begin(I2SConfigESP32 cfg) {
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 
402 using 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: AudioConfig.h:868
I2SFormat
I2S Formats.
Definition: AudioTypes.h:436
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
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:90
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:59